kanna-code 0.20.0 → 0.21.1

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 (326) hide show
  1. package/dist/client/assets/abap-BdImnpbu.js +1 -0
  2. package/dist/client/assets/actionscript-3-CoDkCxhg.js +1 -0
  3. package/dist/client/assets/ada-bCR0ucgS.js +1 -0
  4. package/dist/client/assets/andromeeda-C4gqWexZ.js +1 -0
  5. package/dist/client/assets/angular-html-CU67Zn6k.js +1 -0
  6. package/dist/client/assets/angular-ts-BwZT4LLn.js +1 -0
  7. package/dist/client/assets/apache-Pmp26Uib.js +1 -0
  8. package/dist/client/assets/apex-D8_7TLub.js +1 -0
  9. package/dist/client/assets/apl-dKokRX4l.js +1 -0
  10. package/dist/client/assets/applescript-Co6uUVPk.js +1 -0
  11. package/dist/client/assets/ara-BRHolxvo.js +1 -0
  12. package/dist/client/assets/asciidoc-Ve4PFQV2.js +1 -0
  13. package/dist/client/assets/asm-D_Q5rh1f.js +1 -0
  14. package/dist/client/assets/astro-CbQHKStN.js +1 -0
  15. package/dist/client/assets/aurora-x-D-2ljcwZ.js +1 -0
  16. package/dist/client/assets/awk-DMzUqQB5.js +1 -0
  17. package/dist/client/assets/ayu-dark-DYE7WIF3.js +1 -0
  18. package/dist/client/assets/ayu-light-BA47KaF1.js +1 -0
  19. package/dist/client/assets/ayu-mirage-32ctXXKs.js +1 -0
  20. package/dist/client/assets/ballerina-BFfxhgS-.js +1 -0
  21. package/dist/client/assets/bat-BkioyH1T.js +1 -0
  22. package/dist/client/assets/beancount-k_qm7-4y.js +1 -0
  23. package/dist/client/assets/berry-uYugtg8r.js +1 -0
  24. package/dist/client/assets/bibtex-CHM0blh-.js +1 -0
  25. package/dist/client/assets/bicep-Bmn6On1c.js +1 -0
  26. package/dist/client/assets/bird2-DPOp833l.js +1 -0
  27. package/dist/client/assets/blade-D4QpJJKB.js +1 -0
  28. package/dist/client/assets/bsl-BO_Y6i37.js +1 -0
  29. package/dist/client/assets/c-BIGW1oBm.js +1 -0
  30. package/dist/client/assets/c3-eo99z4R2.js +1 -0
  31. package/dist/client/assets/cadence-Bv_4Rxtq.js +1 -0
  32. package/dist/client/assets/cairo-KRGpt6FW.js +1 -0
  33. package/dist/client/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  34. package/dist/client/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  35. package/dist/client/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  36. package/dist/client/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  37. package/dist/client/assets/clarity-D53aC0YG.js +1 -0
  38. package/dist/client/assets/clojure-P80f7IUj.js +1 -0
  39. package/dist/client/assets/cmake-D1j8_8rp.js +1 -0
  40. package/dist/client/assets/cobol-nwyudZeR.js +1 -0
  41. package/dist/client/assets/codeowners-Bp6g37R7.js +1 -0
  42. package/dist/client/assets/codeql-DsOJ9woJ.js +1 -0
  43. package/dist/client/assets/coffee-Ch7k5sss.js +1 -0
  44. package/dist/client/assets/common-lisp-Cg-RD9OK.js +1 -0
  45. package/dist/client/assets/coq-DkFqJrB1.js +1 -0
  46. package/dist/client/assets/cpp-CofmeUqb.js +1 -0
  47. package/dist/client/assets/crystal-tKQVLTB8.js +1 -0
  48. package/dist/client/assets/csharp-COcwbKMJ.js +1 -0
  49. package/dist/client/assets/css-DPfMkruS.js +1 -0
  50. package/dist/client/assets/csv-fuZLfV_i.js +1 -0
  51. package/dist/client/assets/cue-D82EKSYY.js +1 -0
  52. package/dist/client/assets/cypher-COkxafJQ.js +1 -0
  53. package/dist/client/assets/d-85-TOEBH.js +1 -0
  54. package/dist/client/assets/dark-plus-C3mMm8J8.js +1 -0
  55. package/dist/client/assets/dart-CF10PKvl.js +1 -0
  56. package/dist/client/assets/dax-CEL-wOlO.js +1 -0
  57. package/dist/client/assets/desktop-BmXAJ9_W.js +1 -0
  58. package/dist/client/assets/diff-D97Zzqfu.js +1 -0
  59. package/dist/client/assets/docker-BcOcwvcX.js +1 -0
  60. package/dist/client/assets/dotenv-Da5cRb03.js +1 -0
  61. package/dist/client/assets/dracula-BzJJZx-M.js +1 -0
  62. package/dist/client/assets/dracula-soft-BXkSAIEj.js +1 -0
  63. package/dist/client/assets/dream-maker-BtqSS_iP.js +1 -0
  64. package/dist/client/assets/edge-BkV0erSs.js +1 -0
  65. package/dist/client/assets/elixir-CDX3lj18.js +1 -0
  66. package/dist/client/assets/elm-DbKCFpqz.js +1 -0
  67. package/dist/client/assets/emacs-lisp-C9XAeP06.js +1 -0
  68. package/dist/client/assets/erb-B12qg9BL.js +1 -0
  69. package/dist/client/assets/erlang-DsQrWhSR.js +1 -0
  70. package/dist/client/assets/everforest-dark-BgDCqdQA.js +1 -0
  71. package/dist/client/assets/everforest-light-C8M2exoo.js +1 -0
  72. package/dist/client/assets/fennel-BYunw83y.js +1 -0
  73. package/dist/client/assets/fish-BvzEVeQv.js +1 -0
  74. package/dist/client/assets/fluent-C4IJs8-o.js +1 -0
  75. package/dist/client/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  76. package/dist/client/assets/fortran-free-form-BxgE0vQu.js +1 -0
  77. package/dist/client/assets/fsharp-CXgrBDvD.js +1 -0
  78. package/dist/client/assets/gdresource-BOOCDP_w.js +1 -0
  79. package/dist/client/assets/gdscript-C5YyOfLZ.js +1 -0
  80. package/dist/client/assets/gdshader-DkwncUOv.js +1 -0
  81. package/dist/client/assets/genie-D0YGMca9.js +1 -0
  82. package/dist/client/assets/gherkin-DyxjwDmM.js +1 -0
  83. package/dist/client/assets/git-commit-F4YmCXRG.js +1 -0
  84. package/dist/client/assets/git-rebase-r7XF79zn.js +1 -0
  85. package/dist/client/assets/github-dark-DHJKELXO.js +1 -0
  86. package/dist/client/assets/github-dark-default-Cuk6v7N8.js +1 -0
  87. package/dist/client/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  88. package/dist/client/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  89. package/dist/client/assets/github-light-DAi9KRSo.js +1 -0
  90. package/dist/client/assets/github-light-default-D7oLnXFd.js +1 -0
  91. package/dist/client/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  92. package/dist/client/assets/gleam-BspZqrRM.js +1 -0
  93. package/dist/client/assets/glimmer-js-Rg0-pVw9.js +1 -0
  94. package/dist/client/assets/glimmer-ts-U6CK756n.js +1 -0
  95. package/dist/client/assets/glsl-DplSGwfg.js +1 -0
  96. package/dist/client/assets/gn-n2N0HUVH.js +1 -0
  97. package/dist/client/assets/gnuplot-DdkO51Og.js +1 -0
  98. package/dist/client/assets/go-CxLEBnE3.js +1 -0
  99. package/dist/client/assets/graphql-ChdNCCLP.js +1 -0
  100. package/dist/client/assets/groovy-gcz8RCvz.js +1 -0
  101. package/dist/client/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  102. package/dist/client/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  103. package/dist/client/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  104. package/dist/client/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  105. package/dist/client/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  106. package/dist/client/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  107. package/dist/client/assets/hack-CaT9iCJl.js +1 -0
  108. package/dist/client/assets/haml-B8DHNrY2.js +1 -0
  109. package/dist/client/assets/handlebars-BL8al0AC.js +1 -0
  110. package/dist/client/assets/haskell-Df6bDoY_.js +1 -0
  111. package/dist/client/assets/haxe-CzTSHFRz.js +1 -0
  112. package/dist/client/assets/hcl-BWvSN4gD.js +1 -0
  113. package/dist/client/assets/hjson-D5-asLiD.js +1 -0
  114. package/dist/client/assets/hlsl-D3lLCCz7.js +1 -0
  115. package/dist/client/assets/horizon-BUw7H-hv.js +1 -0
  116. package/dist/client/assets/horizon-bright-Cn-bp-IR.js +1 -0
  117. package/dist/client/assets/houston-DnULxvSX.js +1 -0
  118. package/dist/client/assets/html-GMplVEZG.js +1 -0
  119. package/dist/client/assets/html-derivative-BFtXZ54Q.js +1 -0
  120. package/dist/client/assets/http-jrhK8wxY.js +1 -0
  121. package/dist/client/assets/hurl-irOxFIW8.js +1 -0
  122. package/dist/client/assets/hxml-Bvhsp5Yf.js +1 -0
  123. package/dist/client/assets/hy-DFXneXwc.js +1 -0
  124. package/dist/client/assets/imba-DGztddWO.js +1 -0
  125. package/dist/client/assets/index-BsILdq_R.js +2502 -0
  126. package/dist/client/assets/index-C_DLHgnt.css +32 -0
  127. package/dist/client/assets/ini-BEwlwnbL.js +1 -0
  128. package/dist/client/assets/java-CylS5w8V.js +1 -0
  129. package/dist/client/assets/javascript-wDzz0qaB.js +1 -0
  130. package/dist/client/assets/jinja-4LBKfQ-Z.js +1 -0
  131. package/dist/client/assets/jison-wvAkD_A8.js +1 -0
  132. package/dist/client/assets/json-Cp-IABpG.js +1 -0
  133. package/dist/client/assets/json5-C9tS-k6U.js +1 -0
  134. package/dist/client/assets/jsonc-Des-eS-w.js +1 -0
  135. package/dist/client/assets/jsonl-DcaNXYhu.js +1 -0
  136. package/dist/client/assets/jsonnet-DFQXde-d.js +1 -0
  137. package/dist/client/assets/jssm-C2t-YnRu.js +1 -0
  138. package/dist/client/assets/jsx-g9-lgVsj.js +1 -0
  139. package/dist/client/assets/julia-CxzCAyBv.js +1 -0
  140. package/dist/client/assets/just-Cw27pwNe.js +1 -0
  141. package/dist/client/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  142. package/dist/client/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  143. package/dist/client/assets/kanagawa-wave-DWedfzmr.js +1 -0
  144. package/dist/client/assets/kdl-DV7GczEv.js +1 -0
  145. package/dist/client/assets/kotlin-BdnUsdx6.js +1 -0
  146. package/dist/client/assets/kusto-DZf3V79B.js +1 -0
  147. package/dist/client/assets/laserwave-DUszq2jm.js +1 -0
  148. package/dist/client/assets/latex-CWtU0Tv5.js +1 -0
  149. package/dist/client/assets/lean-BZvkOJ9d.js +1 -0
  150. package/dist/client/assets/less-B1dDrJ26.js +1 -0
  151. package/dist/client/assets/light-plus-B7mTdjB0.js +1 -0
  152. package/dist/client/assets/liquid-DYVedYrR.js +1 -0
  153. package/dist/client/assets/llvm-DjAJT7YJ.js +1 -0
  154. package/dist/client/assets/log-2UxHyX5q.js +1 -0
  155. package/dist/client/assets/logo-BtOb2qkB.js +1 -0
  156. package/dist/client/assets/lua-BaeVxFsk.js +1 -0
  157. package/dist/client/assets/luau-C-HG3fhB.js +1 -0
  158. package/dist/client/assets/make-CHLpvVh8.js +1 -0
  159. package/dist/client/assets/markdown-Cvjx9yec.js +1 -0
  160. package/dist/client/assets/marko-CnJfTvn9.js +1 -0
  161. package/dist/client/assets/material-theme-D5KoaKCx.js +1 -0
  162. package/dist/client/assets/material-theme-darker-BfHTSMKl.js +1 -0
  163. package/dist/client/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  164. package/dist/client/assets/material-theme-ocean-CyktbL80.js +1 -0
  165. package/dist/client/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  166. package/dist/client/assets/matlab-D7o27uSR.js +1 -0
  167. package/dist/client/assets/mdc-BMNejdWA.js +1 -0
  168. package/dist/client/assets/mdx-Cmh6b_Ma.js +1 -0
  169. package/dist/client/assets/mermaid-mWjccvbQ.js +1 -0
  170. package/dist/client/assets/min-dark-CafNBF8u.js +1 -0
  171. package/dist/client/assets/min-light-CTRr51gU.js +1 -0
  172. package/dist/client/assets/mipsasm-CKIfxQSi.js +1 -0
  173. package/dist/client/assets/mojo-rZm6bMo-.js +1 -0
  174. package/dist/client/assets/monokai-D4h5O-jR.js +1 -0
  175. package/dist/client/assets/moonbit-_H4v1dQx.js +1 -0
  176. package/dist/client/assets/move-IF9eRakj.js +1 -0
  177. package/dist/client/assets/narrat-DRg8JJMk.js +1 -0
  178. package/dist/client/assets/nextflow-Zz6hmt5N.js +1 -0
  179. package/dist/client/assets/nextflow-groovy-BeH2EWoN.js +1 -0
  180. package/dist/client/assets/nginx-BpAMiNFr.js +1 -0
  181. package/dist/client/assets/night-owl-C39BiMTA.js +1 -0
  182. package/dist/client/assets/night-owl-light-CMTm3GFP.js +1 -0
  183. package/dist/client/assets/nim-CVrawwO9.js +1 -0
  184. package/dist/client/assets/nix-CwoSXNpI.js +1 -0
  185. package/dist/client/assets/nord-Ddv68eIx.js +1 -0
  186. package/dist/client/assets/nushell-Cz2AlsmD.js +1 -0
  187. package/dist/client/assets/objective-c-DXmwc3jG.js +1 -0
  188. package/dist/client/assets/objective-cpp-CLxacb5B.js +1 -0
  189. package/dist/client/assets/ocaml-C0hk2d4L.js +1 -0
  190. package/dist/client/assets/odin-BBf5iR-q.js +1 -0
  191. package/dist/client/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  192. package/dist/client/assets/one-light-C3Wv6jpd.js +1 -0
  193. package/dist/client/assets/openscad-C4EeE6gA.js +1 -0
  194. package/dist/client/assets/pascal-D93ZcfNL.js +1 -0
  195. package/dist/client/assets/perl-C0TMdlhV.js +1 -0
  196. package/dist/client/assets/php-Dhbhpdrm.js +1 -0
  197. package/dist/client/assets/pierre-dark-DF2SEV7i.js +1 -0
  198. package/dist/client/assets/pierre-light-DOlZxES8.js +1 -0
  199. package/dist/client/assets/pkl-u5AG7uiY.js +1 -0
  200. package/dist/client/assets/plastic-3e1v2bzS.js +1 -0
  201. package/dist/client/assets/plsql-ChMvpjG-.js +1 -0
  202. package/dist/client/assets/po-BTJTHyun.js +1 -0
  203. package/dist/client/assets/poimandres-CS3Unz2-.js +1 -0
  204. package/dist/client/assets/polar-C0HS_06l.js +1 -0
  205. package/dist/client/assets/postcss-CXtECtnM.js +1 -0
  206. package/dist/client/assets/powerquery-CEu0bR-o.js +1 -0
  207. package/dist/client/assets/powershell-Dpen1YoG.js +1 -0
  208. package/dist/client/assets/prisma-Dd19v3D-.js +1 -0
  209. package/dist/client/assets/prolog-CbFg5uaA.js +1 -0
  210. package/dist/client/assets/proto-C7zT0LnQ.js +1 -0
  211. package/dist/client/assets/pug-CGlum2m_.js +1 -0
  212. package/dist/client/assets/puppet-BMWR74SV.js +1 -0
  213. package/dist/client/assets/purescript-CklMAg4u.js +1 -0
  214. package/dist/client/assets/python-B6aJPvgy.js +1 -0
  215. package/dist/client/assets/qml-3beO22l8.js +1 -0
  216. package/dist/client/assets/qmldir-C8lEn-DE.js +1 -0
  217. package/dist/client/assets/qss-IeuSbFQv.js +1 -0
  218. package/dist/client/assets/r-Dspwwk_N.js +1 -0
  219. package/dist/client/assets/racket-BqYA7rlc.js +1 -0
  220. package/dist/client/assets/raku-DXvB9xmW.js +1 -0
  221. package/dist/client/assets/razor-Uh8Bk_45.js +1 -0
  222. package/dist/client/assets/red-bN70gL4F.js +1 -0
  223. package/dist/client/assets/reg-C-SQnVFl.js +1 -0
  224. package/dist/client/assets/regexp-CDVJQ6XC.js +1 -0
  225. package/dist/client/assets/rel-C3B-1QV4.js +1 -0
  226. package/dist/client/assets/riscv-BM1_JUlF.js +1 -0
  227. package/dist/client/assets/ron-D8l8udqQ.js +1 -0
  228. package/dist/client/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
  229. package/dist/client/assets/rose-pine-moon-D4_iv3hh.js +1 -0
  230. package/dist/client/assets/rose-pine-qdsjHGoJ.js +1 -0
  231. package/dist/client/assets/rosmsg-BJDFO7_C.js +1 -0
  232. package/dist/client/assets/rst-BrH8l1NY.js +1 -0
  233. package/dist/client/assets/ruby-Dw2BHqvy.js +1 -0
  234. package/dist/client/assets/rust-B1yitclQ.js +1 -0
  235. package/dist/client/assets/sas-cz2c8ADy.js +1 -0
  236. package/dist/client/assets/sass-Cj5Yp3dK.js +1 -0
  237. package/dist/client/assets/scala-C151Ov-r.js +1 -0
  238. package/dist/client/assets/scheme-C98Dy4si.js +1 -0
  239. package/dist/client/assets/scss-OYdSNvt2.js +1 -0
  240. package/dist/client/assets/sdbl-DVxCFoDh.js +1 -0
  241. package/dist/client/assets/shaderlab-Dg9Lc6iA.js +1 -0
  242. package/dist/client/assets/shellscript-Yzrsuije.js +1 -0
  243. package/dist/client/assets/shellsession-BADoaaVG.js +1 -0
  244. package/dist/client/assets/slack-dark-BthQWCQV.js +1 -0
  245. package/dist/client/assets/slack-ochin-DqwNpetd.js +1 -0
  246. package/dist/client/assets/smalltalk-BERRCDM3.js +1 -0
  247. package/dist/client/assets/snazzy-light-Bw305WKR.js +1 -0
  248. package/dist/client/assets/solarized-dark-DXbdFlpD.js +1 -0
  249. package/dist/client/assets/solarized-light-L9t79GZl.js +1 -0
  250. package/dist/client/assets/solidity-rGO070M0.js +1 -0
  251. package/dist/client/assets/soy-Brmx7dQM.js +1 -0
  252. package/dist/client/assets/sparql-rVzFXLq3.js +1 -0
  253. package/dist/client/assets/splunk-BtCnVYZw.js +1 -0
  254. package/dist/client/assets/sql-BLtJtn59.js +1 -0
  255. package/dist/client/assets/ssh-config-_ykCGR6B.js +1 -0
  256. package/dist/client/assets/stata-BH5u7GGu.js +1 -0
  257. package/dist/client/assets/stylus-BEDo0Tqx.js +1 -0
  258. package/dist/client/assets/surrealql-Bq5Q-fJD.js +1 -0
  259. package/dist/client/assets/svelte-C_ipcX3V.js +1 -0
  260. package/dist/client/assets/swift-D82vCrfD.js +1 -0
  261. package/dist/client/assets/synthwave-84-CbfX1IO0.js +1 -0
  262. package/dist/client/assets/system-verilog-CnnmHF94.js +1 -0
  263. package/dist/client/assets/systemd-4A_iFExJ.js +1 -0
  264. package/dist/client/assets/talonscript-CkByrt1z.js +1 -0
  265. package/dist/client/assets/tasl-QIJgUcNo.js +1 -0
  266. package/dist/client/assets/tcl-dwOrl1Do.js +1 -0
  267. package/dist/client/assets/templ-P3uqSqPl.js +1 -0
  268. package/dist/client/assets/terraform-BETggiCN.js +1 -0
  269. package/dist/client/assets/tex-idrVyKtj.js +1 -0
  270. package/dist/client/assets/tokyo-night-hegEt444.js +1 -0
  271. package/dist/client/assets/toml-vGWfd6FD.js +1 -0
  272. package/dist/client/assets/ts-tags-zn1MmPIZ.js +1 -0
  273. package/dist/client/assets/tsv-B_m7g4N7.js +1 -0
  274. package/dist/client/assets/tsx-COt5Ahok.js +1 -0
  275. package/dist/client/assets/turtle-BsS91CYL.js +1 -0
  276. package/dist/client/assets/twig-DNn4PbVi.js +1 -0
  277. package/dist/client/assets/typescript-BPQ3VLAy.js +1 -0
  278. package/dist/client/assets/typespec-BGHnOYBU.js +1 -0
  279. package/dist/client/assets/typst-DHCkPAjA.js +1 -0
  280. package/dist/client/assets/v-BcVCzyr7.js +1 -0
  281. package/dist/client/assets/vala-CsfeWuGM.js +1 -0
  282. package/dist/client/assets/vb-D17OF-Vu.js +1 -0
  283. package/dist/client/assets/verilog-BQ8w6xss.js +1 -0
  284. package/dist/client/assets/vesper-DU1UobuO.js +1 -0
  285. package/dist/client/assets/vhdl-CeAyd5Ju.js +1 -0
  286. package/dist/client/assets/viml-CJc9bBzg.js +1 -0
  287. package/dist/client/assets/vitesse-black-Bkuqu6BP.js +1 -0
  288. package/dist/client/assets/vitesse-dark-D0r3Knsf.js +1 -0
  289. package/dist/client/assets/vitesse-light-CVO1_9PV.js +1 -0
  290. package/dist/client/assets/vue-DN_0RTcg.js +1 -0
  291. package/dist/client/assets/vue-html-AaS7Mt5G.js +1 -0
  292. package/dist/client/assets/vue-vine-CQOfvN7w.js +1 -0
  293. package/dist/client/assets/vyper-CDx5xZoG.js +1 -0
  294. package/dist/client/assets/wasm-CG6Dc4jp.js +1 -0
  295. package/dist/client/assets/wasm-MzD3tlZU.js +1 -0
  296. package/dist/client/assets/wenyan-BV7otONQ.js +1 -0
  297. package/dist/client/assets/wgsl-Dx-B1_4e.js +1 -0
  298. package/dist/client/assets/wikitext-BhOHFoWU.js +1 -0
  299. package/dist/client/assets/wit-5i3qLPDT.js +1 -0
  300. package/dist/client/assets/wolfram-lXgVvXCa.js +1 -0
  301. package/dist/client/assets/xml-sdJ4AIDG.js +1 -0
  302. package/dist/client/assets/xsl-CtQFsRM5.js +1 -0
  303. package/dist/client/assets/yaml-Buea-lGh.js +1 -0
  304. package/dist/client/assets/zenscript-DVFEvuxE.js +1 -0
  305. package/dist/client/assets/zig-VOosw3JB.js +1 -0
  306. package/dist/client/index.html +2 -2
  307. package/package.json +2 -1
  308. package/src/server/agent.test.ts +45 -1
  309. package/src/server/agent.ts +141 -8
  310. package/src/server/codex-app-server-protocol.ts +28 -0
  311. package/src/server/codex-app-server.test.ts +88 -0
  312. package/src/server/codex-app-server.ts +71 -1
  313. package/src/server/diff-store.test.ts +126 -0
  314. package/src/server/diff-store.ts +365 -0
  315. package/src/server/generate-commit-message.test.ts +59 -0
  316. package/src/server/generate-commit-message.ts +121 -0
  317. package/src/server/read-models.test.ts +42 -1
  318. package/src/server/read-models.ts +10 -3
  319. package/src/server/server.ts +60 -1
  320. package/src/server/terminal-manager.test.ts +37 -4
  321. package/src/server/uploads.ts +4 -0
  322. package/src/server/ws-router.ts +122 -7
  323. package/src/shared/protocol.ts +3 -0
  324. package/src/shared/types.ts +50 -0
  325. package/dist/client/assets/index-Cdjl9mRd.js +0 -606
  326. package/dist/client/assets/index-D6aACRxb.css +0 -32
