lonny-agent 0.2.4 → 0.2.7

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 (509) hide show
  1. package/.github/workflows/ci.yml +21 -0
  2. package/AGENTS.md +106 -0
  3. package/CHANGELOG.md +24 -0
  4. package/README.md +326 -326
  5. package/dist/agent/__tests__/session-restore.test.d.ts +2 -0
  6. package/dist/agent/__tests__/session-restore.test.d.ts.map +1 -0
  7. package/dist/agent/__tests__/session-restore.test.js +260 -0
  8. package/dist/agent/__tests__/session-restore.test.js.map +1 -0
  9. package/dist/agent/commands.d.ts +26 -0
  10. package/dist/agent/commands.d.ts.map +1 -0
  11. package/dist/agent/commands.js +253 -0
  12. package/dist/agent/commands.js.map +1 -0
  13. package/dist/agent/compaction.js +8 -1
  14. package/dist/agent/compaction.js.map +1 -1
  15. package/dist/agent/index.d.ts +2 -1
  16. package/dist/agent/index.d.ts.map +1 -1
  17. package/dist/agent/index.js +2 -2
  18. package/dist/agent/index.js.map +1 -1
  19. package/dist/agent/llm.d.ts +4 -0
  20. package/dist/agent/llm.d.ts.map +1 -1
  21. package/dist/agent/memory.d.ts +12 -0
  22. package/dist/agent/memory.d.ts.map +1 -0
  23. package/dist/agent/memory.js +51 -0
  24. package/dist/agent/memory.js.map +1 -0
  25. package/dist/agent/prompt-builder.d.ts +6 -1
  26. package/dist/agent/prompt-builder.d.ts.map +1 -1
  27. package/dist/agent/prompt-builder.js +100 -33
  28. package/dist/agent/prompt-builder.js.map +1 -1
  29. package/dist/agent/providers/google.d.ts.map +1 -1
  30. package/dist/agent/providers/google.js.map +1 -1
  31. package/dist/agent/providers/openai.d.ts +1 -0
  32. package/dist/agent/providers/openai.d.ts.map +1 -1
  33. package/dist/agent/providers/openai.js +59 -6
  34. package/dist/agent/providers/openai.js.map +1 -1
  35. package/dist/agent/session-chat.d.ts +3 -0
  36. package/dist/agent/session-chat.d.ts.map +1 -0
  37. package/dist/agent/session-chat.js +384 -0
  38. package/dist/agent/session-chat.js.map +1 -0
  39. package/dist/agent/session-display.d.ts +29 -0
  40. package/dist/agent/session-display.d.ts.map +1 -0
  41. package/dist/agent/session-display.js +179 -0
  42. package/dist/agent/session-display.js.map +1 -0
  43. package/dist/agent/session-persistence.d.ts +55 -0
  44. package/dist/agent/session-persistence.d.ts.map +1 -0
  45. package/dist/agent/session-persistence.js +101 -0
  46. package/dist/agent/session-persistence.js.map +1 -0
  47. package/dist/agent/session-utils.d.ts +5 -0
  48. package/dist/agent/session-utils.d.ts.map +1 -0
  49. package/dist/agent/session-utils.js +51 -0
  50. package/dist/agent/session-utils.js.map +1 -0
  51. package/dist/agent/session.d.ts +19 -17
  52. package/dist/agent/session.d.ts.map +1 -1
  53. package/dist/agent/session.js +234 -578
  54. package/dist/agent/session.js.map +1 -1
  55. package/dist/cli/index.d.ts +4 -0
  56. package/dist/cli/index.d.ts.map +1 -1
  57. package/dist/cli/index.js +67 -2
  58. package/dist/cli/index.js.map +1 -1
  59. package/dist/config/index.d.ts +2 -2
  60. package/dist/config/index.d.ts.map +1 -1
  61. package/dist/config/index.js +7 -1
  62. package/dist/config/index.js.map +1 -1
  63. package/dist/index.js +24 -3
  64. package/dist/index.js.map +1 -1
  65. package/dist/tools/__tests__/bash.test.js +169 -2
  66. package/dist/tools/__tests__/bash.test.js.map +1 -1
  67. package/dist/tools/__tests__/edit.test.js +1091 -340
  68. package/dist/tools/__tests__/edit.test.js.map +1 -1
  69. package/dist/tools/__tests__/fetch.test.js +1 -1
  70. package/dist/tools/__tests__/fetch.test.js.map +1 -1
  71. package/dist/tools/__tests__/glob.test.js +1 -1
  72. package/dist/tools/__tests__/glob.test.js.map +1 -1
  73. package/dist/tools/__tests__/grep.test.js +33 -1
  74. package/dist/tools/__tests__/grep.test.js.map +1 -1
  75. package/dist/tools/__tests__/ls.test.js +1 -1
  76. package/dist/tools/__tests__/ls.test.js.map +1 -1
  77. package/dist/tools/__tests__/read.test.js +1 -1
  78. package/dist/tools/__tests__/read.test.js.map +1 -1
  79. package/dist/tools/__tests__/registry.test.js +313 -0
  80. package/dist/tools/__tests__/registry.test.js.map +1 -1
  81. package/dist/tools/__tests__/sed.test.d.ts +2 -0
  82. package/dist/tools/__tests__/sed.test.d.ts.map +1 -0
  83. package/dist/tools/__tests__/sed.test.js +228 -0
  84. package/dist/tools/__tests__/sed.test.js.map +1 -0
  85. package/dist/tools/__tests__/write_plan.test.js +1 -1
  86. package/dist/tools/__tests__/write_plan.test.js.map +1 -1
  87. package/dist/tools/bash.d.ts.map +1 -1
  88. package/dist/tools/bash.js +711 -18
  89. package/dist/tools/bash.js.map +1 -1
  90. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.d.ts +2 -0
  91. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.d.ts.map +1 -0
  92. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.js +7 -0
  93. package/dist/tools/codebase/ast/__tests__/ast-edit-demo.js.map +1 -0
  94. package/dist/tools/codebase/ast/__tests__/ast-edit-final.d.ts +6 -0
  95. package/dist/tools/codebase/ast/__tests__/ast-edit-final.d.ts.map +1 -0
  96. package/dist/tools/codebase/ast/__tests__/ast-edit-final.js +11 -0
  97. package/dist/tools/codebase/ast/__tests__/ast-edit-final.js.map +1 -0
  98. package/dist/tools/codebase/ast/__tests__/ast-edit-test.d.ts +2 -0
  99. package/dist/tools/codebase/ast/__tests__/ast-edit-test.d.ts.map +1 -0
  100. package/dist/tools/codebase/ast/__tests__/ast-edit-test.js +9 -0
  101. package/dist/tools/codebase/ast/__tests__/ast-edit-test.js.map +1 -0
  102. package/dist/tools/codebase/ast/__tests__/integration.test.d.ts +2 -0
  103. package/dist/tools/codebase/ast/__tests__/integration.test.d.ts.map +1 -0
  104. package/dist/tools/codebase/ast/__tests__/integration.test.js +415 -0
  105. package/dist/tools/codebase/ast/__tests__/integration.test.js.map +1 -0
  106. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.d.ts +2 -0
  107. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.d.ts.map +1 -0
  108. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.js +414 -0
  109. package/dist/tools/codebase/ast/__tests__/tree-sitter.test.js.map +1 -0
  110. package/dist/tools/codebase/ast/adapter.d.ts +23 -0
  111. package/dist/tools/codebase/ast/adapter.d.ts.map +1 -0
  112. package/dist/tools/codebase/ast/adapter.js +11 -0
  113. package/dist/tools/codebase/ast/adapter.js.map +1 -0
  114. package/dist/tools/codebase/ast/tools.d.ts +3 -0
  115. package/dist/tools/codebase/ast/tools.d.ts.map +1 -0
  116. package/dist/tools/codebase/ast/tools.js +286 -0
  117. package/dist/tools/codebase/ast/tools.js.map +1 -0
  118. package/dist/tools/codebase/ast/tree-sitter-adapter.d.ts +3 -0
  119. package/dist/tools/codebase/ast/tree-sitter-adapter.d.ts.map +1 -0
  120. package/dist/tools/codebase/ast/tree-sitter-adapter.js +412 -0
  121. package/dist/tools/codebase/ast/tree-sitter-adapter.js.map +1 -0
  122. package/dist/tools/codebase/ast/tree-sitter-converters.d.ts +4 -0
  123. package/dist/tools/codebase/ast/tree-sitter-converters.d.ts.map +1 -0
  124. package/dist/tools/codebase/ast/tree-sitter-converters.js +447 -0
  125. package/dist/tools/codebase/ast/tree-sitter-converters.js.map +1 -0
  126. package/dist/tools/codebase/ast/types.d.ts +103 -0
  127. package/dist/tools/codebase/ast/types.d.ts.map +1 -0
  128. package/dist/tools/codebase/ast/types.js +2 -0
  129. package/dist/tools/codebase/ast/types.js.map +1 -0
  130. package/dist/tools/codebase/ast/wasm/tree-sitter-python.wasm +0 -0
  131. package/dist/tools/codebase/ast/wasm/tree-sitter-tsx.wasm +0 -0
  132. package/dist/tools/codebase/ast/wasm/tree-sitter-typescript.wasm +0 -0
  133. package/dist/tools/codebase/ast/wasm/tree-sitter.wasm +0 -0
  134. package/dist/tools/codebase/ast/wasm/web-tree-sitter.wasm +0 -0
  135. package/dist/tools/codebase/find.d.ts +3 -0
  136. package/dist/tools/codebase/find.d.ts.map +1 -0
  137. package/dist/tools/codebase/find.js +61 -0
  138. package/dist/tools/codebase/find.js.map +1 -0
  139. package/dist/tools/codebase/glob.d.ts +3 -0
  140. package/dist/tools/codebase/glob.d.ts.map +1 -0
  141. package/dist/tools/codebase/glob.js +37 -0
  142. package/dist/tools/codebase/glob.js.map +1 -0
  143. package/dist/tools/codebase/grep.d.ts +3 -0
  144. package/dist/tools/codebase/grep.d.ts.map +1 -0
  145. package/dist/tools/codebase/grep.js +169 -0
  146. package/dist/tools/codebase/grep.js.map +1 -0
  147. package/dist/tools/codebase/ls.d.ts +3 -0
  148. package/dist/tools/codebase/ls.d.ts.map +1 -0
  149. package/dist/tools/codebase/ls.js +39 -0
  150. package/dist/tools/codebase/ls.js.map +1 -0
  151. package/dist/tools/codebase/read.d.ts +10 -0
  152. package/dist/tools/codebase/read.d.ts.map +1 -0
  153. package/dist/tools/codebase/read.js +81 -0
  154. package/dist/tools/codebase/read.js.map +1 -0
  155. package/dist/tools/delete_memory.d.ts +3 -0
  156. package/dist/tools/delete_memory.d.ts.map +1 -0
  157. package/dist/tools/delete_memory.js +46 -0
  158. package/dist/tools/delete_memory.js.map +1 -0
  159. package/dist/tools/edit/diff-compute.d.ts +4 -0
  160. package/dist/tools/edit/diff-compute.d.ts.map +1 -0
  161. package/dist/tools/edit/diff-compute.js +18 -0
  162. package/dist/tools/edit/diff-compute.js.map +1 -0
  163. package/dist/tools/edit/diff-render.d.ts +18 -0
  164. package/dist/tools/edit/diff-render.d.ts.map +1 -0
  165. package/dist/tools/edit/diff-render.js +128 -0
  166. package/dist/tools/edit/diff-render.js.map +1 -0
  167. package/dist/tools/edit/edit.d.ts +4 -0
  168. package/dist/tools/edit/edit.d.ts.map +1 -0
  169. package/dist/tools/edit/edit.js +440 -0
  170. package/dist/tools/edit/edit.js.map +1 -0
  171. package/dist/tools/edit/matcher.d.ts +24 -0
  172. package/dist/tools/edit/matcher.d.ts.map +1 -0
  173. package/dist/tools/edit/matcher.js +66 -0
  174. package/dist/tools/edit/matcher.js.map +1 -0
  175. package/dist/tools/edit/parser.d.ts +10 -0
  176. package/dist/tools/edit/parser.d.ts.map +1 -0
  177. package/dist/tools/edit/parser.js +120 -0
  178. package/dist/tools/edit/parser.js.map +1 -0
  179. package/dist/tools/edit/types.d.ts +16 -0
  180. package/dist/tools/edit/types.d.ts.map +1 -0
  181. package/dist/tools/edit/types.js +2 -0
  182. package/dist/tools/edit/types.js.map +1 -0
  183. package/dist/tools/edit/write_plan.d.ts +5 -0
  184. package/dist/tools/edit/write_plan.d.ts.map +1 -0
  185. package/dist/tools/edit/write_plan.js +98 -0
  186. package/dist/tools/edit/write_plan.js.map +1 -0
  187. package/dist/tools/edit.d.ts +50 -9
  188. package/dist/tools/edit.d.ts.map +1 -1
  189. package/dist/tools/edit.js +394 -159
  190. package/dist/tools/edit.js.map +1 -1
  191. package/dist/tools/execute/bash/constants.d.ts +24 -0
  192. package/dist/tools/execute/bash/constants.d.ts.map +1 -0
  193. package/dist/tools/execute/bash/constants.js +88 -0
  194. package/dist/tools/execute/bash/constants.js.map +1 -0
  195. package/dist/tools/execute/bash/errors.d.ts +39 -0
  196. package/dist/tools/execute/bash/errors.d.ts.map +1 -0
  197. package/dist/tools/execute/bash/errors.js +249 -0
  198. package/dist/tools/execute/bash/errors.js.map +1 -0
  199. package/dist/tools/execute/bash/execution.d.ts +18 -0
  200. package/dist/tools/execute/bash/execution.d.ts.map +1 -0
  201. package/dist/tools/execute/bash/execution.js +207 -0
  202. package/dist/tools/execute/bash/execution.js.map +1 -0
  203. package/dist/tools/execute/bash/index.d.ts +3 -0
  204. package/dist/tools/execute/bash/index.d.ts.map +1 -0
  205. package/dist/tools/execute/bash/index.js +117 -0
  206. package/dist/tools/execute/bash/index.js.map +1 -0
  207. package/dist/tools/execute/bash/platform.d.ts +6 -0
  208. package/dist/tools/execute/bash/platform.d.ts.map +1 -0
  209. package/dist/tools/execute/bash/platform.js +64 -0
  210. package/dist/tools/execute/bash/platform.js.map +1 -0
  211. package/dist/tools/execute/bash/security.d.ts +12 -0
  212. package/dist/tools/execute/bash/security.d.ts.map +1 -0
  213. package/dist/tools/execute/bash/security.js +26 -0
  214. package/dist/tools/execute/bash/security.js.map +1 -0
  215. package/dist/tools/execute/bash/validation.d.ts +16 -0
  216. package/dist/tools/execute/bash/validation.d.ts.map +1 -0
  217. package/dist/tools/execute/bash/validation.js +57 -0
  218. package/dist/tools/execute/bash/validation.js.map +1 -0
  219. package/dist/tools/execute/bash.d.ts +3 -0
  220. package/dist/tools/execute/bash.d.ts.map +1 -0
  221. package/dist/tools/execute/bash.js +728 -0
  222. package/dist/tools/execute/bash.js.map +1 -0
  223. package/dist/tools/execute/git.d.ts +3 -0
  224. package/dist/tools/execute/git.d.ts.map +1 -0
  225. package/dist/tools/execute/git.js +183 -0
  226. package/dist/tools/execute/git.js.map +1 -0
  227. package/dist/tools/execute/task_complete.d.ts +7 -0
  228. package/dist/tools/execute/task_complete.d.ts.map +1 -0
  229. package/dist/tools/execute/task_complete.js +38 -0
  230. package/dist/tools/execute/task_complete.js.map +1 -0
  231. package/dist/tools/fetch.d.ts.map +1 -1
  232. package/dist/tools/fetch.js +2 -0
  233. package/dist/tools/fetch.js.map +1 -1
  234. package/dist/tools/find.d.ts.map +1 -1
  235. package/dist/tools/find.js +2 -0
  236. package/dist/tools/find.js.map +1 -1
  237. package/dist/tools/git.d.ts.map +1 -1
  238. package/dist/tools/git.js +2 -0
  239. package/dist/tools/git.js.map +1 -1
  240. package/dist/tools/glob.d.ts.map +1 -1
  241. package/dist/tools/glob.js +2 -0
  242. package/dist/tools/glob.js.map +1 -1
  243. package/dist/tools/grep.d.ts.map +1 -1
  244. package/dist/tools/grep.js +56 -30
  245. package/dist/tools/grep.js.map +1 -1
  246. package/dist/tools/install/clawhub.d.ts +26 -0
  247. package/dist/tools/install/clawhub.d.ts.map +1 -0
  248. package/dist/tools/install/clawhub.js +182 -0
  249. package/dist/tools/install/clawhub.js.map +1 -0
  250. package/dist/tools/install/constants.d.ts +5 -0
  251. package/dist/tools/install/constants.d.ts.map +1 -0
  252. package/dist/tools/install/constants.js +5 -0
  253. package/dist/tools/install/constants.js.map +1 -0
  254. package/dist/tools/install/install_skill.d.ts +3 -0
  255. package/dist/tools/install/install_skill.d.ts.map +1 -0
  256. package/dist/tools/install/install_skill.js +132 -0
  257. package/dist/tools/install/install_skill.js.map +1 -0
  258. package/dist/tools/install/npm.d.ts +8 -0
  259. package/dist/tools/install/npm.d.ts.map +1 -0
  260. package/dist/tools/install/npm.js +52 -0
  261. package/dist/tools/install/npm.js.map +1 -0
  262. package/dist/tools/install/skill-content.d.ts +9 -0
  263. package/dist/tools/install/skill-content.d.ts.map +1 -0
  264. package/dist/tools/install/skill-content.js +89 -0
  265. package/dist/tools/install/skill-content.js.map +1 -0
  266. package/dist/tools/install_skill.d.ts.map +1 -1
  267. package/dist/tools/install_skill.js +2 -0
  268. package/dist/tools/install_skill.js.map +1 -1
  269. package/dist/tools/list_memory.d.ts +3 -0
  270. package/dist/tools/list_memory.d.ts.map +1 -0
  271. package/dist/tools/list_memory.js +55 -0
  272. package/dist/tools/list_memory.js.map +1 -0
  273. package/dist/tools/ls.d.ts.map +1 -1
  274. package/dist/tools/ls.js +2 -0
  275. package/dist/tools/ls.js.map +1 -1
  276. package/dist/tools/memory/delete_memory.d.ts +3 -0
  277. package/dist/tools/memory/delete_memory.d.ts.map +1 -0
  278. package/dist/tools/memory/delete_memory.js +46 -0
  279. package/dist/tools/memory/delete_memory.js.map +1 -0
  280. package/dist/tools/memory/list_memory.d.ts +3 -0
  281. package/dist/tools/memory/list_memory.d.ts.map +1 -0
  282. package/dist/tools/memory/list_memory.js +55 -0
  283. package/dist/tools/memory/list_memory.js.map +1 -0
  284. package/dist/tools/memory/save_memory.d.ts +3 -0
  285. package/dist/tools/memory/save_memory.d.ts.map +1 -0
  286. package/dist/tools/memory/save_memory.js +45 -0
  287. package/dist/tools/memory/save_memory.js.map +1 -0
  288. package/dist/tools/read.d.ts.map +1 -1
  289. package/dist/tools/read.js +2 -0
  290. package/dist/tools/read.js.map +1 -1
  291. package/dist/tools/registry.d.ts +7 -8
  292. package/dist/tools/registry.d.ts.map +1 -1
  293. package/dist/tools/registry.js +114 -46
  294. package/dist/tools/registry.js.map +1 -1
  295. package/dist/tools/save_memory.d.ts +3 -0
  296. package/dist/tools/save_memory.d.ts.map +1 -0
  297. package/dist/tools/save_memory.js +45 -0
  298. package/dist/tools/save_memory.js.map +1 -0
  299. package/dist/tools/search.d.ts.map +1 -1
  300. package/dist/tools/search.js +2 -0
  301. package/dist/tools/search.js.map +1 -1
  302. package/dist/tools/sed.d.ts +4 -0
  303. package/dist/tools/sed.d.ts.map +1 -0
  304. package/dist/tools/sed.js +121 -0
  305. package/dist/tools/sed.js.map +1 -0
  306. package/dist/tools/tree.d.ts +12 -0
  307. package/dist/tools/tree.d.ts.map +1 -0
  308. package/dist/tools/tree.js +87 -0
  309. package/dist/tools/tree.js.map +1 -0
  310. package/dist/tools/types.d.ts +12 -0
  311. package/dist/tools/types.d.ts.map +1 -1
  312. package/dist/tools/web/fetch.d.ts +3 -0
  313. package/dist/tools/web/fetch.d.ts.map +1 -0
  314. package/dist/tools/web/fetch.js +49 -0
  315. package/dist/tools/web/fetch.js.map +1 -0
  316. package/dist/tools/web/search.d.ts +3 -0
  317. package/dist/tools/web/search.d.ts.map +1 -0
  318. package/dist/tools/web/search.js +134 -0
  319. package/dist/tools/web/search.js.map +1 -0
  320. package/dist/tools/write_plan.d.ts.map +1 -1
  321. package/dist/tools/write_plan.js +2 -0
  322. package/dist/tools/write_plan.js.map +1 -1
  323. package/dist/tui/commands.d.ts +4 -0
  324. package/dist/tui/commands.d.ts.map +1 -0
  325. package/dist/tui/commands.js +118 -0
  326. package/dist/tui/commands.js.map +1 -0
  327. package/dist/tui/components/colors.d.ts +36 -0
  328. package/dist/tui/components/colors.d.ts.map +1 -0
  329. package/dist/tui/components/colors.js +48 -0
  330. package/dist/tui/components/colors.js.map +1 -0
  331. package/dist/tui/components/header-bar.d.ts +26 -0
  332. package/dist/tui/components/header-bar.d.ts.map +1 -0
  333. package/dist/tui/components/header-bar.js +78 -0
  334. package/dist/tui/components/header-bar.js.map +1 -0
  335. package/dist/tui/components/index.d.ts +9 -0
  336. package/dist/tui/components/index.d.ts.map +1 -0
  337. package/dist/tui/components/index.js +9 -0
  338. package/dist/tui/components/index.js.map +1 -0
  339. package/dist/tui/components/landing-screen.d.ts +12 -0
  340. package/dist/tui/components/landing-screen.d.ts.map +1 -0
  341. package/dist/tui/components/landing-screen.js +53 -0
  342. package/dist/tui/components/landing-screen.js.map +1 -0
  343. package/dist/tui/components/pixel-logo.d.ts +5 -0
  344. package/dist/tui/components/pixel-logo.d.ts.map +1 -0
  345. package/dist/tui/components/pixel-logo.js +27 -0
  346. package/dist/tui/components/pixel-logo.js.map +1 -0
  347. package/dist/tui/components/plan-utils.d.ts +11 -0
  348. package/dist/tui/components/plan-utils.d.ts.map +1 -0
  349. package/dist/tui/components/plan-utils.js +62 -0
  350. package/dist/tui/components/plan-utils.js.map +1 -0
  351. package/dist/tui/components/plans-list.d.ts +18 -0
  352. package/dist/tui/components/plans-list.d.ts.map +1 -0
  353. package/dist/tui/components/plans-list.js +46 -0
  354. package/dist/tui/components/plans-list.js.map +1 -0
  355. package/dist/tui/components/rich-footer.d.ts +29 -0
  356. package/dist/tui/components/rich-footer.d.ts.map +1 -0
  357. package/dist/tui/components/rich-footer.js +124 -0
  358. package/dist/tui/components/rich-footer.js.map +1 -0
  359. package/dist/tui/components/todo-panel.d.ts +14 -0
  360. package/dist/tui/components/todo-panel.d.ts.map +1 -0
  361. package/dist/tui/components/todo-panel.js +83 -0
  362. package/dist/tui/components/todo-panel.js.map +1 -0
  363. package/dist/tui/components.d.ts.map +1 -1
  364. package/dist/tui/components.js +14 -7
  365. package/dist/tui/components.js.map +1 -1
  366. package/dist/tui/index.d.ts +2 -1
  367. package/dist/tui/index.d.ts.map +1 -1
  368. package/dist/tui/index.js +99 -505
  369. package/dist/tui/index.js.map +1 -1
  370. package/dist/tui/overlays.d.ts +28 -0
  371. package/dist/tui/overlays.d.ts.map +1 -0
  372. package/dist/tui/overlays.js +154 -0
  373. package/dist/tui/overlays.js.map +1 -0
  374. package/dist/tui/themes.d.ts +5 -0
  375. package/dist/tui/themes.d.ts.map +1 -0
  376. package/dist/tui/themes.js +38 -0
  377. package/dist/tui/themes.js.map +1 -0
  378. package/dist/web/index.d.ts.map +1 -1
  379. package/dist/web/index.js +62 -101
  380. package/dist/web/index.js.map +1 -1
  381. package/dist/web/public/app.js +5 -855
  382. package/dist/web/public/confirm.js +70 -0
  383. package/dist/web/public/index.html +121 -120
  384. package/dist/web/public/input.js +107 -0
  385. package/dist/web/public/messages.js +411 -0
  386. package/dist/web/public/sidebar.js +82 -0
  387. package/dist/web/public/state.js +72 -0
  388. package/dist/web/public/style.css +247 -70
  389. package/dist/web/public/utils.js +37 -0
  390. package/dist/web/public/websocket.js +267 -0
  391. package/dist/web/public/ws.js +19 -0
  392. package/dist/web/session-bridge.d.ts.map +1 -1
  393. package/dist/web/session-bridge.js +85 -10
  394. package/dist/web/session-bridge.js.map +1 -1
  395. package/docs/ast-tools.md +117 -0
  396. package/package.json +11 -5
  397. package/scripts/check-line-count.mjs +53 -0
  398. package/scripts/copy-ast-wasm.mjs +32 -0
  399. package/scripts/copy-native.mjs +24 -0
  400. package/scripts/copy-web.mjs +15 -0
  401. package/src/agent/__tests__/session-restore.test.ts +284 -0
  402. package/src/agent/commands.ts +274 -0
  403. package/src/agent/compaction.ts +9 -2
  404. package/src/agent/index.ts +7 -2
  405. package/src/agent/llm.ts +4 -0
  406. package/src/agent/memory.ts +60 -0
  407. package/src/agent/prompt-builder.ts +111 -33
  408. package/src/agent/providers/google.ts +0 -1
  409. package/src/agent/providers/openai.ts +79 -8
  410. package/src/agent/session-chat.ts +480 -0
  411. package/src/agent/session-display.ts +195 -0
  412. package/src/agent/session-persistence.ts +146 -0
  413. package/src/agent/session-utils.ts +54 -0
  414. package/src/agent/session.ts +269 -670
  415. package/src/cli/index.ts +73 -4
  416. package/src/config/index.ts +14 -7
  417. package/src/index.ts +30 -3
  418. package/src/tools/__tests__/bash.test.ts +188 -2
  419. package/src/tools/__tests__/edit.test.ts +1216 -356
  420. package/src/tools/__tests__/fetch.test.ts +1 -1
  421. package/src/tools/__tests__/glob.test.ts +1 -1
  422. package/src/tools/__tests__/grep.test.ts +36 -1
  423. package/src/tools/__tests__/ls.test.ts +1 -1
  424. package/src/tools/__tests__/read.test.ts +1 -1
  425. package/src/tools/__tests__/registry.test.ts +332 -0
  426. package/src/tools/__tests__/write_plan.test.ts +1 -1
  427. package/src/tools/codebase/ast/__tests__/integration.test.ts +452 -0
  428. package/src/tools/codebase/ast/__tests__/tree-sitter.test.ts +466 -0
  429. package/src/tools/codebase/ast/adapter.ts +44 -0
  430. package/src/tools/codebase/ast/tools.ts +322 -0
  431. package/src/tools/codebase/ast/tree-sitter-adapter.ts +468 -0
  432. package/src/tools/codebase/ast/tree-sitter-converters.ts +480 -0
  433. package/src/tools/codebase/ast/types.ts +102 -0
  434. package/src/tools/codebase/ast/wasm/tree-sitter-python.wasm +0 -0
  435. package/src/tools/codebase/ast/wasm/tree-sitter-tsx.wasm +0 -0
  436. package/src/tools/codebase/ast/wasm/tree-sitter-typescript.wasm +0 -0
  437. package/src/tools/codebase/ast/wasm/tree-sitter.wasm +0 -0
  438. package/src/tools/codebase/ast/wasm/web-tree-sitter.wasm +0 -0
  439. package/src/tools/{find.ts → codebase/find.ts} +4 -2
  440. package/src/tools/{glob.ts → codebase/glob.ts} +4 -2
  441. package/src/tools/{grep.ts → codebase/grep.ts} +61 -32
  442. package/src/tools/{ls.ts → codebase/ls.ts} +4 -2
  443. package/src/tools/{read.ts → codebase/read.ts} +5 -3
  444. package/src/tools/edit/diff-compute.ts +21 -0
  445. package/src/tools/edit/diff-render.ts +145 -0
  446. package/src/tools/edit/edit.ts +468 -0
  447. package/src/tools/edit/matcher.ts +70 -0
  448. package/src/tools/edit/parser.ts +131 -0
  449. package/src/tools/edit/types.ts +19 -0
  450. package/src/tools/{write_plan.ts → edit/write_plan.ts} +4 -2
  451. package/src/tools/execute/bash/constants.ts +93 -0
  452. package/src/tools/execute/bash/errors.ts +304 -0
  453. package/src/tools/execute/bash/execution.ts +211 -0
  454. package/src/tools/execute/bash/index.ts +131 -0
  455. package/src/tools/execute/bash/platform.ts +65 -0
  456. package/src/tools/execute/bash/security.ts +28 -0
  457. package/src/tools/execute/bash/validation.ts +78 -0
  458. package/src/tools/execute/git.ts +209 -0
  459. package/src/tools/execute/task_complete.ts +40 -0
  460. package/src/tools/install/clawhub.ts +222 -0
  461. package/src/tools/install/constants.ts +4 -0
  462. package/src/tools/install/install_skill.ts +150 -0
  463. package/src/tools/install/npm.ts +60 -0
  464. package/src/tools/install/skill-content.ts +108 -0
  465. package/src/tools/memory/delete_memory.ts +46 -0
  466. package/src/tools/memory/list_memory.ts +53 -0
  467. package/src/tools/memory/save_memory.ts +45 -0
  468. package/src/tools/registry.ts +121 -50
  469. package/src/tools/tree.ts +103 -0
  470. package/src/tools/types.ts +13 -0
  471. package/src/tools/{fetch.ts → web/fetch.ts} +4 -2
  472. package/src/tools/{search.ts → web/search.ts} +4 -2
  473. package/src/tui/commands.ts +135 -0
  474. package/src/tui/components/colors.ts +52 -0
  475. package/src/tui/components/header-bar.ts +88 -0
  476. package/src/tui/components/index.ts +8 -0
  477. package/src/tui/components/landing-screen.ts +67 -0
  478. package/src/tui/components/pixel-logo.ts +33 -0
  479. package/src/tui/components/plan-utils.ts +69 -0
  480. package/src/tui/components/plans-list.ts +55 -0
  481. package/src/tui/components/rich-footer.ts +146 -0
  482. package/src/tui/components/todo-panel.ts +95 -0
  483. package/src/tui/index.ts +110 -561
  484. package/src/tui/overlays.ts +203 -0
  485. package/src/tui/themes.ts +41 -0
  486. package/src/web/index.ts +66 -107
  487. package/src/web/public/app.js +5 -855
  488. package/src/web/public/confirm.js +70 -0
  489. package/src/web/public/index.html +121 -120
  490. package/src/web/public/input.js +107 -0
  491. package/src/web/public/messages.js +411 -0
  492. package/src/web/public/sidebar.js +82 -0
  493. package/src/web/public/state.js +72 -0
  494. package/src/web/public/style.css +247 -70
  495. package/src/web/public/utils.js +37 -0
  496. package/src/web/public/websocket.js +267 -0
  497. package/src/web/public/ws.js +19 -0
  498. package/src/web/session-bridge.ts +90 -12
  499. package/.claude/settings.local.json +0 -14
  500. package/.kilo/plans/1780064105789-mighty-pixel.md +0 -171
  501. package/.kilo/plans/1780293725888-quick-wizard.md +0 -62
  502. package/.lonny/plan-web-cwd-status.md +0 -38
  503. package/AGENT.md +0 -99
  504. package/src/tools/bash.ts +0 -39
  505. package/src/tools/edit.ts +0 -554
  506. package/src/tools/exec.ts +0 -348
  507. package/src/tools/git.ts +0 -76
  508. package/src/tools/install_skill.ts +0 -539
  509. package/src/tui/components.ts +0 -635