@@ -54,7 +54,7 @@ describe("read models", () => {
54
54
  lastTurnOutcome: null,
55
55
  })
56
56
 
57
- const chat = deriveChatSnapshot(state, new Map(), new Set(), "chat-1", () => [])
57
+ const chat = deriveChatSnapshot(state, new Map(), new Set(), "chat-1", () => [], () => ({ status: "unknown", files: [] }))
58
58
  expect(chat?.runtime.provider).toBe("claude")
59
59
  expect(chat?.availableProviders.length).toBeGreaterThan(1)
60
60
  expect(chat?.availableProviders.find((provider) => provider.id === "codex")?.models.map((model) => model.id)).toEqual([
@@ -106,4 +106,45 @@ describe("read models", () => {
106
106
  },
107
107
  ])
108
108
  })
109
+
110
+ test("orders sidebar chats by user-visible activity instead of internal updatedAt churn", () => {
111
+ const state = createEmptyState()
112
+ state.projectsById.set("project-1", {
113
+ id: "project-1",
114
+ localPath: "/tmp/project",
115
+ title: "Project",
116
+ createdAt: 1,
117
+ updatedAt: 1,
118
+ })
119
+ state.projectIdsByPath.set("/tmp/project", "project-1")
120
+ state.chatsById.set("chat-old", {
121
+ id: "chat-old",
122
+ projectId: "project-1",
123
+ title: "Older user activity",
124
+ createdAt: 10,
125
+ updatedAt: 500,
126
+ unread: false,
127
+ provider: "claude",
128
+ planMode: false,
129
+ sessionToken: null,
130
+ lastMessageAt: 100,
131
+ lastTurnOutcome: null,
132
+ })
133
+ state.chatsById.set("chat-new", {
134
+ id: "chat-new",
135
+ projectId: "project-1",
136
+ title: "Newer user activity",
137
+ createdAt: 20,
138
+ updatedAt: 50,
139
+ unread: false,
140
+ provider: "claude",
141
+ planMode: false,
142
+ sessionToken: null,
143
+ lastMessageAt: 200,
144
+ lastTurnOutcome: null,
145
+ })
146
+
147
+ const sidebar = deriveSidebarData(state, new Map())
148
+ expect(sidebar.projectGroups[0]?.chats.map((chat) => chat.chatId)).toEqual(["chat-new", "chat-old"])
149
+ })
109
150
  })
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ ChatDiffSnapshot,
2
3
  ChatRuntime,
3
4
  ChatSnapshot,
4
5
  KannaStatus,
@@ -17,6 +18,10 @@ export function deriveStatus(chat: ChatRecord, activeStatus?: KannaStatus): Kann
17
18
  return "idle"
18
19
  }
19
20
 
21
+ function getSidebarChatSortTimestamp(chat: ChatRecord) {
22
+ return chat.lastMessageAt ?? chat.createdAt
23
+ }
24
+
20
25
  export function deriveSidebarData(
21
26
  state: StoreState,
22
27
  activeStatuses: Map<string, KannaStatus>
@@ -28,7 +33,7 @@ export function deriveSidebarData(
28
33
  const projectGroups: SidebarProjectGroup[] = projects.map((project) => {
29
34
  const chats: SidebarChatRow[] = [...state.chatsById.values()]
30
35
  .filter((chat) => chat.projectId === project.id && !chat.deletedAt)
31
- .sort((a, b) => (b.lastMessageAt ?? b.updatedAt) - (a.lastMessageAt ?? a.updatedAt))
36
+ .sort((a, b) => getSidebarChatSortTimestamp(b) - getSidebarChatSortTimestamp(a))
32
37
  .map((chat) => ({
33
38
  _id: chat.id,
34
39
  _creationTime: chat.createdAt,
@@ -73,7 +78,7 @@ export function deriveLocalProjectsSnapshot(
73
78
  for (const project of [...state.projectsById.values()].filter((entry) => !entry.deletedAt)) {
74
79
  const chats = [...state.chatsById.values()].filter((chat) => chat.projectId === project.id && !chat.deletedAt)
75
80
  const lastOpenedAt = chats.reduce(
76
- (latest, chat) => Math.max(latest, chat.lastMessageAt ?? chat.updatedAt ?? 0),
81
+ (latest, chat) => Math.max(latest, getSidebarChatSortTimestamp(chat)),
77
82
  project.updatedAt
78
83
  )
79
84
 
@@ -100,7 +105,8 @@ export function deriveChatSnapshot(
100
105
  activeStatuses: Map<string, KannaStatus>,
101
106
  drainingChatIds: Set<string>,
102
107
  chatId: string,
103
- getMessages: (chatId: string) => ChatSnapshot["messages"]
108
+ getMessages: (chatId: string) => ChatSnapshot["messages"],
109
+ getDiffs: (chatId: string) => ChatDiffSnapshot
104
110
  ): ChatSnapshot | null {
105
111
  const chat = state.chatsById.get(chatId)
106
112
  if (!chat || chat.deletedAt) return null
@@ -122,6 +128,7 @@ export function deriveChatSnapshot(
122
128
  return {
123
129
  runtime,
124
130
  messages: getMessages(chat.id),
131
+ diffs: getDiffs(chat.id),
125
132
  availableProviders: [...SERVER_PROVIDERS],
126
133
  }
127
134
  }
@@ -4,6 +4,7 @@ import { APP_NAME, getRuntimeProfile } from "../shared/branding"
4
4
  import type { ChatAttachment } from "../shared/types"
5
5
  import { EventStore } from "./event-store"
6
6
  import { AgentCoordinator } from "./agent"
7
+ import { DiffStore } from "./diff-store"
7
8
  import { discoverProjects, type DiscoveredProject } from "./discovery"
8
9
  import { KeybindingsManager } from "./keybindings"
9
10
  import { getMachineDisplayName } from "./machine-name"
@@ -11,7 +12,7 @@ import { TerminalManager } from "./terminal-manager"
11
12
  import { UpdateManager } from "./update-manager"
12
13
  import type { UpdateInstallAttemptResult } from "./cli-runtime"
13
14
  import { createWsRouter, type ClientState } from "./ws-router"
14
- import { deleteProjectUpload, inferAttachmentContentType, persistProjectUpload } from "./uploads"
15
+ import { deleteProjectUpload, inferAttachmentContentType, inferProjectFileContentType, persistProjectUpload } from "./uploads"
15
16
  import { getProjectUploadDir } from "./paths"
16
17
 
17
18
  const MAX_UPLOAD_FILES = 10
@@ -68,8 +69,10 @@ export async function startKannaServer(options: StartKannaServerOptions = {}) {
68
69
  const hostname = options.host ?? "127.0.0.1"
69
70
  const strictPort = options.strictPort ?? false
70
71
  const store = new EventStore()
72
+ const diffStore = new DiffStore(store.dataDir)
71
73
  const machineDisplayName = getMachineDisplayName()
72
74
  await store.initialize()
75
+ await diffStore.initialize()
73
76
  await store.migrateLegacyTranscripts(options.onMigrationProgress)
74
77
  let discoveredProjects: DiscoveredProject[] = []
75
78
 
@@ -101,6 +104,7 @@ export async function startKannaServer(options: StartKannaServerOptions = {}) {
101
104
  })
102
105
  router = createWsRouter({
103
106
  store,
107
+ diffStore,
104
108
  agent,
105
109
  terminals,
106
110
  keybindings,
@@ -127,6 +131,7 @@ export async function startKannaServer(options: StartKannaServerOptions = {}) {
127
131
  const upgraded = serverInstance.upgrade(req, {
128
132
  data: {
129
133
  subscriptions: new Map(),
134
+ snapshotSignatures: new Map(),
130
135
  },
131
136
  })
132
137
  return upgraded ? undefined : new Response("WebSocket upgrade failed", { status: 400 })
@@ -151,6 +156,11 @@ export async function startKannaServer(options: StartKannaServerOptions = {}) {
151
156
  return attachmentContentResponse
152
157
  }
153
158
 
159
+ const projectFileContentResponse = await handleProjectFileContent(req, url, store)
160
+ if (projectFileContentResponse) {
161
+ return projectFileContentResponse
162
+ }
163
+
154
164
  return serveStatic(distDir, url.pathname)
155
165
  },
156
166
  websocket: {
@@ -191,6 +201,7 @@ export async function startKannaServer(options: StartKannaServerOptions = {}) {
191
201
  return {
192
202
  port: actualPort,
193
203
  store,
204
+ diffStore,
194
205
  updateManager,
195
206
  stop: shutdown,
196
207
  }
@@ -289,6 +300,54 @@ async function handleAttachmentContent(req: Request, url: URL, store: EventStore
289
300
  })
290
301
  }
291
302
 
303
+ async function handleProjectFileContent(req: Request, url: URL, store: EventStore) {
304
+ const match = url.pathname.match(/^\/api\/projects\/([^/]+)\/files\/([^/]+)\/content$/)
305
+ if (!match) {
306
+ return null
307
+ }
308
+
309
+ if (req.method !== "GET") {
310
+ return new Response(null, {
311
+ status: 405,
312
+ headers: {
313
+ Allow: "GET",
314
+ },
315
+ })
316
+ }
317
+
318
+ const project = store.getProject(match[1])
319
+ if (!project) {
320
+ return Response.json({ error: "Project not found" }, { status: 404 })
321
+ }
322
+
323
+ const relativePath = path.posix.normalize(decodeURIComponent(match[2]).replaceAll("\\", "/"))
324
+ if (!relativePath || relativePath === "." || relativePath.startsWith("../") || relativePath.includes("/../") || path.posix.isAbsolute(relativePath)) {
325
+ return Response.json({ error: "Invalid project file path" }, { status: 400 })
326
+ }
327
+
328
+ const filePath = path.resolve(project.localPath, relativePath)
329
+ const projectRoot = path.resolve(project.localPath)
330
+ if (filePath !== projectRoot && !filePath.startsWith(`${projectRoot}${path.sep}`)) {
331
+ return Response.json({ error: "Invalid project file path" }, { status: 400 })
332
+ }
333
+
334
+ const file = Bun.file(filePath)
335
+ try {
336
+ const info = await stat(filePath)
337
+ if (!info.isFile()) {
338
+ return Response.json({ error: "File not found" }, { status: 404 })
339
+ }
340
+ } catch {
341
+ return Response.json({ error: "File not found" }, { status: 404 })
342
+ }
343
+
344
+ return new Response(file, {
345
+ headers: {
346
+ "Content-Type": inferProjectFileContentType(relativePath, file.type),
347
+ },
348
+ })
349
+ }
350
+
292
351
  async function handleProjectUploadDelete(req: Request, url: URL, store: EventStore) {
293
352
  if (req.method !== "DELETE") {
294
353
  return null
@@ -1,5 +1,5 @@
1
- import { afterEach, beforeAll, describe, expect, test } from "bun:test"
2
- import { mkdtemp, rm } from "node:fs/promises"
1
+ import { afterAll, afterEach, beforeAll, describe, expect, test } from "bun:test"
2
+ import { mkdtemp, mkdir, rm } from "node:fs/promises"
3
3
  import os from "node:os"
4
4
  import path from "node:path"
5
5
  import { TerminalManager } from "./terminal-manager"
@@ -13,10 +13,19 @@ const isSupportedPlatform = process.platform !== "win32" && typeof Bun.Terminal
13
13
  const describeIfSupported = isSupportedPlatform ? describe : describe.skip
14
14
 
15
15
  let tempProjectPath = ""
16
+ let tempHomePath = ""
17
+ const originalHome = process.env.HOME
18
+ const originalZdotdir = process.env.ZDOTDIR
19
+ const originalHistfile = process.env.HISTFILE
16
20
 
17
21
  beforeAll(async () => {
18
22
  if (!isSupportedPlatform) return
19
23
  tempProjectPath = await mkdtemp(path.join(os.tmpdir(), "kanna-terminal-manager-"))
24
+ tempHomePath = await mkdtemp(path.join(os.tmpdir(), "kanna-terminal-home-"))
25
+ await mkdir(path.join(tempHomePath, ".config"), { recursive: true })
26
+ process.env.HOME = tempHomePath
27
+ process.env.ZDOTDIR = tempHomePath
28
+ process.env.HISTFILE = path.join(tempHomePath, ".zsh_history")
20
29
  })
21
30
 
22
31
  afterEach(async () => {
@@ -25,6 +34,30 @@ afterEach(async () => {
25
34
  tempProjectPath = await mkdtemp(path.join(os.tmpdir(), "kanna-terminal-manager-"))
26
35
  })
27
36
 
37
+ afterAll(async () => {
38
+ if (originalHome === undefined) {
39
+ delete process.env.HOME
40
+ } else {
41
+ process.env.HOME = originalHome
42
+ }
43
+
44
+ if (originalZdotdir === undefined) {
45
+ delete process.env.ZDOTDIR
46
+ } else {
47
+ process.env.ZDOTDIR = originalZdotdir
48
+ }
49
+
50
+ if (originalHistfile === undefined) {
51
+ delete process.env.HISTFILE
52
+ } else {
53
+ process.env.HISTFILE = originalHistfile
54
+ }
55
+
56
+ if (tempHomePath) {
57
+ await rm(tempHomePath, { recursive: true, force: true })
58
+ }
59
+ })
60
+
28
61
  async function waitFor(check: () => boolean, timeoutMs: number, intervalMs = 25) {
29
62
  const startedAt = Date.now()
30
63
  while (Date.now() - startedAt < timeoutMs) {
@@ -70,8 +103,8 @@ describeIfSupported("TerminalManager", () => {
70
103
  const { manager, getOutput } = await createSession(terminalId)
71
104
 
72
105
  try {
73
- manager.write(terminalId, 'python3 -c "import time; time.sleep(30)"\r')
74
- await waitFor(() => getOutput().includes("time.sleep(30)"), COMMAND_TIMEOUT_MS)
106
+ manager.write(terminalId, `python3 -c "import time; print('__KANNA_SLEEP__', flush=True); time.sleep(30)"\r`)
107
+ await waitFor(() => getOutput().includes("__KANNA_SLEEP__"), COMMAND_TIMEOUT_MS)
75
108
 
76
109
  manager.write(terminalId, "\x03")
77
110
  manager.write(terminalId, "printf '__KANNA_AFTER_INT__\\n'\r")
@@ -108,6 +108,10 @@ export function inferAttachmentContentType(fileName: string, fallbackType?: stri
108
108
  return fallbackType || DEFAULT_BINARY_MIME_TYPE
109
109
  }
110
110
 
111
+ export function inferProjectFileContentType(fileName: string, fallbackType?: string): string {
112
+ return inferAttachmentContentType(fileName, fallbackType)
113
+ }
114
+
111
115
  export async function deleteProjectUpload(args: {
112
116
  localPath: string
113
117
  storedName: string
@@ -4,6 +4,7 @@ import type { ClientEnvelope, ServerEnvelope, SubscriptionTopic } from "../share
4
4
  import { isClientEnvelope } from "../shared/protocol"
5
5
  import type { AgentCoordinator } from "./agent"
6
6
  import type { DiscoveredProject } from "./discovery"
7
+ import { DiffStore } from "./diff-store"
7
8
  import { EventStore } from "./event-store"
8
9
  import { openExternal } from "./external-open"
9
10
  import { KeybindingsManager } from "./keybindings"
@@ -14,10 +15,12 @@ import { deriveChatSnapshot, deriveLocalProjectsSnapshot, deriveSidebarData } fr
14
15
 
15
16
  export interface ClientState {
16
17
  subscriptions: Map<string, SubscriptionTopic>
18
+ snapshotSignatures: Map<string, string>
17
19
  }
18
20
 
19
21
  interface CreateWsRouterArgs {
20
22
  store: EventStore
23
+ diffStore?: Pick<DiffStore, "getSnapshot" | "refreshSnapshot" | "generateCommitMessage" | "commitFiles">
21
24
  agent: AgentCoordinator
22
25
  terminals: TerminalManager
23
26
  keybindings: KeybindingsManager
@@ -31,8 +34,17 @@ function send(ws: ServerWebSocket<ClientState>, message: ServerEnvelope) {
31
34
  ws.send(JSON.stringify(message))
32
35
  }
33
36
 
37
+ function ensureSnapshotSignatures(ws: ServerWebSocket<ClientState>) {
38
+ if (!ws.data.snapshotSignatures) {
39
+ ws.data.snapshotSignatures = new Map()
40
+ }
41
+
42
+ return ws.data.snapshotSignatures
43
+ }
44
+
34
45
  export function createWsRouter({
35
46
  store,
47
+ diffStore,
36
48
  agent,
37
49
  terminals,
38
50
  keybindings,
@@ -42,6 +54,12 @@ export function createWsRouter({
42
54
  updateManager,
43
55
  }: CreateWsRouterArgs) {
44
56
  const sockets = new Set<ServerWebSocket<ClientState>>()
57
+ const resolvedDiffStore = diffStore ?? {
58
+ getSnapshot: () => ({ status: "unknown", branchName: undefined, files: [] as const }),
59
+ refreshSnapshot: async () => false,
60
+ generateCommitMessage: async () => ({ subject: "Update selected files", body: "", usedFallback: true, failureMessage: null }),
61
+ commitFiles: async () => false,
62
+ }
45
63
 
46
64
  function createEnvelope(id: string, topic: SubscriptionTopic): ServerEnvelope {
47
65
  if (topic.type === "sidebar") {
@@ -121,14 +139,29 @@ export function createWsRouter({
121
139
  id,
122
140
  snapshot: {
123
141
  type: "chat",
124
- data: deriveChatSnapshot(store.state, agent.getActiveStatuses(), agent.getDrainingChatIds(), topic.chatId, (chatId) => store.getMessages(chatId)),
142
+ data: deriveChatSnapshot(
143
+ store.state,
144
+ agent.getActiveStatuses(),
145
+ agent.getDrainingChatIds(),
146
+ topic.chatId,
147
+ (chatId) => store.getMessages(chatId),
148
+ (chatId) => resolvedDiffStore.getSnapshot(chatId)
149
+ ),
125
150
  },
126
151
  }
127
152
  }
128
153
 
129
154
  function pushSnapshots(ws: ServerWebSocket<ClientState>) {
155
+ const snapshotSignatures = ensureSnapshotSignatures(ws)
130
156
  for (const [id, topic] of ws.data.subscriptions.entries()) {
131
- send(ws, createEnvelope(id, topic))
157
+ const envelope = createEnvelope(id, topic)
158
+ if (envelope.type !== "snapshot") continue
159
+ const signature = JSON.stringify(envelope.snapshot)
160
+ if (snapshotSignatures.get(id) === signature) {
161
+ continue
162
+ }
163
+ snapshotSignatures.set(id, signature)
164
+ send(ws, envelope)
132
165
  }
133
166
  }
134
167
 
@@ -150,9 +183,15 @@ export function createWsRouter({
150
183
 
151
184
  function pushTerminalSnapshot(terminalId: string) {
152
185
  for (const ws of sockets) {
186
+ const snapshotSignatures = ensureSnapshotSignatures(ws)
153
187
  for (const [id, topic] of ws.data.subscriptions.entries()) {
154
188
  if (topic.type !== "terminal" || topic.terminalId !== terminalId) continue
155
- send(ws, createEnvelope(id, topic))
189
+ const envelope = createEnvelope(id, topic)
190
+ if (envelope.type !== "snapshot") continue
191
+ const signature = JSON.stringify(envelope.snapshot)
192
+ if (snapshotSignatures.get(id) === signature) continue
193
+ snapshotSignatures.set(id, signature)
194
+ send(ws, envelope)
156
195
  }
157
196
  }
158
197
  }
@@ -177,18 +216,30 @@ export function createWsRouter({
177
216
 
178
217
  const disposeKeybindingEvents = keybindings.onChange(() => {
179
218
  for (const ws of sockets) {
219
+ const snapshotSignatures = ensureSnapshotSignatures(ws)
180
220
  for (const [id, topic] of ws.data.subscriptions.entries()) {
181
221
  if (topic.type !== "keybindings") continue
182
- send(ws, createEnvelope(id, topic))
222
+ const envelope = createEnvelope(id, topic)
223
+ if (envelope.type !== "snapshot") continue
224
+ const signature = JSON.stringify(envelope.snapshot)
225
+ if (snapshotSignatures.get(id) === signature) continue
226
+ snapshotSignatures.set(id, signature)
227
+ send(ws, envelope)
183
228
  }
184
229
  }
185
230
  })
186
231
 
187
232
  const disposeUpdateEvents = updateManager?.onChange(() => {
188
233
  for (const ws of sockets) {
234
+ const snapshotSignatures = ensureSnapshotSignatures(ws)
189
235
  for (const [id, topic] of ws.data.subscriptions.entries()) {
190
236
  if (topic.type !== "update") continue
191
- send(ws, createEnvelope(id, topic))
237
+ const envelope = createEnvelope(id, topic)
238
+ if (envelope.type !== "snapshot") continue
239
+ const signature = JSON.stringify(envelope.snapshot)
240
+ if (snapshotSignatures.get(id) === signature) continue
241
+ snapshotSignatures.set(id, signature)
242
+ send(ws, envelope)
192
243
  }
193
244
  }
194
245
  }) ?? (() => {})
@@ -299,6 +350,60 @@ export function createWsRouter({
299
350
  send(ws, { v: PROTOCOL_VERSION, type: "ack", id, result })
300
351
  break
301
352
  }
353
+ case "chat.refreshDiffs": {
354
+ const chat = store.getChat(command.chatId)
355
+ if (!chat) {
356
+ throw new Error("Chat not found")
357
+ }
358
+ const project = store.getProject(chat.projectId)
359
+ if (!project) {
360
+ throw new Error("Project not found")
361
+ }
362
+ const changed = await resolvedDiffStore.refreshSnapshot(command.chatId, project.localPath)
363
+ send(ws, { v: PROTOCOL_VERSION, type: "ack", id })
364
+ if (changed) {
365
+ broadcastSnapshots()
366
+ }
367
+ return
368
+ }
369
+ case "chat.generateCommitMessage": {
370
+ const chat = store.getChat(command.chatId)
371
+ if (!chat) {
372
+ throw new Error("Chat not found")
373
+ }
374
+ const project = store.getProject(chat.projectId)
375
+ if (!project) {
376
+ throw new Error("Project not found")
377
+ }
378
+ const result = await resolvedDiffStore.generateCommitMessage({
379
+ projectPath: project.localPath,
380
+ paths: command.paths,
381
+ })
382
+ send(ws, { v: PROTOCOL_VERSION, type: "ack", id, result })
383
+ return
384
+ }
385
+ case "chat.commitDiffs": {
386
+ const chat = store.getChat(command.chatId)
387
+ if (!chat) {
388
+ throw new Error("Chat not found")
389
+ }
390
+ const project = store.getProject(chat.projectId)
391
+ if (!project) {
392
+ throw new Error("Project not found")
393
+ }
394
+ const changed = await resolvedDiffStore.commitFiles({
395
+ chatId: command.chatId,
396
+ projectPath: project.localPath,
397
+ paths: command.paths,
398
+ summary: command.summary,
399
+ description: command.description,
400
+ })
401
+ send(ws, { v: PROTOCOL_VERSION, type: "ack", id })
402
+ if (changed) {
403
+ broadcastSnapshots()
404
+ }
405
+ return
406
+ }
302
407
  case "chat.cancel": {
303
408
  await agent.cancel(command.chatId)
304
409
  send(ws, { v: PROTOCOL_VERSION, type: "ack", id })
@@ -350,6 +455,11 @@ export function createWsRouter({
350
455
  broadcastSnapshots()
351
456
  } catch (error) {
352
457
  const messageText = error instanceof Error ? error.message : String(error)
458
+ console.error("[ws-router] command failed", {
459
+ id,
460
+ type: command.type,
461
+ message: messageText,
462
+ })
353
463
  send(ws, { v: PROTOCOL_VERSION, type: "error", id, message: messageText })
354
464
  }
355
465
  }
@@ -377,20 +487,25 @@ export function createWsRouter({
377
487
  }
378
488
 
379
489
  if (parsed.type === "subscribe") {
490
+ const snapshotSignatures = ensureSnapshotSignatures(ws)
380
491
  ws.data.subscriptions.set(parsed.id, parsed.topic)
492
+ snapshotSignatures.delete(parsed.id)
381
493
  if (parsed.topic.type === "local-projects") {
382
494
  void refreshDiscovery().then(() => {
383
495
  if (ws.data.subscriptions.has(parsed.id)) {
384
- send(ws, createEnvelope(parsed.id, parsed.topic))
496
+ pushSnapshots(ws)
385
497
  }
386
498
  })
499
+ return
387
500
  }
388
- send(ws, createEnvelope(parsed.id, parsed.topic))
501
+ pushSnapshots(ws)
389
502
  return
390
503
  }
391
504
 
392
505
  if (parsed.type === "unsubscribe") {
506
+ const snapshotSignatures = ensureSnapshotSignatures(ws)
393
507
  ws.data.subscriptions.delete(parsed.id)
508
+ snapshotSignatures.delete(parsed.id)
394
509
  send(ws, { v: PROTOCOL_VERSION, type: "ack", id: parsed.id })
395
510
  return
396
511
  }
@@ -75,6 +75,9 @@ export type ClientCommand =
75
75
  effort?: string
76
76
  planMode?: boolean
77
77
  }
78
+ | { type: "chat.refreshDiffs"; chatId: string }
79
+ | { type: "chat.generateCommitMessage"; chatId: string; paths: string[] }
80
+ | { type: "chat.commitDiffs"; chatId: string; paths: string[]; summary: string; description?: string }
78
81
  | { type: "chat.cancel"; chatId: string }
79
82
  | { type: "chat.stopDraining"; chatId: string }
80
83
  | { type: "chat.respondTool"; chatId: string; toolUseId: string; result: unknown }
@@ -171,6 +171,16 @@ export function resolveClaudeApiModelId(modelId: string, contextWindow?: ClaudeC
171
171
  return contextWindow === "1m" ? `${modelId}[1m]` : modelId
172
172
  }
173
173
 
174
+ export function resolveClaudeContextWindowTokens(contextWindow: ClaudeContextWindow): number {
175
+ switch (contextWindow) {
176
+ case "1m":
177
+ return 1_000_000
178
+ case "200k":
179
+ default:
180
+ return 200_000
181
+ }
182
+ }
183
+
174
184
  export type KannaStatus =
175
185
  | "idle"
176
186
  | "starting"
@@ -440,6 +450,43 @@ export interface StatusEntry extends TranscriptEntryBase {
440
450
  status: string
441
451
  }
442
452
 
453
+ export interface ContextWindowUsageSnapshot {
454
+ usedTokens: number
455
+ totalProcessedTokens?: number
456
+ maxTokens?: number
457
+ inputTokens?: number
458
+ cachedInputTokens?: number
459
+ outputTokens?: number
460
+ reasoningOutputTokens?: number
461
+ lastUsedTokens?: number
462
+ lastInputTokens?: number
463
+ lastCachedInputTokens?: number
464
+ lastOutputTokens?: number
465
+ lastReasoningOutputTokens?: number
466
+ toolUses?: number
467
+ durationMs?: number
468
+ compactsAutomatically: boolean
469
+ }
470
+
471
+ export interface ChatDiffFile {
472
+ path: string
473
+ changeType: "added" | "deleted" | "modified" | "renamed"
474
+ patch: string
475
+ mimeType?: string
476
+ size?: number
477
+ }
478
+
479
+ export interface ChatDiffSnapshot {
480
+ status: "unknown" | "ready" | "no_repo"
481
+ branchName?: string
482
+ files: ChatDiffFile[]
483
+ }
484
+
485
+ export interface ContextWindowUpdatedEntry extends TranscriptEntryBase {
486
+ kind: "context_window_updated"
487
+ usage: ContextWindowUsageSnapshot
488
+ }
489
+
443
490
  export interface CompactBoundaryEntry extends TranscriptEntryBase {
444
491
  kind: "compact_boundary"
445
492
  }
@@ -466,6 +513,7 @@ export type TranscriptEntry =
466
513
  | ToolResultEntry
467
514
  | ResultEntry
468
515
  | StatusEntry
516
+ | ContextWindowUpdatedEntry
469
517
  | CompactBoundaryEntry
470
518
  | CompactSummaryEntry
471
519
  | ContextClearedEntry
@@ -579,6 +627,7 @@ export type HydratedTranscriptMessage =
579
627
  | ({ kind: "assistant_text"; text: string; id: string; messageId?: string; timestamp: string; hidden?: boolean })
580
628
  | ({ kind: "result"; success: boolean; cancelled?: boolean; result: string; durationMs: number; costUsd?: number; id: string; messageId?: string; timestamp: string; hidden?: boolean })
581
629
  | ({ kind: "status"; status: string; id: string; messageId?: string; timestamp: string; hidden?: boolean })
630
+ | ({ kind: "context_window_updated"; usage: ContextWindowUsageSnapshot; id: string; messageId?: string; timestamp: string; hidden?: boolean })
582
631
  | ({ kind: "compact_boundary"; id: string; messageId?: string; timestamp: string; hidden?: boolean })
583
632
  | ({ kind: "compact_summary"; summary: string; id: string; messageId?: string; timestamp: string; hidden?: boolean })
584
633
  | ({ kind: "context_cleared"; id: string; messageId?: string; timestamp: string; hidden?: boolean })
@@ -601,6 +650,7 @@ export interface ChatRuntime {
601
650
  export interface ChatSnapshot {
602
651
  runtime: ChatRuntime
603
652
  messages: TranscriptEntry[]
653
+ diffs: ChatDiffSnapshot
604
654
  availableProviders: ProviderCatalogEntry[]
605
655
  }
606
656