@@ -1,857 +1,7 @@
1
- /* ── WebSocket Client for Lonny Web UI ───────────────────────── */
1
+ /* ── Entry Point ── */
2
2
 
3
- ;(() => {
4
- // ── DOM References ──
5
- const messagesEl = document.getElementById('messages')
6
- const chatInput = document.getElementById('chat-input')
7
- const sendBtn = document.getElementById('send-btn')
8
- const stopBtn = document.getElementById('stop-btn')
9
- const statusIndicator = document.getElementById('status-indicator')
10
- const modeDisplay = document.getElementById('mode-display')
11
- const modelDisplay = document.getElementById('model-display')
12
- const tokenIn = document.getElementById('token-in')
13
- const tokenOut = document.getElementById('token-out')
14
- const tokenCalls = document.getElementById('token-calls')
15
- const balanceDisplay = document.getElementById('balance-display')
16
- const balanceSep = document.getElementById('balance-sep')
17
- const cwdDisplay = document.getElementById('cwd-display')
18
- const connectionOverlay = document.getElementById('connection-overlay')
19
- const chatContainer = document.getElementById('chat-container')
3
+ import { initInput } from './input.js'
4
+ import { connect } from './websocket.js'
20
5
 
21
- // ── Sidebar DOM References ──
22
- const sidebar = document.getElementById('sidebar')
23
- const sidebarTabs = document.querySelectorAll('.sidebar-tab')
24
- const plansPane = document.getElementById('plans-pane')
25
- const todosPane = document.getElementById('todos-pane')
26
- const plansList = document.getElementById('plans-list')
27
- const todosList = document.getElementById('todos-list')
28
- const plansPlaceholder = document.getElementById('plans-placeholder')
29
- const todosPlaceholder = document.getElementById('todos-placeholder')
30
-
31
- // ── State ──
32
- let ws = null
33
- let isRunning = false
34
- let currentMode = 'code'
35
- let currentModel = ''
36
- let currentProvider = ''
37
- let streamingMsgEl = null
38
- let streamingText = ''
39
- let thinkingEl = null
40
- let thinkingText = ''
41
- let reconnectAttempts = 0
42
- let plans = []
43
- let currentPlanName = ''
44
- let todos = []
45
- const MAX_RECONNECT_ATTEMPTS = 10
46
- const RECONNECT_DELAY = 2000
47
- const pendingToolCalls = new Map() // id -> tool-call element
48
- let pendingConfirmResolve = null // for confirm dialog
49
-
50
- // ── Utility Functions ──
51
-
52
- function getWsUrl() {
53
- const proto = location.protocol === 'https:' ? 'wss:' : 'ws:'
54
- return `${proto}//${location.host}`
55
- }
56
-
57
- function scrollToBottom() {
58
- requestAnimationFrame(() => {
59
- chatContainer.scrollTop = chatContainer.scrollHeight
60
- })
61
- }
62
-
63
- function escapeHtml(text) {
64
- const div = document.createElement('div')
65
- div.textContent = text
66
- return div.innerHTML
67
- }
68
-
69
- function formatTimestamp() {
70
- return new Date().toLocaleTimeString()
71
- }
72
-
73
- function formatTokenCount(n) {
74
- if (n >= 1000000) return (n / 1000000).toFixed(n % 1000000 === 0 ? 0 : 1) + 'M'
75
- if (n >= 1000) return (n / 1000).toFixed(n % 1000 === 0 ? 0 : 1) + 'K'
76
- return String(n)
77
- }
78
-
79
- function setInputEnabled(enabled) {
80
- chatInput.disabled = !enabled
81
- sendBtn.disabled = !enabled
82
- sendBtn.classList.toggle('hidden', !enabled)
83
- stopBtn.classList.toggle('hidden', enabled)
84
- if (!enabled) {
85
- stopBtn.disabled = false
86
- }
87
- if (enabled) {
88
- chatInput.focus()
89
- }
90
- }
91
-
92
- function setStatus(running) {
93
- isRunning = running
94
- statusIndicator.textContent = running ? '● running' : '○ idle'
95
- statusIndicator.className = running ? 'status-running' : 'status-idle'
96
- setInputEnabled(!running)
97
- }
98
-
99
- function updateStats() {
100
- // Token stats are updated via messages from the server
101
- }
102
-
103
- // ── Sidebar Functions ──
104
-
105
- function switchTab(tabId) {
106
- sidebarTabs.forEach(tab => {
107
- tab.classList.toggle('active', tab.dataset.tab === tabId)
108
- })
109
- plansPane.classList.toggle('active', tabId === 'plans')
110
- todosPane.classList.toggle('active', tabId === 'todos')
111
- }
112
-
113
- function updatePlans() {
114
- plansList.innerHTML = ''
115
- if (plans.length === 0) {
116
- plansPlaceholder.style.display = ''
117
- return
118
- }
119
- plansPlaceholder.style.display = 'none'
120
- for (const plan of plans) {
121
- const div = document.createElement('div')
122
- div.className = 'plan-item' + (plan.name === currentPlanName ? ' active' : '')
123
- const timeStr = plan.mtime
124
- ? new Date(plan.mtime).toLocaleDateString() +
125
- ' ' +
126
- new Date(plan.mtime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
127
- : ''
128
- div.dataset.planName = plan.name
129
- div.innerHTML = `<div class="plan-name">${escapeHtml(plan.name)}</div><div class="plan-time">${escapeHtml(timeStr)}</div>`
130
- div.addEventListener('click', () => onPlanClick(plan.name))
131
- plansList.appendChild(div)
132
- }
133
- }
134
-
135
- function updateTodos() {
136
- todosList.innerHTML = ''
137
- if (!currentPlanName) {
138
- todosPlaceholder.textContent = '(no plan selected)'
139
- todosPlaceholder.style.display = ''
140
- return
141
- }
142
- if (todos.length === 0) {
143
- todosPlaceholder.textContent = '(no todos)'
144
- todosPlaceholder.style.display = ''
145
- return
146
- }
147
- todosPlaceholder.style.display = 'none'
148
- // Header
149
- const header = document.createElement('div')
150
- header.className = 'todo-header-text'
151
- header.textContent = currentPlanName
152
- todosList.appendChild(header)
153
- // Items
154
- for (const todo of todos) {
155
- const div = document.createElement('div')
156
- div.className = 'todo-item'
157
- const done = todo.done
158
- div.innerHTML =
159
- `<span class="todo-check ${done ? 'done' : 'pending'}">${done ? '✅' : '⬜'}</span>` +
160
- `<span class="todo-text ${done ? 'done' : ''}">${escapeHtml(todo.text)}</span>`
161
- todosList.appendChild(div)
162
- }
163
- }
164
-
165
- function onPlanClick(planName) {
166
- // Send load_plan message to server
167
- if (ws && ws.readyState === WebSocket.OPEN) {
168
- ws.send(JSON.stringify({ type: 'load_plan', planName }))
169
- }
170
- // Switch to the Todo tab
171
- switchTab('todos')
172
- }
173
-
174
- function updatePlansAndTodos() {
175
- updatePlans()
176
- updateTodos()
177
- }
178
-
179
- // ── Message Rendering ──
180
-
181
- function addSystemMessage(text) {
182
- const div = document.createElement('div')
183
- div.className = 'system-message'
184
- div.textContent = text
185
- messagesEl.appendChild(div)
186
- scrollToBottom()
187
- }
188
-
189
- function addUserMessage(text) {
190
- const msgDiv = document.createElement('div')
191
- msgDiv.className = 'message'
192
-
193
- const header = document.createElement('div')
194
- header.className = 'message-header'
195
- header.innerHTML = `<span class="label user">You</span> <span class="timestamp">${formatTimestamp()}</span>`
196
- msgDiv.appendChild(header)
197
-
198
- const body = document.createElement('div')
199
- body.className = 'message-body user'
200
- body.innerHTML = renderMarkdown(text)
201
- msgDiv.appendChild(body)
202
-
203
- messagesEl.appendChild(msgDiv)
204
- scrollToBottom()
205
- }
206
-
207
- function startAssistantMessage() {
208
- streamingText = ''
209
- streamingMsgEl = document.createElement('div')
210
- streamingMsgEl.className = 'message'
211
-
212
- const header = document.createElement('div')
213
- header.className = 'message-header'
214
- header.innerHTML = `<span class="label assistant">Lonny</span> <span class="timestamp">${formatTimestamp()}</span>`
215
- streamingMsgEl.appendChild(header)
216
-
217
- const body = document.createElement('div')
218
- body.className = 'message-body assistant streaming'
219
- body.textContent = ''
220
- streamingMsgEl.appendChild(body)
221
-
222
- messagesEl.appendChild(streamingMsgEl)
223
- scrollToBottom()
224
- }
225
-
226
- function renderMarkdown(text) {
227
- if (typeof marked !== 'undefined' && marked.parse) {
228
- return marked.parse(text, { breaks: true, gfm: true })
229
- }
230
- return '<pre>' + escapeHtml(text) + '</pre>'
231
- }
232
-
233
- function appendChunk(text) {
234
- if (!streamingMsgEl) {
235
- startAssistantMessage()
236
- }
237
- streamingText += text
238
- const body = streamingMsgEl.querySelector('.message-body')
239
- body.innerHTML = renderMarkdown(streamingText)
240
- // Ensure streaming cursor stays visible
241
- body.classList.add('streaming')
242
- scrollToBottom()
243
- }
244
-
245
- function finalizeAssistantMessage() {
246
- if (!streamingMsgEl) return
247
- const body = streamingMsgEl.querySelector('.message-body')
248
- // Re-render final text as markdown
249
- body.innerHTML = renderMarkdown(streamingText)
250
- body.classList.remove('streaming')
251
- streamingMsgEl = null
252
- streamingText = ''
253
- scrollToBottom()
254
- }
255
-
256
- function addToolCall(name, input, id) {
257
- const container = streamingMsgEl || messagesEl.querySelector('.message:last-child')
258
- const div = document.createElement('div')
259
- div.className = id ? 'tool-call executing' : 'tool-call'
260
- div.dataset.toolId = id || ''
261
-
262
- if (name === 'edit') {
263
- // Special display for edit tool: show file paths being edited
264
- const edits = input && input.edits
265
- const paths = Array.isArray(edits) ? edits.map(e => e.file_path).filter(Boolean) : []
266
- const uniquePaths = [...new Set(paths)]
267
- const pathsStr =
268
- uniquePaths.length > 0 ? uniquePaths.map(p => escapeHtml(p)).join(', ') : '(no files)'
269
- div.innerHTML = `<span class="tool-name tool-name-edit">✏ edit</span> <span class="tool-input tool-input-edit">${pathsStr}</span>`
270
- } else {
271
- const inputStr =
272
- typeof input === 'object' ? JSON.stringify(input).slice(0, 120) : String(input)
273
- div.innerHTML = `<span class="tool-name">${escapeHtml(name)}</span> <span class="tool-input">${escapeHtml(inputStr)}</span>`
274
- }
275
-
276
- if (container && container.matches('.message')) {
277
- container.appendChild(div)
278
- } else {
279
- messagesEl.appendChild(div)
280
- }
281
- if (id) pendingToolCalls.set(id, div)
282
- scrollToBottom()
283
- }
284
-
285
- function renderDiffContent(text) {
286
- const lines = text.split('\n')
287
- return lines
288
- .map(line => {
289
- const trimmed = line.trim()
290
- if (trimmed.startsWith('+ ')) {
291
- return `<span class="diff-added">${escapeHtml(line)}\n</span>`
292
- } else if (trimmed.startsWith('- ')) {
293
- return `<span class="diff-removed">${escapeHtml(line)}\n</span>`
294
- }
295
- return escapeHtml(line) + '\n'
296
- })
297
- .join('')
298
- }
299
-
300
- function addToolResult(name, success, outputOrError, id) {
301
- // Remove from pending tracking and clear executing state
302
- if (id) {
303
- const pendingEl = pendingToolCalls.get(id)
304
- if (pendingEl) {
305
- pendingEl.classList.remove('executing')
306
- pendingToolCalls.delete(id)
307
- // For edit tool, update the tool-call element to show completion summary
308
- if (name === 'edit' && success) {
309
- pendingEl.classList.add('completed')
310
- }
311
- }
312
- }
313
- const container = streamingMsgEl || messagesEl.querySelector('.message:last-child')
314
- const div = document.createElement('div')
315
- div.className = 'tool-result'
316
- if (success) {
317
- let display = outputOrError
318
- if (display === '(no output)') display = ''
319
- // Special display for edit tool success — show rich diff content
320
- if (name === 'edit' && display) {
321
- const lines = display.split('\n')
322
- const summaryLines = []
323
- const diffLines = []
324
- for (const line of lines) {
325
- const trimmed = line.trim()
326
- if (
327
- trimmed.startsWith('Edited ') ||
328
- trimmed.startsWith('Created ') ||
329
- trimmed.startsWith('Deleted ')
330
- ) {
331
- summaryLines.push(trimmed)
332
- } else if (trimmed.startsWith('- ') || trimmed.startsWith('+ ')) {
333
- diffLines.push(line)
334
- }
335
- }
336
- let html = `<span class="tool-result-success">✔ ${escapeHtml(name)}</span>`
337
- if (summaryLines.length > 0) {
338
- html += `<span class="tool-result-summary">${summaryLines.map(s => escapeHtml(s)).join('<br>')}</span>`
339
- }
340
- if (diffLines.length > 0) {
341
- html += `<span class="tool-result-diff"><pre><code>${renderDiffContent(diffLines.join('\n'))}</code></pre></span>`
342
- } else if (display.includes(' FAIL ')) {
343
- html += `<span class="tool-result-diff"><pre>${escapeHtml(display)}</pre></span>`
344
- }
345
- div.innerHTML = html
346
- } else {
347
- const summary = typeof display === 'string' ? display.slice(0, 80) : ''
348
- div.innerHTML = `<span class="tool-result-success">✔ ${escapeHtml(name)}</span>${summary ? ' ' + escapeHtml(summary) : ''}`
349
- }
350
- } else {
351
- div.innerHTML = `<span class="tool-result-error">✖ ${escapeHtml(name)}</span> ${escapeHtml(outputOrError)}`
352
- }
353
- if (container && container.matches('.message')) {
354
- container.appendChild(div)
355
- } else {
356
- messagesEl.appendChild(div)
357
- }
358
- scrollToBottom()
359
- }
360
-
361
- function showConfirmDialog(toolCalls) {
362
- const overlay = document.getElementById('confirm-overlay')
363
- const list = document.getElementById('confirm-tool-list')
364
- if (!overlay || !list) return
365
- list.innerHTML = ''
366
- for (const tc of toolCalls) {
367
- const detail = tc.input?.file_path || tc.input?.command || tc.input?.package_name || ''
368
- const item = document.createElement('div')
369
- item.className = 'confirm-tool-item'
370
- item.innerHTML =
371
- `<span class="confirm-tool-name">${escapeHtml(tc.name)}</span>` +
372
- (detail
373
- ? ` <span class="confirm-tool-input">${escapeHtml(detail.length > 80 ? detail.slice(0, 80) + '…' : detail)}</span>`
374
- : '')
375
- list.appendChild(item)
376
- }
377
- overlay.classList.remove('hidden')
378
-
379
- pendingConfirmResolve = null
380
-
381
- function cleanup(approved) {
382
- overlay.classList.add('hidden')
383
- allowBtn.removeEventListener('click', onAllow)
384
- rejectBtn.removeEventListener('click', onReject)
385
- if (pendingConfirmResolve) {
386
- pendingConfirmResolve(approved)
387
- pendingConfirmResolve = null
388
- }
389
- }
390
-
391
- const allowBtn = document.getElementById('confirm-allow-btn')
392
- const rejectBtn = document.getElementById('confirm-reject-btn')
393
- const onAllow = () => {
394
- if (ws && ws.readyState === WebSocket.OPEN) {
395
- ws.send(JSON.stringify({ type: 'tool_confirm_response', approved: true }))
396
- }
397
- cleanup(true)
398
- }
399
- const onReject = () => {
400
- if (ws && ws.readyState === WebSocket.OPEN) {
401
- ws.send(JSON.stringify({ type: 'tool_confirm_response', approved: false }))
402
- }
403
- cleanup(false)
404
- }
405
- allowBtn.addEventListener('click', onAllow)
406
- rejectBtn.addEventListener('click', onReject)
407
- }
408
-
409
- function closeConfirmDialog() {
410
- const overlay = document.getElementById('confirm-overlay')
411
- if (overlay && !overlay.classList.contains('hidden')) {
412
- overlay.classList.add('hidden')
413
- if (pendingConfirmResolve) {
414
- pendingConfirmResolve(false)
415
- pendingConfirmResolve = null
416
- }
417
- }
418
- }
419
-
420
- function showThinking(text) {
421
- if (!thinkingEl) {
422
- thinkingEl = document.createElement('div')
423
- thinkingEl.className = 'thinking-block'
424
- thinkingEl.innerHTML =
425
- '<div class="thinking-label">🤔 Think</div><div class="thinking-content"></div>'
426
- // Insert inside the streaming message (before .message-body) so it stays
427
- // within the assistant message flow, not as a separate top-level element.
428
- if (streamingMsgEl) {
429
- const body = streamingMsgEl.querySelector('.message-body')
430
- streamingMsgEl.insertBefore(thinkingEl, body)
431
- } else {
432
- messagesEl.appendChild(thinkingEl)
433
- }
434
- thinkingText = ''
435
- }
436
- thinkingText += text
437
- const content = thinkingEl.querySelector('.thinking-content')
438
- content.innerHTML = renderMarkdown(thinkingText)
439
- scrollToBottom()
440
- }
441
-
442
- function hideThinking() {
443
- if (thinkingEl) {
444
- // Don't remove — keep the thinking content visible in the message flow.
445
- // Just mark it as finalized and clear the reference so new thinking
446
- // blocks can be created for subsequent turns.
447
- thinkingEl.classList.add('thinking-done')
448
- thinkingEl = null
449
- thinkingText = ''
450
- }
451
- }
452
-
453
- function renderSessionHistory(messages) {
454
- if (!Array.isArray(messages)) return
455
- for (const msg of messages) {
456
- if (msg.role === 'user') {
457
- addUserMessage(msg.content || '')
458
- } else if (msg.role === 'assistant') {
459
- startAssistantMessage()
460
- if (msg.reasoning_content) {
461
- showThinking(msg.reasoning_content)
462
- hideThinking()
463
- }
464
- if (msg.content) {
465
- streamingText = msg.content
466
- const body = streamingMsgEl.querySelector('.message-body')
467
- body.innerHTML = renderMarkdown(msg.content)
468
- }
469
- if (msg.tool_calls && msg.tool_calls.length > 0) {
470
- for (const tc of msg.tool_calls) {
471
- addToolCall(tc.name, tc.input)
472
- }
473
- }
474
- finalizeAssistantMessage()
475
- } else if (msg.role === 'tool') {
476
- const content = msg.content || ''
477
- const isError = content.startsWith('ERROR: ')
478
- if (isError) {
479
- addToolResult(msg.name || '', false, content.replace(/^ERROR:\s*/, ''))
480
- } else {
481
- addToolResult(msg.name || '', true, content)
482
- }
483
- }
484
- }
485
- }
486
-
487
- function addTokenStats(turnIn, turnOut, totalIn, totalOut, turnApi, totalApi) {
488
- const div = document.createElement('div')
489
- div.className = 'token-stats-bar'
490
- div.textContent = `▴${turnIn} ▾${turnOut} total ${totalIn + totalOut} calls ${turnApi}(${totalApi})`
491
- messagesEl.appendChild(div)
492
- scrollToBottom()
493
-
494
- // Update status bar (use formatted values)
495
- tokenIn.textContent = formatTokenCount(totalIn)
496
- tokenOut.textContent = formatTokenCount(totalOut)
497
- tokenCalls.textContent = `(${totalApi})`
498
- }
499
-
500
- function addErrorMessage(text) {
501
- const div = document.createElement('div')
502
- div.className = 'message'
503
- div.innerHTML = `<div class="message-body error">${escapeHtml(text)}</div>`
504
- messagesEl.appendChild(div)
505
- scrollToBottom()
506
- }
507
-
508
- // ── WebSocket Connection ──
509
-
510
- function connect() {
511
- connectionOverlay.classList.remove('hidden')
512
-
513
- try {
514
- ws = new WebSocket(getWsUrl())
515
- } catch (err) {
516
- connectionOverlay.classList.add('hidden')
517
- addErrorMessage(`Connection failed: ${err.message}`)
518
- return
519
- }
520
-
521
- ws.onopen = () => {
522
- connectionOverlay.classList.add('hidden')
523
- reconnectAttempts = 0
524
- addSystemMessage('Connected to Lonny')
525
- }
526
-
527
- ws.onmessage = event => {
528
- try {
529
- const msg = JSON.parse(event.data)
530
- handleMessage(msg)
531
- } catch (err) {
532
- console.error('Failed to parse message:', err)
533
- }
534
- }
535
-
536
- ws.onclose = () => {
537
- setStatus(false)
538
- hideThinking()
539
- // Clear any pending tool call spinners on disconnect
540
- for (const [, el] of pendingToolCalls) el.classList.remove('executing')
541
- pendingToolCalls.clear()
542
- if (streamingMsgEl) {
543
- finalizeAssistantMessage()
544
- }
545
-
546
- if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
547
- reconnectAttempts++
548
- const delay = RECONNECT_DELAY * Math.min(reconnectAttempts, 5)
549
- connectionOverlay.querySelector('p').textContent =
550
- `Reconnecting in ${delay / 1000}s... (attempt ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`
551
- connectionOverlay.classList.remove('hidden')
552
- setTimeout(connect, delay)
553
- } else {
554
- connectionOverlay.querySelector('p').textContent =
555
- 'Connection lost. Please refresh the page.'
556
- connectionOverlay.classList.remove('hidden')
557
- addErrorMessage('Connection lost. Refresh the page to reconnect.')
558
- }
559
- }
560
-
561
- ws.onerror = () => {
562
- // onclose will fire after this
563
- }
564
- }
565
-
566
- // ── Message Handler ──
567
-
568
- function handleMessage(msg) {
569
- switch (msg.type) {
570
- case 'hello':
571
- currentMode = msg.mode || 'code'
572
- currentModel = msg.model || ''
573
- currentProvider = msg.provider || ''
574
- modeDisplay.textContent = currentMode
575
- modelDisplay.textContent = `${currentProvider}/${currentModel}`
576
- // Sync token stats from session (formatted)
577
- if (msg.totalIn !== undefined) {
578
- tokenIn.textContent = formatTokenCount(msg.totalIn || 0)
579
- tokenOut.textContent = formatTokenCount(msg.totalOut || 0)
580
- tokenCalls.textContent = `(${msg.totalApi || 0})`
581
- }
582
- // Show DeepSeek balance if available
583
- if (msg.webBalance) {
584
- balanceDisplay.textContent = '余额:' + msg.webBalance
585
- balanceDisplay.style.display = ''
586
- balanceSep.style.display = ''
587
- } else if (msg.balance) {
588
- balanceDisplay.textContent = '余额:' + msg.balance
589
- balanceDisplay.style.display = ''
590
- balanceSep.style.display = ''
591
- } else {
592
- balanceDisplay.style.display = 'none'
593
- balanceSep.style.display = 'none'
594
- }
595
- // Show working directory
596
- if (msg.cwd) {
597
- const maxLen = 35
598
- cwdDisplay.textContent =
599
- msg.cwd.length > maxLen ? '...' + msg.cwd.slice(-maxLen) : msg.cwd
600
- }
601
- break
602
-
603
- case 'chunk':
604
- appendChunk(msg.text || '')
605
- break
606
-
607
- case 'thinking':
608
- showThinking(msg.text || '')
609
- break
610
-
611
- case 'thinking_end':
612
- hideThinking()
613
- break
614
-
615
- case 'tool_call':
616
- addToolCall(msg.name, msg.input, msg.id)
617
- break
618
-
619
- case 'tool_result':
620
- if (msg.success) {
621
- addToolResult(msg.name, true, msg.output || '', msg.id)
622
- } else {
623
- addToolResult(msg.name, false, msg.error || 'Unknown error', msg.id)
624
- }
625
- break
626
-
627
- case 'turn_start':
628
- setStatus(true)
629
- // Start a fresh assistant message container for the upcoming response
630
- startAssistantMessage()
631
- break
632
-
633
- case 'turn_end':
634
- hideThinking()
635
- // Clear any remaining pending tool call spinners
636
- for (const [, el] of pendingToolCalls) el.classList.remove('executing')
637
- pendingToolCalls.clear()
638
- finalizeAssistantMessage()
639
- break
640
-
641
- case 'done':
642
- setStatus(false)
643
- hideThinking()
644
- // Clear any remaining pending tool call spinners
645
- for (const [, el] of pendingToolCalls) el.classList.remove('executing')
646
- pendingToolCalls.clear()
647
- finalizeAssistantMessage()
648
- if (msg.reason === 'error') {
649
- addErrorMessage('An error occurred during processing.')
650
- }
651
- break
652
-
653
- case 'mode_changed':
654
- currentMode = msg.mode
655
- modeDisplay.textContent = currentMode
656
- addSystemMessage(`Switched to ${currentMode} mode`)
657
- break
658
-
659
- case 'model_changed':
660
- currentModel = msg.model
661
- modelDisplay.textContent = `${currentProvider}/${currentModel}`
662
- addSystemMessage(`Model switched to ${currentModel}`)
663
- break
664
-
665
- case 'session_cleared':
666
- messagesEl.innerHTML = ''
667
- addSystemMessage('Session cleared. Starting fresh.')
668
- break
669
-
670
- case 'help':
671
- if (Array.isArray(msg.commands)) {
672
- addSystemMessage('Available commands:')
673
- msg.commands.forEach(cmd => addSystemMessage(cmd))
674
- }
675
- break
676
-
677
- case 'compaction':
678
- addSystemMessage(`📦 Compressed context: ${msg.before} → ${msg.after} messages`)
679
- break
680
-
681
- case 'plan_written':
682
- addSystemMessage(`📝 Plan written: ${msg.display || ''}`)
683
- break
684
-
685
- case 'session_history':
686
- renderSessionHistory(msg.messages)
687
- // Scroll to bottom after all messages are rendered
688
- setTimeout(scrollToBottom, 50)
689
- break
690
-
691
- case 'plan_data':
692
- plans = msg.plans || []
693
- currentPlanName = msg.currentPlanName || ''
694
- todos = msg.todos || []
695
- updatePlansAndTodos()
696
- break
697
-
698
- case 'balance_update':
699
- if (msg.webBalance) {
700
- balanceDisplay.textContent = '余额:' + msg.webBalance
701
- balanceDisplay.style.display = ''
702
- balanceSep.style.display = ''
703
- } else if (msg.balance) {
704
- balanceDisplay.textContent = '余额:' + msg.balance
705
- balanceDisplay.style.display = ''
706
- balanceSep.style.display = ''
707
- }
708
- break
709
-
710
- case 'token_stats':
711
- addTokenStats(msg.turnIn, msg.turnOut, msg.totalIn, msg.totalOut, msg.turnApi, msg.totalApi)
712
- break
713
-
714
- case 'tool_confirm_request': {
715
- if (!msg.toolCalls || msg.toolCalls.length === 0) break
716
- showConfirmDialog(msg.toolCalls)
717
- break
718
- }
719
-
720
- case 'error':
721
- closeConfirmDialog()
722
- addErrorMessage(msg.message || 'Unknown error')
723
- break
724
-
725
- case 'pong':
726
- break
727
-
728
- default:
729
- console.log('Unknown message type:', msg.type)
730
- }
731
- }
732
-
733
- // ── Confirm dialog: click overlay background to close ──
734
- document.getElementById('confirm-overlay').addEventListener('click', e => {
735
- if (e.target === e.currentTarget) {
736
- closeConfirmDialog()
737
- }
738
- })
739
-
740
- // ── Send Message ──
741
-
742
- function sendMessage() {
743
- const text = chatInput.value.trim()
744
- if (!text || isRunning) return
745
-
746
- // Add user message to chat
747
- addUserMessage(text)
748
- chatInput.value = ''
749
-
750
- // Send to server
751
- if (ws && ws.readyState === WebSocket.OPEN) {
752
- ws.send(JSON.stringify({ type: 'message', text }))
753
- } else {
754
- addErrorMessage('Not connected to server.')
755
- }
756
- }
757
-
758
- // ── Event Listeners ──
759
-
760
- sendBtn.addEventListener('click', sendMessage)
761
-
762
- // Stop button
763
- stopBtn.addEventListener('click', () => {
764
- if (ws && ws.readyState === WebSocket.OPEN) {
765
- ws.send(JSON.stringify({ type: 'stop' }))
766
- }
767
- stopBtn.disabled = true
768
- })
769
-
770
- chatInput.addEventListener('keydown', e => {
771
- if (e.key === 'Enter' && !e.shiftKey) {
772
- e.preventDefault()
773
- sendMessage()
774
- }
775
- // Tab to select hint item
776
- if (e.key === 'Tab' && !slashHint.classList.contains('hidden')) {
777
- e.preventDefault()
778
- const selected = slashHint.querySelector('.slash-hint-item.selected')
779
- if (selected) {
780
- const cmd = selected.dataset.cmd
781
- chatInput.value = '/' + cmd
782
- hideSlashHint()
783
- chatInput.focus()
784
- }
785
- }
786
- if (e.key === 'Escape') {
787
- hideSlashHint()
788
- }
789
- })
790
-
791
- // Slash command hint
792
- const slashHint = document.getElementById('slash-hint')
793
-
794
- function showSlashHint() {
795
- slashHint.classList.remove('hidden')
796
- // Select first item by default
797
- const items = slashHint.querySelectorAll('.slash-hint-item')
798
- items.forEach((item, i) => {
799
- item.classList.toggle('selected', i === 0)
800
- })
801
- }
802
-
803
- function hideSlashHint() {
804
- slashHint.classList.add('hidden')
805
- }
806
-
807
- // Click to select a hint item
808
- slashHint.addEventListener('click', e => {
809
- const item = e.target.closest('.slash-hint-item')
810
- if (item) {
811
- const cmd = item.dataset.cmd
812
- chatInput.value = '/' + cmd
813
- hideSlashHint()
814
- chatInput.focus()
815
- }
816
- })
817
-
818
- // Hover to change selection
819
- slashHint.addEventListener('mouseover', e => {
820
- const item = e.target.closest('.slash-hint-item')
821
- if (item) {
822
- slashHint.querySelectorAll('.slash-hint-item').forEach(el => el.classList.remove('selected'))
823
- item.classList.add('selected')
824
- }
825
- })
826
-
827
- // Show/hide hint based on input content
828
- chatInput.addEventListener('input', () => {
829
- chatInput.style.height = 'auto'
830
- chatInput.style.height = Math.min(chatInput.scrollHeight, 150) + 'px'
831
-
832
- const text = chatInput.value
833
- // Show hint only when the text starts with / and is only the / (no other chars yet)
834
- if (text === '/') {
835
- showSlashHint()
836
- } else if (!text.startsWith('/') || text.includes(' ')) {
837
- hideSlashHint()
838
- }
839
- })
840
-
841
- // ── Sidebar Tab Switching ──
842
- sidebarTabs.forEach(tab => {
843
- tab.addEventListener('click', () => {
844
- switchTab(tab.dataset.tab)
845
- })
846
- })
847
-
848
- // ── Heartbeat (keep connection alive) ──
849
- setInterval(() => {
850
- if (ws && ws.readyState === WebSocket.OPEN) {
851
- ws.send(JSON.stringify({ type: 'ping' }))
852
- }
853
- }, 30000)
854
-
855
- // ── Initialize ──
856
- connect()
857
- })()
6
+ connect()
7
+ initInput()