@hienlh/ppm 0.19.1 → 0.19.2

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 (506) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/assets/skills/ppm/SKILL.md +1 -1
  3. package/assets/skills/ppm/references/cli-reference.md +4 -4
  4. package/assets/skills/ppm/references/http-api.md +1 -1
  5. package/dist/web/assets/{adaptive-context-menu-JfuUSqbe.js → adaptive-context-menu-BbuRVTLn.js} +1 -1
  6. package/dist/web/assets/{ai-resource-editor-MfdJKVM5.js → ai-resource-editor-BCCOUzox.js} +1 -1
  7. package/dist/web/assets/{ai-settings-section-CJE7jFsj.js → ai-settings-section-BEM1z7kp.js} +1 -1
  8. package/dist/web/assets/{angular-html-VzxSzTCy.js → angular-html-BlcrLjUx.js} +1 -1
  9. package/dist/web/assets/{angular-ts-CmJ4bmzM.js → angular-ts-BUjtceAJ.js} +1 -1
  10. package/dist/web/assets/{api-client-qRWu6_s5.js → api-client-BzWM7XPR.js} +1 -1
  11. package/dist/web/assets/api-client-D1CePgQ4.js +1 -0
  12. package/dist/web/assets/api-settings-CNtKkHqW.js +1 -0
  13. package/dist/web/assets/{api-settings-CPg4yGvS.js → api-settings-Cubuqjsu.js} +1 -1
  14. package/dist/web/assets/{api-themes-DsAp8m7m.js → api-themes-YsZgf6Xj.js} +1 -1
  15. package/dist/web/assets/{apl-do17bnVx.js → apl-B3g0v1pk.js} +1 -1
  16. package/dist/web/assets/{astro-ClY0e0OA.js → astro-FRnyn7cH.js} +1 -1
  17. package/dist/web/assets/{audio-preview-Fw935Mok.js → audio-preview-DWTRm4HC.js} +1 -1
  18. package/dist/web/assets/{blade-BXSdXDGq.js → blade-NLdOqUKw.js} +1 -1
  19. package/dist/web/assets/{bot-CWZAuloY.js → bot-D6sx11y0.js} +1 -1
  20. package/dist/web/assets/{bsl-MxsMKDUZ.js → bsl-CenHZfaU.js} +1 -1
  21. package/dist/web/assets/{button-CrwgGd1y.js → button-Cq8yixjf.js} +1 -1
  22. package/dist/web/assets/{cairo-4CIrzX_p.js → cairo-B_WR0aiQ.js} +1 -1
  23. package/dist/web/assets/chat-tab-CmXGsJGc.js +15 -0
  24. package/dist/web/assets/{chevron-down-D7CbB5f6.js → chevron-down-Dp0rpHb8.js} +1 -1
  25. package/dist/web/assets/chevron-right-De-Wz5Lz.js +1 -0
  26. package/dist/web/assets/circle-BCy0AUCo.js +1 -0
  27. package/dist/web/assets/{circle-question-mark-BBzFZrNh.js → circle-question-mark-CzR7YRc-.js} +1 -1
  28. package/dist/web/assets/clock-C1CQUQPZ.js +1 -0
  29. package/dist/web/assets/{cobol-phPOE89d.js → cobol-DOynhWYj.js} +1 -1
  30. package/dist/web/assets/code-DJgYrFzm.js +1 -0
  31. package/dist/web/assets/code-editor-DtHXzNZ1.js +7 -0
  32. package/dist/web/assets/{coffee-Cjuukfz3.js → coffee-Bn6dbw4P.js} +1 -1
  33. package/dist/web/assets/{conflict-editor-Ni0PPsKu.js → conflict-editor-Cgb-z12D.js} +1 -1
  34. package/dist/web/assets/{context-menu-BEAa1qhd.js → context-menu-DfWZAxQy.js} +1 -1
  35. package/dist/web/assets/{copy-CbPdNp7E.js → copy-BewHj66E.js} +1 -1
  36. package/dist/web/assets/{cpp-CT7tGBC3.js → cpp-CoZR9t8r.js} +1 -1
  37. package/dist/web/assets/{createLucideIcon-ConhwJnb.js → createLucideIcon-D8pyde1A.js} +1 -1
  38. package/dist/web/assets/{crystal-D8U1Qnqh.js → crystal-BSheONSg.js} +1 -1
  39. package/dist/web/assets/{csv-preview-Coc2oed1.js → csv-preview-BZMHfFH3.js} +1 -1
  40. package/dist/web/assets/{data-grid-overlay-editor-B8WQkZDB.js → data-grid-overlay-editor-gwPoVSCU.js} +1 -1
  41. package/dist/web/assets/{data-grid-types-KpjaKAci.js → data-grid-types-Dq_H1IPa.js} +1 -1
  42. package/dist/web/assets/{database-BqyeGogo.js → database-Y3ESDK4-.js} +1 -1
  43. package/dist/web/assets/database-viewer-GVaQVMdb.js +1 -0
  44. package/dist/web/assets/{dialog-DrL3WEXd.js → dialog-BSfuiTkJ.js} +1 -1
  45. package/dist/web/assets/{diff-viewer-CT0lmDc9.js → diff-viewer-BG5_fH1E.js} +1 -1
  46. package/dist/web/assets/{dist-C2_dKAa_.js → dist-aDSxTR_v.js} +1 -1
  47. package/dist/web/assets/{docx-preview-76KDw1eE.js → docx-preview-DqVDOMmv.js} +1 -1
  48. package/dist/web/assets/{download-DAF7SXcf.js → download-DG3_9xXy.js} +1 -1
  49. package/dist/web/assets/{edge-Db4ze6bw.js → edge-DTtoslQu.js} +1 -1
  50. package/dist/web/assets/{edit-diff-preview-DyC_-4My.js → edit-diff-preview-CXSbSUBX.js} +1 -1
  51. package/dist/web/assets/{elixir-RZPZ0472.js → elixir-CIBCZ3Zi.js} +1 -1
  52. package/dist/web/assets/{elm-DNZcQt_7.js → elm-BLmb7K1H.js} +1 -1
  53. package/dist/web/assets/{erb-DRONsZXs.js → erb-BBY8ApRd.js} +1 -1
  54. package/dist/web/assets/{erlang-DeG0-QwJ.js → erlang-D5Zb5_P0.js} +1 -1
  55. package/dist/web/assets/{esm-CZy-sSoK.js → esm-6F8v1jLL.js} +1 -1
  56. package/dist/web/assets/explorer-body-BPFnjxYj.js +2 -0
  57. package/dist/web/assets/explorer-window-content-Da1T9uM4.js +1 -0
  58. package/dist/web/assets/{extension-store-ClQpnxQr.js → extension-store-Bhu--fPX.js} +1 -1
  59. package/dist/web/assets/{extension-webview-BW5GuBnH.js → extension-webview-BSh6KGd7.js} +1 -1
  60. package/dist/web/assets/{external-link-ojDHcQxQ.js → external-link-B6Hl0U4X.js} +1 -1
  61. package/dist/web/assets/{eye-off-Rq1n2-oe.js → eye-off-CUdzgmX8.js} +1 -1
  62. package/dist/web/assets/{file-exclamation-point-Bpg-zDPP.js → file-exclamation-point-JzhTU2TX.js} +1 -1
  63. package/dist/web/assets/{file-store-28e44KP3.js → file-store-6HW2Lfl4.js} +1 -1
  64. package/dist/web/assets/{folder-symlink-LD-mr54V.js → folder-symlink-Dx1hOk8X.js} +1 -1
  65. package/dist/web/assets/{fortran-fixed-form-CK5VRJNd.js → fortran-fixed-form-CBdwkEUJ.js} +1 -1
  66. package/dist/web/assets/{fsharp-TVVgzHWp.js → fsharp-AT4XdVYH.js} +1 -1
  67. package/dist/web/assets/{gdresource-DiUcyewI.js → gdresource-D852fWBR.js} +1 -1
  68. package/dist/web/assets/{git-commit-CfOruFrg.js → git-commit-DWfWAyxy.js} +1 -1
  69. package/dist/web/assets/{git-log-panel-D1-YXZ0O.js → git-log-panel-DNC9fV0M.js} +1 -1
  70. package/dist/web/assets/{git-rebase-Bxyr24oc.js → git-rebase-C6opk8gr.js} +1 -1
  71. package/dist/web/assets/glide-data-grid-CICFjJon.js +141 -0
  72. package/dist/web/assets/{glimmer-js-CT5aJxz_.js → glimmer-js-DLi5mWYO.js} +1 -1
  73. package/dist/web/assets/{glimmer-ts-CRjP9Yey.js → glimmer-ts-D-scFX30.js} +1 -1
  74. package/dist/web/assets/{glsl-BX2--oI_.js → glsl-dbQzF4iA.js} +1 -1
  75. package/dist/web/assets/{graphql-BIQsvBxQ.js → graphql-D1m5Wen2.js} +1 -1
  76. package/dist/web/assets/group-chat-tab-DbUkcOaJ.js +1 -0
  77. package/dist/web/assets/{hack-CTWk0k1m.js → hack-CqYmpO7u.js} +1 -1
  78. package/dist/web/assets/{haml-DGh27hXc.js → haml-BXzKWEag.js} +1 -1
  79. package/dist/web/assets/{hand-DKSxDa5c.js → hand-x0ejq3fS.js} +1 -1
  80. package/dist/web/assets/{handlebars-BJL1jNrG.js → handlebars-DXnrtHtq.js} +1 -1
  81. package/dist/web/assets/{html-DALyDNqU.js → html-DhgQJ-xL.js} +1 -1
  82. package/dist/web/assets/{html-derivative-B4XcU57e.js → html-derivative-xZRft-YE.js} +1 -1
  83. package/dist/web/assets/{http-DfMo7nNu.js → http-B53mSj14.js} +1 -1
  84. package/dist/web/assets/{hurl-CGc_n2Qf.js → hurl-CsomVy04.js} +1 -1
  85. package/dist/web/assets/{hxml-40zZ1t4x.js → hxml-LffjWwKA.js} +1 -1
  86. package/dist/web/assets/{image-preview-Cp3XB8UO.js → image-preview-CNA6XMNS.js} +1 -1
  87. package/dist/web/assets/index-DTqbMiVc.js +52 -0
  88. package/dist/web/assets/{input-DTmI80l2.js → input-D0ol5jW0.js} +1 -1
  89. package/dist/web/assets/{jinja-D5zuIn9u.js → jinja-CCHURl7V.js} +1 -1
  90. package/dist/web/assets/{jison-Dxut9QwA.js → jison-7qBd8dnR.js} +1 -1
  91. package/dist/web/assets/{julia-Cu85tTPo.js → julia-BY9pnTcy.js} +1 -1
  92. package/dist/web/assets/{just-CctM4uLa.js → just-D7wRSNRr.js} +1 -1
  93. package/dist/web/assets/{keybindings-store-Bi0q9x4Q.js → keybindings-store-BGkB72gM.js} +1 -1
  94. package/dist/web/assets/keybindings-store-Bq6VWl5X.js +1 -0
  95. package/dist/web/assets/{keyboard-AdwmYDSu.js → keyboard-DBBaoQSu.js} +1 -1
  96. package/dist/web/assets/{latex-kpwS_CC7.js → latex-RgsCae55.js} +1 -1
  97. package/dist/web/assets/{lib-Ca7ai8xa.js → lib-v9x_qXgI.js} +1 -1
  98. package/dist/web/assets/{liquid-B8Rr0xue.js → liquid-CnDFzvOZ.js} +1 -1
  99. package/dist/web/assets/loader-circle-DouKgIXy.js +1 -0
  100. package/dist/web/assets/{lock-BEtt_eTO.js → lock-CNxUGpTk.js} +1 -1
  101. package/dist/web/assets/{lua-C_3gixwM.js → lua-0lj5W8sm.js} +1 -1
  102. package/dist/web/assets/markdown-renderer-DEAVpo0C.js +1 -0
  103. package/dist/web/assets/markdown-renderer-dbLIvU2M.js +3 -0
  104. package/dist/web/assets/{marko-B28rvNL-.js → marko-CQgqAhbV.js} +1 -1
  105. package/dist/web/assets/{mdc-DQ6-STFO.js → mdc-BkIcTiIy.js} +1 -1
  106. package/dist/web/assets/{media-url-BjvbfTTl.js → media-url-DDciwvLU.js} +1 -1
  107. package/dist/web/assets/{mobile-bottom-sheet-DXn5g2LB.js → mobile-bottom-sheet-pjcHKorA.js} +1 -1
  108. package/dist/web/assets/mobile-explorer-sheet-CD_kl-jA.js +1 -0
  109. package/dist/web/assets/{monitor-CXI_XpWJ.js → monitor-CxPV_fQI.js} +1 -1
  110. package/dist/web/assets/{monitor-smartphone-C1wUPOSg.js → monitor-smartphone-DLZ6Q05s.js} +1 -1
  111. package/dist/web/assets/{nextflow-B1xq2O8L.js → nextflow-DvIW68DS.js} +1 -1
  112. package/dist/web/assets/{nginx-xsLxRGxO.js → nginx-O-hft0gG.js} +1 -1
  113. package/dist/web/assets/{nim-wqcek3WO.js → nim-BxrR4HwQ.js} +1 -1
  114. package/dist/web/assets/notification-store-Dgbsw9p9.js +1 -0
  115. package/dist/web/assets/{number-overlay-editor-1c4sgZJt.js → number-overlay-editor-BoLRYuZT.js} +1 -1
  116. package/dist/web/assets/{overview-panel-Cr2xsHc6.js → overview-panel-l3g1gT5e.js} +1 -1
  117. package/dist/web/assets/{panel-store-Dv28vz3L.js → panel-store-UOV8wrfR.js} +1 -1
  118. package/dist/web/assets/{pdf-preview-DKeIIJTv.js → pdf-preview-B2CJ_EKv.js} +1 -1
  119. package/dist/web/assets/{pencil-CHKLOgLs.js → pencil-BlIIg6RH.js} +1 -1
  120. package/dist/web/assets/{perl-BH54gN9q.js → perl-Da-uNqmL.js} +1 -1
  121. package/dist/web/assets/{php-Br5ZrEZm.js → php-JMu45mEx.js} +1 -1
  122. package/dist/web/assets/play-DpEEBJTT.js +1 -0
  123. package/dist/web/assets/{plus-BxqD2FIZ.js → plus-BR0qewJI.js} +1 -1
  124. package/dist/web/assets/portal-container-context-D-495m5C.js +1 -0
  125. package/dist/web/assets/{postgres-viewer-BJPr_FYL.js → postgres-viewer-DstZwUai.js} +3 -3
  126. package/dist/web/assets/{process-table-BqTcAJ-h.js → process-table-kmQ2-8qJ.js} +1 -1
  127. package/dist/web/assets/{project-store-KHjFTCxf.js → project-store-DTUCS65m.js} +1 -1
  128. package/dist/web/assets/{pug-8VCubZ3H.js → pug-CLzyQhia.js} +1 -1
  129. package/dist/web/assets/{qml-D8AF1RZ6.js → qml-BHXfGB1l.js} +1 -1
  130. package/dist/web/assets/{razor-6v042IJW.js → razor-BqetJLN3.js} +1 -1
  131. package/dist/web/assets/{react-DHs6efqI.js → react-B549sK_T.js} +1 -1
  132. package/dist/web/assets/{refresh-cw-BALzw7L3.js → refresh-cw-BQU5g2CY.js} +1 -1
  133. package/dist/web/assets/remote-desktop-mobile-sheet-KKTRNjOL.js +2 -0
  134. package/dist/web/assets/{remote-desktop-mobile-view-DaoB49k4.js → remote-desktop-mobile-view-C1ST4-Fm.js} +1 -1
  135. package/dist/web/assets/{remote-desktop-readiness-gate--LJgsuoj.js → remote-desktop-readiness-gate-BH3BE5wA.js} +1 -1
  136. package/dist/web/assets/{remote-desktop-window-content-Cihx4PNZ.js → remote-desktop-window-content-BaZJQt_N.js} +1 -1
  137. package/dist/web/assets/{rotate-ccw-CuvNBM80.js → rotate-ccw-CpjRo90J.js} +1 -1
  138. package/dist/web/assets/{rst-mTGFo4AQ.js → rst-iQdJZ0BP.js} +1 -1
  139. package/dist/web/assets/{ruby-BBaOP636.js → ruby-CNwzZGdH.js} +1 -1
  140. package/dist/web/assets/{run-in-terminal-Zl5iIx-B.js → run-in-terminal-CBXmVGWa.js} +1 -1
  141. package/dist/web/assets/{sas-D5QEkIH2.js → sas-BpBIT2eR.js} +1 -1
  142. package/dist/web/assets/{save-Dxq-Zepc.js → save-DYLiVzU8.js} +1 -1
  143. package/dist/web/assets/{scroll-area-B5yhPpZo.js → scroll-area-lc0ZHORr.js} +1 -1
  144. package/dist/web/assets/{scss-C5Mu6y2u.js → scss-D1MotuEp.js} +1 -1
  145. package/dist/web/assets/{send-Cits8q0l.js → send-C_sLFt56.js} +1 -1
  146. package/dist/web/assets/{send-to-chat-Cv7ikFUC.js → send-to-chat-DJAdlvJ8.js} +1 -1
  147. package/dist/web/assets/{settings-store-Dp4i8zeA.js → settings-store-zbPkG7o-.js} +2 -2
  148. package/dist/web/assets/settings-tab-Cn-AqUP_.js +1 -0
  149. package/dist/web/assets/{settings-tab-BtMvvKAF.js → settings-tab-xyRpt2gq.js} +1 -1
  150. package/dist/web/assets/{shaderlab-mLnpMpN3.js → shaderlab-BNqmcPQi.js} +1 -1
  151. package/dist/web/assets/{shallow-BBJ_qoEF.js → shallow-CmPMAPte.js} +1 -1
  152. package/dist/web/assets/{shellsession-qdcDfUuk.js → shellsession-BBLOVOO-.js} +1 -1
  153. package/dist/web/assets/{shield-check-CH7F1P7m.js → shield-check-CUI36aSc.js} +1 -1
  154. package/dist/web/assets/{shield-off-C9HiF0-6.js → shield-off-GdkkVhb3.js} +1 -1
  155. package/dist/web/assets/{soy-CGGf1G43.js → soy-BpTGo9uy.js} +1 -1
  156. package/dist/web/assets/{sparkles-BnuLH6Ck.js → sparkles-Dh-_J2Rd.js} +1 -1
  157. package/dist/web/assets/{sparql-tQHB50VD.js → sparql-D7A3yKob.js} +1 -1
  158. package/dist/web/assets/{sql-query-editor-DZqnYIB7.js → sql-query-editor-B86NiZHI.js} +1 -1
  159. package/dist/web/assets/{sqlite-viewer-DXutos0e.js → sqlite-viewer-DB775F6C.js} +1 -1
  160. package/dist/web/assets/{stata-DVQNLMcR.js → stata-B7N56ptS.js} +1 -1
  161. package/dist/web/assets/{surrealql-lu576tBi.js → surrealql-CTmnD-lv.js} +1 -1
  162. package/dist/web/assets/{svelte-CSCrOBjT.js → svelte-BppwY9TR.js} +1 -1
  163. package/dist/web/assets/{switch-BPol3BFC.js → switch-BmjKoIpR.js} +1 -1
  164. package/dist/web/assets/{system-monitor-body-Ct8_GX_f.js → system-monitor-body-BDxHQvy9.js} +2 -2
  165. package/dist/web/assets/system-monitor-tab-BoTHLx6B.js +1 -0
  166. package/dist/web/assets/system-monitor-window-content-B9Y98UW6.js +1 -0
  167. package/dist/web/assets/{tab-host-window-content-C5kYP0-z.js → tab-host-window-content-C1j8zr68.js} +1 -1
  168. package/dist/web/assets/{tab-store-DS3YlUwR.js → tab-store-DzYSsw5a.js} +1 -1
  169. package/dist/web/assets/{table-BXDyImqk.js → table-D5Q4ap4W.js} +1 -1
  170. package/dist/web/assets/team-member-sheet-Cxz6h5Ne.js +2 -0
  171. package/dist/web/assets/{team-member-window-content-BJ_XFw8-.js → team-member-window-content-CUSuRHCB.js} +1 -1
  172. package/dist/web/assets/{templ-CPt8AS4Q.js → templ-CvTMKKra.js} +1 -1
  173. package/dist/web/assets/{terminal-tab-8bKQ3CWR.js → terminal-tab-B9I_HlES.js} +3 -3
  174. package/dist/web/assets/{tex-BrTByT7i.js → tex--c4POEUE.js} +1 -1
  175. package/dist/web/assets/{text-wrap-CzMjB6GD.js → text-wrap-DBvQD3kc.js} +1 -1
  176. package/dist/web/assets/{tool-cards-pIu_cmEL.js → tool-cards-D3KSzd2K.js} +4 -4
  177. package/dist/web/assets/{trash-2-BjShZ7if.js → trash-2-By4VMbC4.js} +1 -1
  178. package/dist/web/assets/{triangle-alert-qQ6Plydp.js → triangle-alert-CUTURFbG.js} +1 -1
  179. package/dist/web/assets/{ts-tags-DSqEZ7A8.js → ts-tags--kx7D-Hw.js} +1 -1
  180. package/dist/web/assets/{twig-DW9qc1bf.js → twig-UG9uMHGd.js} +1 -1
  181. package/dist/web/assets/{upload-7Zs6oaK5.js → upload-BNlX8D07.js} +1 -1
  182. package/dist/web/assets/{use-blob-url-DKbV7PM_.js → use-blob-url-Bdxopwwc.js} +1 -1
  183. package/dist/web/assets/{use-coarse-long-press-DGyC48NF.js → use-coarse-long-press-BmI_GVW5.js} +1 -1
  184. package/dist/web/assets/{use-is-mobile-ClE-UcPF.js → use-is-mobile-B9UZNwSm.js} +1 -1
  185. package/dist/web/assets/{use-monaco-theme-DaxAk3gB.js → use-monaco-theme-CVCye2kq.js} +1 -1
  186. package/dist/web/assets/use-open-team-member-D9EOB8-i.js +1 -0
  187. package/dist/web/assets/{use-remote-desktop-display-choice-BUA_1eaH.js → use-remote-desktop-display-choice-neUqyE3p.js} +1 -1
  188. package/dist/web/assets/{use-visual-viewport-CCX21c9w.js → use-visual-viewport-Sfb6CZ80.js} +1 -1
  189. package/dist/web/assets/{use-websocket-CWpG7PWN.js → use-websocket-xOOwBdwP.js} +1 -1
  190. package/dist/web/assets/vendor-markdown-Ls5AcU91.js +291 -0
  191. package/dist/web/assets/{vendor-ui-CXJtBULr.js → vendor-ui-CaeeFO0B.js} +1 -1
  192. package/dist/web/assets/video-preview-DvpgBDhM.js +1 -0
  193. package/dist/web/assets/{vue-html-CG5BKtq1.js → vue-html-8KEW6-I9.js} +1 -1
  194. package/dist/web/assets/{vue-CJNDL1g-.js → vue-jjTomlbt.js} +1 -1
  195. package/dist/web/assets/{vue-vine-Dd5HZ7T5.js → vue-vine-C5tohCkU.js} +1 -1
  196. package/dist/web/assets/{window-pip-registry-9H6Y-HqR.js → window-pip-registry-FdZdGF7h.js} +1 -1
  197. package/dist/web/assets/{window-store-CNzkeUyO.js → window-store-D0x_fxr0.js} +1 -1
  198. package/dist/web/assets/x-DiOUR_mp.js +1 -0
  199. package/dist/web/assets/{xml-DTOm5niz.js → xml-ByFFosqL.js} +1 -1
  200. package/dist/web/assets/{xsl-BD_kBC04.js → xsl-CZUwiLkj.js} +1 -1
  201. package/dist/web/index.html +59 -59
  202. package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
  203. package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
  204. package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
  205. package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
  206. package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
  207. package/dist/web/sw.js +1 -1
  208. package/package.json +1 -1
  209. package/scripts/check-ppm-dir-usage.sh +0 -0
  210. package/scripts/install.sh +0 -0
  211. package/scripts/release.sh +0 -0
  212. package/src/providers/claude-agent-sdk.ts +51 -43
  213. package/src/providers/codex-app-server/codex-provider.ts +7 -1
  214. package/src/server/routes/chat.ts +28 -3
  215. package/src/server/ws/chat.ts +9 -4
  216. package/src/services/named-tunnel/named-tunnel-retry.ts +26 -0
  217. package/src/services/named-tunnel/network-ready.ts +63 -0
  218. package/src/services/supervisor.ts +98 -0
  219. package/src/services/transcript-images.ts +14 -7
  220. package/src/services/upgrade.service.ts +7 -2
  221. package/src/shared/image-limits.ts +37 -0
  222. package/src/types/api.ts +3 -1
  223. package/src/types/chat.ts +6 -0
  224. package/src/types/extension-messages.ts +2 -0
  225. package/src/web/components/chat/attachment-chips.tsx +10 -0
  226. package/src/web/components/chat/chat-tab.tsx +37 -13
  227. package/src/web/components/chat/message-input.tsx +102 -11
  228. package/src/web/components/chat/session-debug-dialog.tsx +2 -1
  229. package/src/web/components/chat/user-message-parse.ts +33 -11
  230. package/src/web/components/database/database-viewer.tsx +34 -11
  231. package/src/web/components/database/extract-query-table.ts +24 -0
  232. package/src/web/components/database/glide-data-grid.tsx +11 -4
  233. package/src/web/components/database/glide-grid-types.ts +4 -0
  234. package/src/web/components/database/split-sql-statements.ts +116 -0
  235. package/src/web/components/database/sql-completion-provider.ts +1 -26
  236. package/src/web/components/database/sql-query-editor.tsx +2 -1
  237. package/src/web/components/database/use-database.ts +16 -1
  238. package/src/web/components/database/use-glide-cell-content.ts +11 -6
  239. package/src/web/components/editor/code-editor.tsx +30 -56
  240. package/src/web/hooks/use-chat.ts +6 -3
  241. package/src/web/lib/attachment-marker-split.ts +44 -0
  242. package/src/web/lib/image-animation.ts +51 -0
  243. package/src/web/lib/image-resize-limits.ts +47 -0
  244. package/src/web/lib/image-resize.ts +133 -0
  245. package/dist/web/assets/api-client-DLWwuUkJ.js +0 -1
  246. package/dist/web/assets/api-settings-BFuaZrNC.js +0 -1
  247. package/dist/web/assets/chat-tab-bSOVoC5N.js +0 -15
  248. package/dist/web/assets/chevron-right-D7Pn5agw.js +0 -1
  249. package/dist/web/assets/circle-4g2coECH.js +0 -1
  250. package/dist/web/assets/clock-DbiNiwCG.js +0 -1
  251. package/dist/web/assets/code-DdKXqtf_.js +0 -1
  252. package/dist/web/assets/code-editor-COTa37oe.js +0 -10
  253. package/dist/web/assets/database-viewer-Dta7lSSk.js +0 -1
  254. package/dist/web/assets/explorer-body-BGjR2Gbt.js +0 -2
  255. package/dist/web/assets/explorer-window-content-DAhHh5EQ.js +0 -1
  256. package/dist/web/assets/glide-data-grid-BSsqxe2v.js +0 -138
  257. package/dist/web/assets/group-chat-tab-CMtzoaFc.js +0 -1
  258. package/dist/web/assets/index-BkHlMfPD.js +0 -52
  259. package/dist/web/assets/keybindings-store-_KfovSz7.js +0 -1
  260. package/dist/web/assets/loader-circle-DD65e9-7.js +0 -1
  261. package/dist/web/assets/markdown-renderer-DroTmKbJ.js +0 -1
  262. package/dist/web/assets/markdown-renderer-ntDi3Tx-.js +0 -3
  263. package/dist/web/assets/mobile-explorer-sheet-BP6iU4ma.js +0 -1
  264. package/dist/web/assets/notification-store-II5Kw9rD.js +0 -1
  265. package/dist/web/assets/play-CPbVzGgq.js +0 -1
  266. package/dist/web/assets/portal-container-context-BmG4--I-.js +0 -1
  267. package/dist/web/assets/remote-desktop-mobile-sheet-CYZUjYTO.js +0 -2
  268. package/dist/web/assets/settings-tab-UPPycGRF.js +0 -1
  269. package/dist/web/assets/system-monitor-tab-DC_EerdI.js +0 -1
  270. package/dist/web/assets/system-monitor-window-content-D25DMNpw.js +0 -1
  271. package/dist/web/assets/team-member-sheet-DvmioeQL.js +0 -2
  272. package/dist/web/assets/use-open-team-member-CopUuDHG.js +0 -1
  273. package/dist/web/assets/vendor-markdown-CKTlmXbz.js +0 -291
  274. package/dist/web/assets/video-preview-C2_fdunE.js +0 -1
  275. package/dist/web/assets/x-Dzu9ecti.js +0 -1
  276. /package/dist/web/assets/{abap-Cjj2olMr.js → abap-C5DFF0s3.js} +0 -0
  277. /package/dist/web/assets/{actionscript-3-DCkW7kWC.js → actionscript-3-B0qyRNbc.js} +0 -0
  278. /package/dist/web/assets/{ada-AEWvZXuX.js → ada-bAJzplug.js} +0 -0
  279. /package/dist/web/assets/{andromeeda-D7cSpME6.js → andromeeda-DUCpISy6.js} +0 -0
  280. /package/dist/web/assets/{apache-DjvX0RSe.js → apache-BhAr84Z_.js} +0 -0
  281. /package/dist/web/assets/{apex-70xIZ0pH.js → apex-CRv8YnNq.js} +0 -0
  282. /package/dist/web/assets/{applescript-C3qKqxmA.js → applescript-CD-Cw8bt.js} +0 -0
  283. /package/dist/web/assets/{ara-iGt2zixk.js → ara-BUzeQYoG.js} +0 -0
  284. /package/dist/web/assets/{asciidoc-Bs2t-06W.js → asciidoc-CT2hTg-v.js} +0 -0
  285. /package/dist/web/assets/{asm-B-T0eqhE.js → asm-CAu35JeY.js} +0 -0
  286. /package/dist/web/assets/{aurora-x-D3h_9M3E.js → aurora-x-BeFJoIA1.js} +0 -0
  287. /package/dist/web/assets/{awk-CZwfwpPi.js → awk-Vau-DrGN.js} +0 -0
  288. /package/dist/web/assets/{ayu-dark-DulkglEA.js → ayu-dark-CLe-OTsQ.js} +0 -0
  289. /package/dist/web/assets/{ayu-light-DyNQiRpn.js → ayu-light-CJbQfu4M.js} +0 -0
  290. /package/dist/web/assets/{ayu-mirage-CcEt4xkI.js → ayu-mirage-GWaKwdrx.js} +0 -0
  291. /package/dist/web/assets/{ballerina-OhQPjyHS.js → ballerina-DWu0USI5.js} +0 -0
  292. /package/dist/web/assets/{bat-DqKcJTUn.js → bat-_07Huw7E.js} +0 -0
  293. /package/dist/web/assets/{beancount-BdXlMi8T.js → beancount-EcvogrcF.js} +0 -0
  294. /package/dist/web/assets/{berry-DvgY6BkY.js → berry-BBB87SY7.js} +0 -0
  295. /package/dist/web/assets/{bibtex-BSRJq0yM.js → bibtex-DbueS87A.js} +0 -0
  296. /package/dist/web/assets/{bicep-DpAdumKt.js → bicep-tcmXnUo4.js} +0 -0
  297. /package/dist/web/assets/{bird2-rlbtp8di.js → bird2-B2qwilIR.js} +0 -0
  298. /package/dist/web/assets/{c-AjOLBXIs.js → c-Z58oivSt.js} +0 -0
  299. /package/dist/web/assets/{c3-DqIXz4Ia.js → c3-U40cGT9p.js} +0 -0
  300. /package/dist/web/assets/{cadence-DAJtUpOE.js → cadence-BFcD-LyM.js} +0 -0
  301. /package/dist/web/assets/{catppuccin-frappe-DUPoeZfb.js → catppuccin-frappe-RkC9AX6J.js} +0 -0
  302. /package/dist/web/assets/{catppuccin-latte-CK3U68Ql.js → catppuccin-latte-Bsi4l-nO.js} +0 -0
  303. /package/dist/web/assets/{catppuccin-macchiato-BzteLnIX.js → catppuccin-macchiato-B8JIdTW-.js} +0 -0
  304. /package/dist/web/assets/{catppuccin-mocha-ozLd0aBR.js → catppuccin-mocha-oH8wwrDj.js} +0 -0
  305. /package/dist/web/assets/{clarity-6KqEoBfz.js → clarity-C3WNeRCR.js} +0 -0
  306. /package/dist/web/assets/{clojure-B_uBL5C7.js → clojure-CFoAz7u9.js} +0 -0
  307. /package/dist/web/assets/{cmake-B3nxBNmj.js → cmake-Bcsqp9yR.js} +0 -0
  308. /package/dist/web/assets/{codeowners-DkS-51at.js → codeowners-DC2Z7heP.js} +0 -0
  309. /package/dist/web/assets/{codeql-rpr1z_mR.js → codeql-BfG68Al5.js} +0 -0
  310. /package/dist/web/assets/{common-lisp-BfNbv_W6.js → common-lisp-Z5P-lKQe.js} +0 -0
  311. /package/dist/web/assets/{coq-EAW-1nw0.js → coq-BJslqrnv.js} +0 -0
  312. /package/dist/web/assets/{csharp-BE3Gn5bq.js → csharp-DhhZTOTs.js} +0 -0
  313. /package/dist/web/assets/{css-Cr27CfPY.js → css-DiFIpMfE.js} +0 -0
  314. /package/dist/web/assets/{csv-B5VtaPtH.js → csv-DViLbHxI.js} +0 -0
  315. /package/dist/web/assets/{cue-jm6lYNrx.js → cue-CoG3mg2I.js} +0 -0
  316. /package/dist/web/assets/{cypher-DXkvK-pn.js → cypher-jTxC8NAB.js} +0 -0
  317. /package/dist/web/assets/{d-CgG1_yzd.js → d-6hKIUAbU.js} +0 -0
  318. /package/dist/web/assets/{dark-plus-CALzcIVB.js → dark-plus-D_-np2Sr.js} +0 -0
  319. /package/dist/web/assets/{dart-BEVoLlWg.js → dart-CVpxLpVn.js} +0 -0
  320. /package/dist/web/assets/{dax-H0BW3Q_P.js → dax-BPqJ8qIY.js} +0 -0
  321. /package/dist/web/assets/{desktop-BtYq1l_0.js → desktop-u_y5_Hr5.js} +0 -0
  322. /package/dist/web/assets/{diff-CnBYdfTg.js → diff-Duj1XMrd.js} +0 -0
  323. /package/dist/web/assets/{dist-CJJKpgR7.js → dist-CEZ4plgH.js} +0 -0
  324. /package/dist/web/assets/{docker-COBitpld.js → docker-8UGSCc7N.js} +0 -0
  325. /package/dist/web/assets/{dotenv-CG0x1m7c.js → dotenv-DVnj8kyR.js} +0 -0
  326. /package/dist/web/assets/{dracula-CLCi47X-.js → dracula-D0cEmSg7.js} +0 -0
  327. /package/dist/web/assets/{dracula-soft-BMv5cCAV.js → dracula-soft-DfGS5Yfr.js} +0 -0
  328. /package/dist/web/assets/{dream-maker-1Bd4OSww.js → dream-maker-qG8kMGTF.js} +0 -0
  329. /package/dist/web/assets/{emacs-lisp-B-2zKLn-.js → emacs-lisp-iVqyMLyT.js} +0 -0
  330. /package/dist/web/assets/{everforest-dark-BGVynCbV.js → everforest-dark-Cruygd5U.js} +0 -0
  331. /package/dist/web/assets/{everforest-light--rn3R7Cm.js → everforest-light-BiJa8RHL.js} +0 -0
  332. /package/dist/web/assets/{fennel-SWg9Us_u.js → fennel-H6XSEIoz.js} +0 -0
  333. /package/dist/web/assets/{fish-B04oqgKk.js → fish-ho_7CxX6.js} +0 -0
  334. /package/dist/web/assets/{fluent-BefzAGMN.js → fluent-1l5ssJTF.js} +0 -0
  335. /package/dist/web/assets/{fortran-free-form-alw7jMxb.js → fortran-free-form-Bo1x233A.js} +0 -0
  336. /package/dist/web/assets/{gdscript-BAyhvjCx.js → gdscript-DffFMp_k.js} +0 -0
  337. /package/dist/web/assets/{gdshader-Crl09X-B.js → gdshader-CvnpAiAF.js} +0 -0
  338. /package/dist/web/assets/{genie-BSIGRHu7.js → genie-2eOvAEzW.js} +0 -0
  339. /package/dist/web/assets/{gherkin-DJhSnSWH.js → gherkin-DL-5OUlV.js} +0 -0
  340. /package/dist/web/assets/{github-dark-r8C6qwug.js → github-dark-DrTZprgG.js} +0 -0
  341. /package/dist/web/assets/{github-dark-default-FVtXjcm8.js → github-dark-default-_mDH8IMP.js} +0 -0
  342. /package/dist/web/assets/{github-dark-dimmed-CzrUPgSb.js → github-dark-dimmed-C48hJuLn.js} +0 -0
  343. /package/dist/web/assets/{github-dark-high-contrast-1s9rF5EB.js → github-dark-high-contrast-BQ-zGGNe.js} +0 -0
  344. /package/dist/web/assets/{github-light-BvIWCkl7.js → github-light-BYc90Yx8.js} +0 -0
  345. /package/dist/web/assets/{github-light-default-CVdhzrxV.js → github-light-default-Daecj4J5.js} +0 -0
  346. /package/dist/web/assets/{github-light-high-contrast-CBAf6hjF.js → github-light-high-contrast-DoafCbsU.js} +0 -0
  347. /package/dist/web/assets/{gleam-DvL1q7aa.js → gleam-hUWWjmnp.js} +0 -0
  348. /package/dist/web/assets/{gn-BG5NvwAf.js → gn-7PJ3R70o.js} +0 -0
  349. /package/dist/web/assets/{gnuplot-BARnM-zG.js → gnuplot-V-Pxck7b.js} +0 -0
  350. /package/dist/web/assets/{go-DXvjjq4q.js → go-ARF6DprD.js} +0 -0
  351. /package/dist/web/assets/{groovy-CRylSKhr.js → groovy-CKGBIPOz.js} +0 -0
  352. /package/dist/web/assets/{gruvbox-dark-hard-BVPjnqm7.js → gruvbox-dark-hard-Cm9wqdI3.js} +0 -0
  353. /package/dist/web/assets/{gruvbox-dark-medium-U_PDgq0a.js → gruvbox-dark-medium-Cmsd6dG8.js} +0 -0
  354. /package/dist/web/assets/{gruvbox-dark-soft-CkzNtpbr.js → gruvbox-dark-soft-BBGlGGCz.js} +0 -0
  355. /package/dist/web/assets/{gruvbox-light-hard-DfMmR2G8.js → gruvbox-light-hard-BCR7KmqG.js} +0 -0
  356. /package/dist/web/assets/{gruvbox-light-medium-BGCak-dd.js → gruvbox-light-medium-CP32bga8.js} +0 -0
  357. /package/dist/web/assets/{gruvbox-light-soft-DnpM3fkN.js → gruvbox-light-soft-qjxk8ps6.js} +0 -0
  358. /package/dist/web/assets/{haskell-DXgGIDD_.js → haskell-C5DzwGRa.js} +0 -0
  359. /package/dist/web/assets/{haxe-B7qlTpRz.js → haxe-C8f_jCVv.js} +0 -0
  360. /package/dist/web/assets/{hcl-ADWfVe_Q.js → hcl-DetJkf6E.js} +0 -0
  361. /package/dist/web/assets/{hjson-BaM9gLbq.js → hjson-DeZhwIdo.js} +0 -0
  362. /package/dist/web/assets/{hlsl-iqaRA4MG.js → hlsl-SMIDLRyX.js} +0 -0
  363. /package/dist/web/assets/{horizon-BPpTnhB9.js → horizon-BMAVDxMx.js} +0 -0
  364. /package/dist/web/assets/{horizon-bright-yBLg3QbE.js → horizon-bright-3BaV_VGD.js} +0 -0
  365. /package/dist/web/assets/{houston-eFOp0ySL.js → houston-BWBbEPvb.js} +0 -0
  366. /package/dist/web/assets/{hy-BALeMn3i.js → hy-DcsmE-Nm.js} +0 -0
  367. /package/dist/web/assets/{imba-CdGUm_GD.js → imba-CnkgnYMf.js} +0 -0
  368. /package/dist/web/assets/{ini-DG7imsqe.js → ini-BnvTdau7.js} +0 -0
  369. /package/dist/web/assets/{java-D5GTT474.js → java-BLFACXat.js} +0 -0
  370. /package/dist/web/assets/{javascript-V7WwJvRm.js → javascript-BKcFWENk.js} +0 -0
  371. /package/dist/web/assets/{json-B9FhwAgR.js → json-3AJtJADf.js} +0 -0
  372. /package/dist/web/assets/{json5--Abk7MUy.js → json5-DLyDVlwe.js} +0 -0
  373. /package/dist/web/assets/{jsonc-C8jb-Z6x.js → jsonc-B5DgF1jW.js} +0 -0
  374. /package/dist/web/assets/{jsonl-CaJNxfkm.js → jsonl-CV_O71a2.js} +0 -0
  375. /package/dist/web/assets/{jsonnet-CPrKMSCU.js → jsonnet-DZfPLgYz.js} +0 -0
  376. /package/dist/web/assets/{jssm-DEu-9_uo.js → jssm-Cr1zYOsY.js} +0 -0
  377. /package/dist/web/assets/{jsx-MfAsCWDW.js → jsx-VN-JS2_q.js} +0 -0
  378. /package/dist/web/assets/{kanagawa-dragon-DnbcAduF.js → kanagawa-dragon-D9CcT3xg.js} +0 -0
  379. /package/dist/web/assets/{kanagawa-lotus-yxsY3ew8.js → kanagawa-lotus-DLXsB8fr.js} +0 -0
  380. /package/dist/web/assets/{kanagawa-wave-C2RyvZaM.js → kanagawa-wave-weDs3Wjz.js} +0 -0
  381. /package/dist/web/assets/{kdl-CvssmvcU.js → kdl-0XF56jnT.js} +0 -0
  382. /package/dist/web/assets/{kotlin-jufO49mU.js → kotlin-BPkH9vzk.js} +0 -0
  383. /package/dist/web/assets/{kusto-BAFnylR7.js → kusto-DMVpADb9.js} +0 -0
  384. /package/dist/web/assets/{laserwave-BgxFC4Ge.js → laserwave-Cl6T2cGP.js} +0 -0
  385. /package/dist/web/assets/{lean-Bgt24V1D.js → lean-BvwzQGl1.js} +0 -0
  386. /package/dist/web/assets/{less-Bk_UaqsM.js → less-DI_03xkC.js} +0 -0
  387. /package/dist/web/assets/{light-plus-nMxFfInN.js → light-plus-hIiIsQN_.js} +0 -0
  388. /package/dist/web/assets/{llvm-3_PASDOO.js → llvm-B5hjjSWc.js} +0 -0
  389. /package/dist/web/assets/{log-CKm0CxB1.js → log-D28raQiE.js} +0 -0
  390. /package/dist/web/assets/{logo-DFP1_5U6.js → logo-6Cdd4ASb.js} +0 -0
  391. /package/dist/web/assets/{luau-BcZnoyh3.js → luau-CK9nKpOX.js} +0 -0
  392. /package/dist/web/assets/{make-CpNSwgwT.js → make-CVnxKCB6.js} +0 -0
  393. /package/dist/web/assets/{markdown-DhU3Djpv.js → markdown-B9mMQzD5.js} +0 -0
  394. /package/dist/web/assets/{material-theme-DD5L3OqF.js → material-theme-BZSs2o0j.js} +0 -0
  395. /package/dist/web/assets/{material-theme-darker-CG2_tD3R.js → material-theme-darker-BeAQBzQ_.js} +0 -0
  396. /package/dist/web/assets/{material-theme-lighter-CzNkUc9Y.js → material-theme-lighter-BAe0IXcs.js} +0 -0
  397. /package/dist/web/assets/{material-theme-ocean-BtLiAaF_.js → material-theme-ocean-CiPIf0Np.js} +0 -0
  398. /package/dist/web/assets/{material-theme-palenight-Brvjz035.js → material-theme-palenight-DJBb4_XN.js} +0 -0
  399. /package/dist/web/assets/{matlab-CCY0KtMD.js → matlab-CY0dbp_s.js} +0 -0
  400. /package/dist/web/assets/{mdx-CH6YY0aS.js → mdx-DU9zbF7h.js} +0 -0
  401. /package/dist/web/assets/{mermaid-Bqh3gFI0.js → mermaid-UmmjGwdh.js} +0 -0
  402. /package/dist/web/assets/{min-dark-BUwfTzI6.js → min-dark-C59GkzOm.js} +0 -0
  403. /package/dist/web/assets/{min-light-oLanXCOk.js → min-light-B9QglrdQ.js} +0 -0
  404. /package/dist/web/assets/{mipsasm-CTh3hPWK.js → mipsasm-Dc6Jsr_D.js} +0 -0
  405. /package/dist/web/assets/{mojo-DXdszZKa.js → mojo-D5ij-nGW.js} +0 -0
  406. /package/dist/web/assets/{monokai-TNE_tRxc.js → monokai-QAtE_yCP.js} +0 -0
  407. /package/dist/web/assets/{moonbit-jrk-Gx1X.js → moonbit-BMtIdiTL.js} +0 -0
  408. /package/dist/web/assets/{move-DItn61bH.js → move-BB_jMHYZ.js} +0 -0
  409. /package/dist/web/assets/{narrat-3kB4dkh0.js → narrat-BvvoKGVn.js} +0 -0
  410. /package/dist/web/assets/{nextflow-groovy-CSKtwETr.js → nextflow-groovy-CqxtAsnF.js} +0 -0
  411. /package/dist/web/assets/{night-owl-Dm91nSIq.js → night-owl-CLoE9kjt.js} +0 -0
  412. /package/dist/web/assets/{night-owl-light-riwrcPRg.js → night-owl-light-BwIFT5Xd.js} +0 -0
  413. /package/dist/web/assets/{nix-sLgl9zGF.js → nix-BKlnVT9r.js} +0 -0
  414. /package/dist/web/assets/{nord-TWkJPI6M.js → nord-B7R1KRAK.js} +0 -0
  415. /package/dist/web/assets/{nushell-CDZi4-Gf.js → nushell-CzUme-NJ.js} +0 -0
  416. /package/dist/web/assets/{objective-c-DW9PboNp.js → objective-c-D-UKKiOc.js} +0 -0
  417. /package/dist/web/assets/{objective-cpp-2XCCX6q1.js → objective-cpp-BucWn4Fx.js} +0 -0
  418. /package/dist/web/assets/{ocaml-CxPdnjrh.js → ocaml-EJCNXMGo.js} +0 -0
  419. /package/dist/web/assets/{odin-BJjlKpkM.js → odin-CEjnRU_3.js} +0 -0
  420. /package/dist/web/assets/{one-dark-pro-K1KOmBZ7.js → one-dark-pro-Bv469NX5.js} +0 -0
  421. /package/dist/web/assets/{one-light-DQB-vUyZ.js → one-light-BlzO5RWI.js} +0 -0
  422. /package/dist/web/assets/{openscad-DCoLwn4j.js → openscad-Czl6alLw.js} +0 -0
  423. /package/dist/web/assets/{pascal-z2ktbbWp.js → pascal-BI9fLvG4.js} +0 -0
  424. /package/dist/web/assets/{pkl-Dyq4-v6z.js → pkl-ChQLsyjU.js} +0 -0
  425. /package/dist/web/assets/{plastic-C4Rq5PnD.js → plastic-DGL3mtdL.js} +0 -0
  426. /package/dist/web/assets/{plsql-BCIjO63y.js → plsql-BJDjqcWp.js} +0 -0
  427. /package/dist/web/assets/{po-_kXpd_0n.js → po-DK5O3OpF.js} +0 -0
  428. /package/dist/web/assets/{poimandres-C9XggjB3.js → poimandres-CnuqZT4T.js} +0 -0
  429. /package/dist/web/assets/{polar-DV_1L04z.js → polar-DCsUxSsQ.js} +0 -0
  430. /package/dist/web/assets/{postcss-BuqS91mQ.js → postcss-8UjdFmis.js} +0 -0
  431. /package/dist/web/assets/{powerquery-DK13B2Dc.js → powerquery-CAp0CGQj.js} +0 -0
  432. /package/dist/web/assets/{powershell-tkuhhtuP.js → powershell-CbBsKZJP.js} +0 -0
  433. /package/dist/web/assets/{prisma-WNXfBtpr.js → prisma-BnNpXCJN.js} +0 -0
  434. /package/dist/web/assets/{prolog-BzEdNKfO.js → prolog-CC-l9E8Y.js} +0 -0
  435. /package/dist/web/assets/{proto-B5meVsrA.js → proto-CV_1k2rL.js} +0 -0
  436. /package/dist/web/assets/{puppet-C7h_p4r2.js → puppet-2D8HU2bv.js} +0 -0
  437. /package/dist/web/assets/{purescript-qH1KxAs6.js → purescript-BEJAORgU.js} +0 -0
  438. /package/dist/web/assets/{python-DeTIYd5V.js → python-BBdbb7nn.js} +0 -0
  439. /package/dist/web/assets/{qmldir-4Rm5AvD3.js → qmldir-D5ZYI4Ie.js} +0 -0
  440. /package/dist/web/assets/{qss-YRbeNtr0.js → qss-BOJVGyXE.js} +0 -0
  441. /package/dist/web/assets/{r-DHwHK0Jh.js → r-B5dlEtkj.js} +0 -0
  442. /package/dist/web/assets/{racket-gKn4SFLQ.js → racket-BUAEygJ6.js} +0 -0
  443. /package/dist/web/assets/{raku-DWTmAj3A.js → raku-BG0Ss0ID.js} +0 -0
  444. /package/dist/web/assets/{red-BFuHRZFS.js → red-C5yyQbtG.js} +0 -0
  445. /package/dist/web/assets/{reg-DYoOF5tH.js → reg-DHoBWtLf.js} +0 -0
  446. /package/dist/web/assets/{regexp-6rXQHD2-.js → regexp-DDtJrlKv.js} +0 -0
  447. /package/dist/web/assets/{rel-Z_tTruCU.js → rel-CR6WJDRD.js} +0 -0
  448. /package/dist/web/assets/{riscv-DfTfKuvU.js → riscv-2V9tC-nx.js} +0 -0
  449. /package/dist/web/assets/{ron-CsUtW8gQ.js → ron-QLTmqGUw.js} +0 -0
  450. /package/dist/web/assets/{rose-pine-Dng_vCo6.js → rose-pine-be0__X3A.js} +0 -0
  451. /package/dist/web/assets/{rose-pine-dawn-Ds88Nezf.js → rose-pine-dawn-n_WQJwsW.js} +0 -0
  452. /package/dist/web/assets/{rose-pine-moon-DWpzrqNz.js → rose-pine-moon-t2aLtzgB.js} +0 -0
  453. /package/dist/web/assets/{rosmsg-h2sO84oD.js → rosmsg-Ce1JD8vt.js} +0 -0
  454. /package/dist/web/assets/{rust-Dx86yfCi.js → rust-5fHH-ZoW.js} +0 -0
  455. /package/dist/web/assets/{sass-BwwVhdT9.js → sass-DPmGFiOJ.js} +0 -0
  456. /package/dist/web/assets/{scala-WJqNEN-w.js → scala-BRMVFbZF.js} +0 -0
  457. /package/dist/web/assets/{scheme-uwj-t1Rp.js → scheme-BMSJWevF.js} +0 -0
  458. /package/dist/web/assets/{sdbl-BYy5hgEz.js → sdbl-DTZ-pEHI.js} +0 -0
  459. /package/dist/web/assets/{shellscript-DWTW_w2K.js → shellscript-6-9yg5Cz.js} +0 -0
  460. /package/dist/web/assets/{slack-dark-BvhGq5vQ.js → slack-dark-DKJgiR7e.js} +0 -0
  461. /package/dist/web/assets/{slack-ochin-C9g0ZRFr.js → slack-ochin-WjehVgyK.js} +0 -0
  462. /package/dist/web/assets/{smalltalk-ZEiZ6i7U.js → smalltalk-BChf4SN1.js} +0 -0
  463. /package/dist/web/assets/{snazzy-light-29OA24q5.js → snazzy-light-C1WAwOx6.js} +0 -0
  464. /package/dist/web/assets/{solarized-dark-Bnq9IOn_.js → solarized-dark-BxREZrGx.js} +0 -0
  465. /package/dist/web/assets/{solarized-light-CYdI7T5g.js → solarized-light-DG29B1pR.js} +0 -0
  466. /package/dist/web/assets/{solidity-DcuIDB-_.js → solidity-bDbtGwdR.js} +0 -0
  467. /package/dist/web/assets/{splunk-D5ONE91g.js → splunk-Ceqx5njI.js} +0 -0
  468. /package/dist/web/assets/{sql-DIwJJDU-.js → sql-OwMEKbz8.js} +0 -0
  469. /package/dist/web/assets/{ssh-config-D8717tLI.js → ssh-config-WAR3lu2k.js} +0 -0
  470. /package/dist/web/assets/{stylus-Cm1lO4Y6.js → stylus-Cjmoz3d8.js} +0 -0
  471. /package/dist/web/assets/{swift-X8oWpSeU.js → swift-S_ZSBG1q.js} +0 -0
  472. /package/dist/web/assets/{synthwave-84-DF-t1UKY.js → synthwave-84-DiUeWMSh.js} +0 -0
  473. /package/dist/web/assets/{system-verilog-JYyHFpPf.js → system-verilog-RRjqPjUV.js} +0 -0
  474. /package/dist/web/assets/{systemd-Da-5Ma_c.js → systemd-BD3SkXoU.js} +0 -0
  475. /package/dist/web/assets/{talonscript-CXraVPho.js → talonscript-DWefISOm.js} +0 -0
  476. /package/dist/web/assets/{tasl-BCmWezH9.js → tasl-DCjSS-09.js} +0 -0
  477. /package/dist/web/assets/{tcl-BTufzfbf.js → tcl-DTYqT_66.js} +0 -0
  478. /package/dist/web/assets/{terraform-CtUA9c8W.js → terraform-CkbP5QnR.js} +0 -0
  479. /package/dist/web/assets/{tokyo-night-BCThYXmz.js → tokyo-night-qVSqO9xy.js} +0 -0
  480. /package/dist/web/assets/{toml-D5YlDr6Y.js → toml-D9F5Osqs.js} +0 -0
  481. /package/dist/web/assets/{tsv-CBB-P_Sl.js → tsv-C5tpHoxm.js} +0 -0
  482. /package/dist/web/assets/{tsx-D5B4juDp.js → tsx-CMWkGtGm.js} +0 -0
  483. /package/dist/web/assets/{turtle-D8PCeJ-v.js → turtle-Cn4z3xOi.js} +0 -0
  484. /package/dist/web/assets/{typescript-CMPnTawp.js → typescript-DSyAEWsn.js} +0 -0
  485. /package/dist/web/assets/{typespec-CHxldxIW.js → typespec-B93Qap7y.js} +0 -0
  486. /package/dist/web/assets/{typst-Q8CqX5OR.js → typst-DtlOmFlX.js} +0 -0
  487. /package/dist/web/assets/{v-Bf2hKAMu.js → v-Dfzf-szS.js} +0 -0
  488. /package/dist/web/assets/{vala-Cbete-UE.js → vala-CyiQZQ9f.js} +0 -0
  489. /package/dist/web/assets/{vb-CA-xWAcX.js → vb-C7DGSldd.js} +0 -0
  490. /package/dist/web/assets/{verilog-B3wb8CfT.js → verilog-kF-hClY2.js} +0 -0
  491. /package/dist/web/assets/{vesper-D6r3VJq_.js → vesper-BmKcADuz.js} +0 -0
  492. /package/dist/web/assets/{vhdl-C42S_797.js → vhdl-CDnb16KJ.js} +0 -0
  493. /package/dist/web/assets/{viml-BePrIAs8.js → viml-B8cs6Ady.js} +0 -0
  494. /package/dist/web/assets/{vitesse-black-DRqn61Ck.js → vitesse-black-BR6o9Gto.js} +0 -0
  495. /package/dist/web/assets/{vitesse-dark-39_8UE-w.js → vitesse-dark-Dye14-KB.js} +0 -0
  496. /package/dist/web/assets/{vitesse-light-B1jwrxDm.js → vitesse-light-BLpswIxf.js} +0 -0
  497. /package/dist/web/assets/{vyper-tDJv3imF.js → vyper-DU51Z97W.js} +0 -0
  498. /package/dist/web/assets/{wasm-DR7eskZH.js → wasm-CVqlJAR2.js} +0 -0
  499. /package/dist/web/assets/{wenyan-CezVfqN3.js → wenyan-DhCa3hCn.js} +0 -0
  500. /package/dist/web/assets/{wgsl-CwOcIiIG.js → wgsl-Bj1Ec4vv.js} +0 -0
  501. /package/dist/web/assets/{wikitext-C5coomYc.js → wikitext-C90DHNa0.js} +0 -0
  502. /package/dist/web/assets/{wit-C9yyX6-U.js → wit-DUgbOD7Z.js} +0 -0
  503. /package/dist/web/assets/{wolfram-BWi0rMAa.js → wolfram-BgwUqD1I.js} +0 -0
  504. /package/dist/web/assets/{yaml-CGXhKUtT.js → yaml-DEQulj-8.js} +0 -0
  505. /package/dist/web/assets/{zenscript-Do7WLoJh.js → zenscript-C7Jp7TgT.js} +0 -0
  506. /package/dist/web/assets/{zig-Cr80Qrru.js → zig-9x4qXYLg.js} +0 -0
@@ -0,0 +1,63 @@
1
+ /**
2
+ * "Is the network back yet?" gate for the named-tunnel spawn path.
3
+ *
4
+ * A connector started before the machine's Wi-Fi has reassociated cannot
5
+ * register, and the readiness wait then expires on a failure that would have
6
+ * cleared itself seconds later. Observed live after a laptop resumed from
7
+ * sleep: the supervisor regenerated the tunnel immediately, named timed out,
8
+ * and the permanent hostname served 530 until someone intervened.
9
+ *
10
+ * Only the named path waits: a quick tunnel that fails is retried with a fresh
11
+ * URL anyway, while a named failure costs the user their fixed address.
12
+ */
13
+
14
+ /** Cheap, unauthenticated, and the exact host the connector needs anyway. */
15
+ const PROBE_URL = "https://api.cloudflare.com/client/v4/";
16
+
17
+ export interface NetworkReadyOptions {
18
+ /** Give up after this long and let the caller try regardless. */
19
+ timeoutMs?: number;
20
+ /** Wait between probes. */
21
+ intervalMs?: number;
22
+ /** Per-probe timeout. */
23
+ probeTimeoutMs?: number;
24
+ /** Injectable for tests; defaults to a HEAD against Cloudflare's API. */
25
+ probe?: () => Promise<boolean>;
26
+ sleep?: (ms: number) => Promise<void>;
27
+ now?: () => number;
28
+ }
29
+
30
+ const defaultSleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));
31
+
32
+ async function defaultProbe(probeTimeoutMs: number): Promise<boolean> {
33
+ try {
34
+ // Any HTTP answer proves DNS + TCP + TLS to Cloudflare work; the status
35
+ // code is irrelevant (401/404 are just as good a signal as 200).
36
+ await fetch(PROBE_URL, { method: "HEAD", signal: AbortSignal.timeout(probeTimeoutMs) });
37
+ return true;
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Resolves `true` as soon as Cloudflare is reachable, `false` if the budget
45
+ * runs out. Never throws: a failed wait is a hint, not a veto — the caller
46
+ * still attempts the spawn, because a probe can fail for reasons (proxy, DNS
47
+ * filtering) that would not stop the connector itself.
48
+ */
49
+ export async function waitForCloudflareReachable(opts: NetworkReadyOptions = {}): Promise<boolean> {
50
+ const timeoutMs = opts.timeoutMs ?? 60_000;
51
+ const intervalMs = opts.intervalMs ?? 2_000;
52
+ const probeTimeoutMs = opts.probeTimeoutMs ?? 3_000;
53
+ const probe = opts.probe ?? (() => defaultProbe(probeTimeoutMs));
54
+ const sleep = opts.sleep ?? defaultSleep;
55
+ const now = opts.now ?? Date.now;
56
+
57
+ const deadline = now() + timeoutMs;
58
+ for (;;) {
59
+ if (await probe()) return true;
60
+ if (now() >= deadline) return false;
61
+ await sleep(intervalMs);
62
+ }
63
+ }
@@ -22,6 +22,8 @@ import {
22
22
  import type { ResolvedTunnelConfig, TunnelMode } from "./named-tunnel/named-tunnel-config.ts";
23
23
  import { readTunnelConfigFresh, chooseTunnelSpawn } from "./named-tunnel/named-tunnel-runtime.ts";
24
24
  import { waitForLogLine } from "./named-tunnel/named-tunnel-readiness.ts";
25
+ import { nextNamedRetryDelayMs } from "./named-tunnel/named-tunnel-retry.ts";
26
+ import { waitForCloudflareReachable } from "./named-tunnel/network-ready.ts";
25
27
  import { decideNamedProbeAction, publicHostnameIsOurs } from "./named-tunnel/named-tunnel-probe-state.ts";
26
28
  import { isCloudflaredPid } from "./tunnel-registry.service.ts";
27
29
  import { getQuickTunnelArgs } from "./cloudflared.service.ts";
@@ -102,6 +104,18 @@ let namedTunnelMode: ResolvedTunnelConfig | null = null;
102
104
  // but live=quick, and every status write / throttle / restart decision must
103
105
  // key off what's actually running.
104
106
  let lastSpawnMode: TunnelMode = "quick";
107
+ // How long to wait for the network before attempting a named tunnel.
108
+ const NETWORK_WAIT_MS = 60_000;
109
+ // Falling back to quick used to be permanent until the next supervisor start.
110
+ // These drive a bounded set of retries so a transient failure (network not up
111
+ // yet after a resume) heals itself instead of leaving the hostname dark.
112
+ let namedRetryAttempt = 0;
113
+ let namedRetryTimer: ReturnType<typeof setTimeout> | null = null;
114
+ // A retry that finds the machine still offline re-arms the same rung instead
115
+ // of burning it. Bounded so a laptop left off the network doesn't log a
116
+ // deferral every minute for the rest of its uptime.
117
+ let namedRetryDeferrals = 0;
118
+ const MAX_NAMED_RETRY_DEFERRALS = 10;
105
119
  // Set once the named probe has already tried a restart-and-hope; a second
106
120
  // consecutive failure then warns and stops instead of looping kills forever.
107
121
  let namedProbeRestartAttempted = false;
@@ -293,6 +307,72 @@ async function ensureBindablePort(preferred: number, host: string): Promise<numb
293
307
  * The trycloudflare URL will change — unavoidable, the old quick-tunnel session
294
308
  * is dead. Fire-and-forget: spawnTunnel's own loop owns liveness afterwards.
295
309
  */
310
+ /** Cancel a pending named retry — a newer decision supersedes it. */
311
+ function cancelNamedRetry(): void {
312
+ if (namedRetryTimer) { clearTimeout(namedRetryTimer); namedRetryTimer = null; }
313
+ }
314
+
315
+ /**
316
+ * After a downgrade to quick, try named again on a bounded backoff.
317
+ *
318
+ * Re-checks everything at fire time rather than trusting the state captured
319
+ * when the timer was set: the user may have switched to quick deliberately, a
320
+ * `retunnel` may already have restored named, or the supervisor may be going
321
+ * down. Any of those makes the retry pointless or actively wrong.
322
+ */
323
+ function scheduleNamedRetry(port: number): void {
324
+ cancelNamedRetry();
325
+ const delay = nextNamedRetryDelayMs(namedRetryAttempt);
326
+ if (delay === null) {
327
+ log("WARN", "Named tunnel still failing after the last retry — staying on the quick URL");
328
+ return;
329
+ }
330
+ const attemptNumber = namedRetryAttempt + 1;
331
+ log("INFO", `Named tunnel downgraded to quick — retrying in ${Math.round(delay / 1000)}s (#${attemptNumber})`);
332
+ namedRetryTimer = setTimeout(() => {
333
+ namedRetryTimer = null;
334
+ if (shuttingDown || getState() !== "running") return;
335
+ if (namedTunnelMode?.mode !== "named") return; // user switched to quick meanwhile
336
+ if (lastSpawnMode === "named") return; // already recovered
337
+ // Restarting tears down the quick tunnel that is currently serving the
338
+ // machine, so only do it once the network can actually carry a connector —
339
+ // otherwise a still-offline laptop loses its working temporary URL too and
340
+ // gets a new one for nothing. Offline: re-arm the same rung, don't burn it.
341
+ void waitForCloudflareReachable({ timeoutMs: 5_000, intervalMs: 1_000 }).then((reachable) => {
342
+ if (shuttingDown || getState() !== "running") return;
343
+ if (namedTunnelMode?.mode !== "named" || lastSpawnMode === "named") return;
344
+ if (!reachable) {
345
+ if (++namedRetryDeferrals > MAX_NAMED_RETRY_DEFERRALS) {
346
+ log("WARN", "Named retry abandoned — network still down after repeated attempts");
347
+ return;
348
+ }
349
+ log("INFO", `Named retry #${attemptNumber} deferred — network still down`);
350
+ scheduleNamedRetry(port);
351
+ return;
352
+ }
353
+ namedRetryDeferrals = 0;
354
+ namedRetryAttempt = attemptNumber;
355
+ log("INFO", `Retrying the named tunnel (#${attemptNumber})`);
356
+ restartTunnel(port);
357
+ });
358
+ }, delay);
359
+ namedRetryTimer.unref?.();
360
+ }
361
+
362
+ /**
363
+ * Called once per successful spawn: arm the retry ladder when we had to fall
364
+ * back to quick, clear it the moment named is actually live again.
365
+ */
366
+ function noteSpawnOutcomeForNamedRetry(mode: TunnelMode, downgraded: boolean, port: number): void {
367
+ if (mode === "named") {
368
+ cancelNamedRetry();
369
+ namedRetryAttempt = 0;
370
+ namedRetryDeferrals = 0;
371
+ return;
372
+ }
373
+ if (downgraded) scheduleNamedRetry(port);
374
+ }
375
+
296
376
  function restartTunnel(port: number) {
297
377
  lastTunnelRegenAt = Date.now();
298
378
  if (tunnelChild) { try { tunnelChild.kill(); } catch {} tunnelChild = null; }
@@ -744,6 +824,15 @@ export async function spawnTunnel(port: number, generation: number = ++tunnelGen
744
824
  }
745
825
 
746
826
  const attempts = buildTunnelAttempts(namedTunnelMode, port);
827
+ // Only the named path pays for this: its failure costs the user their fixed
828
+ // address, while a quick tunnel just retries with a new URL. Spawning a
829
+ // connector before the machine's network is back is the difference between
830
+ // "reconnects in 5s" and "hostname dark until someone notices".
831
+ if (attempts[0]?.mode === "named") {
832
+ const reachable = await waitForCloudflareReachable({ timeoutMs: NETWORK_WAIT_MS });
833
+ if (!reachable) log("WARN", `Cloudflare unreachable after ${NETWORK_WAIT_MS / 1000}s — attempting the tunnel anyway`);
834
+ if (generation !== tunnelGeneration) return; // superseded while waiting
835
+ }
747
836
  const logPath = cloudflaredLogPath();
748
837
  // Truncate stale content from a prior generation; best-effort — Windows can
749
838
  // silently fail this while a previous detached cloudflared still holds the
@@ -821,6 +910,7 @@ export async function spawnTunnel(port: number, generation: number = ++tunnelGen
821
910
  adoptedTunnelPid = winPid;
822
911
  tunnelChild = null;
823
912
  lastSpawnMode = successfulMode;
913
+ noteSpawnOutcomeForNamedRetry(successfulMode, downgraded, port);
824
914
  // `namedProbeRestartAttempted` is deliberately NOT touched here — see the
825
915
  // probe's `decideNamedProbeAction` state machine. A successful spawn only
826
916
  // proves cloudflared reconnected to Cloudflare's edge, not that the
@@ -908,6 +998,7 @@ export async function spawnTunnel(port: number, generation: number = ++tunnelGen
908
998
  }
909
999
 
910
1000
  lastSpawnMode = successfulMode;
1001
+ noteSpawnOutcomeForNamedRetry(successfulMode, downgraded, port);
911
1002
  // `namedProbeRestartAttempted` is deliberately NOT touched here — see the
912
1003
  // probe's `decideNamedProbeAction` state machine and the win32 branch above
913
1004
  // for why a bare spawn success must never re-arm the one-restart budget.
@@ -1708,6 +1799,7 @@ export async function softStop() {
1708
1799
  export function shutdown() {
1709
1800
  if (shuttingDown) return;
1710
1801
  shuttingDown = true;
1802
+ cancelNamedRetry();
1711
1803
  log("INFO", "Supervisor shutting down");
1712
1804
 
1713
1805
  // Unblock if paused
@@ -1910,6 +2002,10 @@ export async function runSupervisor(opts: {
1910
2002
  // over from the outage trips "restart once" after a handful of probes.
1911
2003
  namedProbeRestartAttempted = false;
1912
2004
  tunnelFailCount = 0;
2005
+ // A deliberate retunnel supersedes any pending automatic retry, and
2006
+ // restarts the ladder so a later transient failure gets a full budget.
2007
+ cancelNamedRetry();
2008
+ namedRetryAttempt = 0;
1913
2009
  restartTunnel(_opts.port);
1914
2010
  // Deliberate fall-through (no return): a bare `ppm restart` sends a
1915
2011
  // bare SIGUSR2 with no command file of its own, and a `retunnel` that
@@ -2059,6 +2155,8 @@ export async function runSupervisor(opts: {
2059
2155
  // rather than inheriting a stale exhausted one (flag AND counter).
2060
2156
  namedProbeRestartAttempted = false;
2061
2157
  tunnelFailCount = 0;
2158
+ cancelNamedRetry();
2159
+ namedRetryAttempt = 0;
2062
2160
  restartTunnel(_opts.port);
2063
2161
  });
2064
2162
  }
@@ -1,5 +1,6 @@
1
1
  import { readImageDimensions } from "./image-dimensions.ts";
2
2
  import { base64ByteLength, imagePlaceholderText } from "../shared/tool-result-content.ts";
3
+ import { MAX_IMAGE_DIMENSION } from "../shared/image-limits.ts";
3
4
 
4
5
  /**
5
6
  * Auditing and removal of image payloads inside a Claude Code session transcript.
@@ -11,9 +12,14 @@ import { base64ByteLength, imagePlaceholderText } from "../shared/tool-result-co
11
12
  *
12
13
  * Images live in two places, and they are not equally safe to remove. A tool result can be
13
14
  * produced again by re-reading the file it came from, so those are removed by default. An
14
- * image the user attached to a message usually exists nowhere else, so it is left alone
15
- * unless the caller opts in — which it must be able to do, because a single oversized
16
- * attachment is enough to make every later turn of the session fail outright.
15
+ * attachment is left alone unless the caller opts in.
16
+ *
17
+ * That default is now more cautious than it needs to be for images this app sent itself: the
18
+ * composer uploads a copy before embedding the payload, and leaves the path in the message, so
19
+ * such an attachment is as re-readable as a tool result. It stays opt-in because a transcript
20
+ * also collects attachments from elsewhere — another client, an older build, a session shared
21
+ * with the CLI — and for those the payload really is the last copy. The caller must be able to
22
+ * reach it either way: one oversized attachment fails every later turn of the session.
17
23
  *
18
24
  * A tool result's payload sits at `message.content[].content[]`; an attachment sits directly
19
25
  * at `message.content[]`. A record also carries an image-shaped echo at the top-level
@@ -28,7 +34,7 @@ import { base64ByteLength, imagePlaceholderText } from "../shared/tool-result-co
28
34
  * Comparisons here are `>=` rather than `>` for that reason: treating 2000px as acceptable
29
35
  * hides the one image the API is rejecting and makes a cleanup look like a no-op.
30
36
  */
31
- export const MANY_IMAGE_DIMENSION_LIMIT = 2000;
37
+ export const MANY_IMAGE_DIMENSION_LIMIT = MAX_IMAGE_DIMENSION;
32
38
 
33
39
  /** Base64 characters decoded to inspect a header — a JPEG frame can sit behind EXIF. */
34
40
  const HEADER_CHARS = 4096;
@@ -38,9 +44,10 @@ export type StripMode = "oversized" | "all";
38
44
  /** Which of the two image locations a scan or strip should cover. */
39
45
  export interface ImageScopeOpts {
40
46
  /**
41
- * Also cover images the user attached to their own messages. Off by default: the payload
42
- * in the transcript is typically the only remaining copy, so removing it is lossy in a way
43
- * removing a tool result is not.
47
+ * Also cover images the user attached to their own messages. Off by default, because a
48
+ * transcript may hold attachments this app never uploaded from another client or an older
49
+ * build for which the embedded payload is the last copy. Attachments the composer sent
50
+ * keep an uploaded file and its path, so removing those is recoverable.
44
51
  */
45
52
  includeAttachments?: boolean;
46
53
  }
@@ -117,10 +117,15 @@ let upgradeInProgress = false;
117
117
 
118
118
  /** Build the global-install command. Absolute runtime path (not a bare
119
119
  * "bun"/"npm") because autostart (launchd/systemd) may not have the runtime's
120
- * bin dir on $PATH, where a bare name fails with "Executable not found". */
120
+ * bin dir on $PATH, where a bare name fails with "Executable not found".
121
+ *
122
+ * bun gets `--no-cache`: it resolves versions from a cached package manifest
123
+ * and, right after a release, that manifest predates the version the update
124
+ * check just found — `bun install -g pkg@new` then fails with "No version
125
+ * matching … (but package exists)" until the cache happens to expire. */
121
126
  export function buildUpgradeCommand(method: "bun" | "npm", pkg: string): string[] {
122
127
  return method === "bun"
123
- ? [process.execPath, "install", "-g", pkg]
128
+ ? [process.execPath, "install", "-g", "--no-cache", pkg]
124
129
  : [resolveNpmBin(), "install", "-g", pkg];
125
130
  }
126
131
 
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The per-image dimension ceiling the API applies, and the arithmetic for staying under it.
3
+ *
4
+ * Shared because both ends of the pipeline need the same number: the browser downscales an
5
+ * attachment before it is ever uploaded, and the transcript tooling reports on images that
6
+ * slipped in from elsewhere (another client, an older build) still carrying the full size.
7
+ */
8
+
9
+ /**
10
+ * Longest side, in pixels, at which the API starts refusing an image once a request carries
11
+ * several of them. The value is a ceiling, so an image measuring exactly this is already over
12
+ * it — comparisons are `>=`, and a downscale targets strictly below.
13
+ */
14
+ export const MAX_IMAGE_DIMENSION = 2000;
15
+
16
+ /**
17
+ * Dimensions that bring an image under the cap, or null when it is already small enough.
18
+ *
19
+ * Scales the longest side to one pixel below the cap and takes the other side with it, so the
20
+ * aspect ratio survives and the result cannot land back on the boundary through rounding.
21
+ */
22
+ export function fitWithin(
23
+ width: number,
24
+ height: number,
25
+ max: number = MAX_IMAGE_DIMENSION,
26
+ ): { width: number; height: number } | null {
27
+ if (!(width > 0) || !(height > 0)) return null;
28
+ const longest = Math.max(width, height);
29
+ if (longest < max) return null;
30
+
31
+ const target = max - 1;
32
+ const scale = target / longest;
33
+ return {
34
+ width: width >= height ? target : Math.max(1, Math.round(width * scale)),
35
+ height: height > width ? target : Math.max(1, Math.round(height * scale)),
36
+ };
37
+ }
package/src/types/api.ts CHANGED
@@ -23,7 +23,9 @@ export type TerminalWsMessage =
23
23
 
24
24
  /** WebSocket message types (chat) */
25
25
  export type ChatWsClientMessage =
26
- | { type: "message"; content: string; permissionMode?: string; priority?: 'now' | 'next' | 'later'; images?: Array<{ data: string; mediaType: string }>; model?: string; effort?: string; thinking?: boolean }
26
+ | { type: "message"; content: string; permissionMode?: string; priority?: 'now' | 'next' | 'later'; images?: Array<{ data: string; mediaType: string }>;
27
+ /** Uploaded paths for the same images, for providers that take a file not a payload. */
28
+ imagePaths?: string[]; model?: string; effort?: string; thinking?: boolean }
27
29
  | { type: "cancel" }
28
30
  | { type: "set_model"; model: string }
29
31
  | { type: "set_effort"; effort: string }
package/src/types/chat.ts CHANGED
@@ -2,6 +2,12 @@ export interface SendMessageOpts {
2
2
  permissionMode?: import("./config").PermissionMode | string;
3
3
  priority?: 'now' | 'next' | 'later';
4
4
  images?: Array<{ data: string; mediaType: string }>;
5
+ /**
6
+ * Uploaded paths for the same attachments, for providers that take a file rather than a
7
+ * payload. Codex's turn input accepts `localImage` by path and has no base64 form, so an
8
+ * image reaches it this way or not at all.
9
+ */
10
+ imagePaths?: string[];
5
11
  /** Per-session model override; falls back to provider config model when absent */
6
12
  model?: string;
7
13
  /** Override the provider's 1M-context setting for this call (false = never add the
@@ -22,6 +22,8 @@ export interface TreeItemMsg {
22
22
  description?: string;
23
23
  tooltip?: string;
24
24
  icon?: string;
25
+ /** Label wraps instead of truncating — for rows whose text is the content. */
26
+ wrap?: boolean;
25
27
  collapsibleState: "none" | "collapsed" | "expanded";
26
28
  command?: string;
27
29
  children?: TreeItemMsg[];
@@ -52,6 +52,16 @@ export function AttachmentChips({ attachments, onRemove }: AttachmentChipsProps)
52
52
  <Loader2 className="size-3 shrink-0 animate-spin text-text-subtle" />
53
53
  ) : att.status === "error" ? (
54
54
  <span className="text-error shrink-0" title="Upload failed">!</span>
55
+ ) : att.resized ? (
56
+ // Says what happened to the image. A screenshot arriving at the model smaller
57
+ // than the one that was pasted is worth stating rather than leaving to be
58
+ // discovered, and the measurement was already being recorded for nothing.
59
+ <span
60
+ className="shrink-0 text-[10px] text-text-subtle"
61
+ title={`Shrunk from ${att.resized.from.width}x${att.resized.from.height} to ${att.resized.to.width}x${att.resized.to.height}`}
62
+ >
63
+ {att.resized.to.width}&times;{att.resized.to.height}
64
+ </span>
55
65
  ) : null}
56
66
 
57
67
  {/* Remove button */}
@@ -2,6 +2,8 @@ import { useState, useCallback, useRef, useEffect } from "react";
2
2
  import { Loader2, Upload, X } from "lucide-react";
3
3
  import { toast } from "sonner";
4
4
  import { api, projectUrl } from "@/lib/api-client";
5
+ import { selectInlineImages } from "@/lib/image-resize-limits";
6
+ import { splitAttachmentMarkers } from "@/lib/attachment-marker-split";
5
7
  import { useChat } from "@/hooks/use-chat";
6
8
  import { useUsage } from "@/hooks/use-usage";
7
9
  import { useTabStore } from "@/stores/tab-store";
@@ -56,7 +58,7 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
56
58
  );
57
59
 
58
60
  // Pending message to send after WS connects (replaces unreliable setTimeout)
59
- const pendingSendRef = useRef<{ content: string; permissionMode?: string } | null>(null);
61
+ const pendingSendRef = useRef<{ content: string; permissionMode?: string; images?: Array<{ data: string; mediaType: string }>; imagePaths?: string[] } | null>(null);
60
62
 
61
63
  // Drag-and-drop state
62
64
  const [isDragging, setIsDragging] = useState(false);
@@ -142,9 +144,9 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
142
144
  // Flush pending message once WS connects (replaces unreliable setTimeout)
143
145
  useEffect(() => {
144
146
  if (isConnected && pendingSendRef.current) {
145
- const { content, permissionMode: pm } = pendingSendRef.current;
147
+ const { content, permissionMode: pm, images: pendingImages, imagePaths: pendingPaths } = pendingSendRef.current;
146
148
  pendingSendRef.current = null;
147
- sendMessage(content, { permissionMode: pm });
149
+ sendMessage(content, { permissionMode: pm, ...(pendingImages?.length && { images: pendingImages }), ...(pendingPaths?.length && { imagePaths: pendingPaths }) });
148
150
  }
149
151
  }, [isConnected, sendMessage]);
150
152
 
@@ -350,7 +352,11 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
350
352
 
351
353
  /** Build message content with file references and inline text snippets prepended */
352
354
  const buildMessageWithAttachments = useCallback(
353
- (content: string, attachments: ChatAttachment[]): string => {
355
+ (
356
+ content: string,
357
+ attachments: ChatAttachment[],
358
+ inlineImages: Array<{ data: string; mediaType: string }> = [],
359
+ ): string => {
354
360
  if (attachments.length === 0) return content;
355
361
 
356
362
  const parts: string[] = [];
@@ -360,12 +366,27 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
360
366
  if (att.textContent) parts.push(att.textContent);
361
367
  }
362
368
 
363
- // Server-uploaded file references
364
- const fileAtts = attachments.filter((a) => a.serverPath);
365
- if (fileAtts.length > 0) {
366
- const fileRefs = fileAtts.map((a) => a.serverPath!).join("\n");
369
+ // Server-uploaded file references. An image keeps its path even when the payload rides
370
+ // along with the message, because the transcript is what the chat re-renders from and
371
+ // the path is the only thing in it a thumbnail can be drawn from.
372
+ //
373
+ // An image sent inline says so in the marker. A bare path in a user message reads as an
374
+ // invitation to open it, and a model that takes it pays for the same picture twice —
375
+ // once inline, once as a tool result, both replayed on every later turn.
376
+ //
377
+ // Which marker an image gets follows the payloads that are actually going out, not
378
+ // `imageData` alone: the per-message caps can leave one behind, and announcing that its
379
+ // contents are included would hand the model neither the picture nor a reason to open
380
+ // the file. `inlineImages` defaults to none, so a caller that sends no payloads at all
381
+ // (the edit-fork path takes only text) gets the plain markers throughout.
382
+ const { inlineImagePaths, pathOnlyPaths } = splitAttachmentMarkers(attachments, inlineImages);
383
+ for (const path of inlineImagePaths) {
384
+ parts.push(`[Attached image (contents included in this message): ${path}]`);
385
+ }
386
+ if (pathOnlyPaths.length > 0) {
387
+ const fileRefs = pathOnlyPaths.join("\n");
367
388
  parts.push(
368
- fileAtts.length === 1
389
+ pathOnlyPaths.length === 1
369
390
  ? `[Attached file: ${fileRefs}]`
370
391
  : `[Attached files:\n${fileRefs}\n]`,
371
392
  );
@@ -379,8 +400,11 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
379
400
 
380
401
  const handleSend = useCallback(
381
402
  async (content: string, attachments: ChatAttachment[] = [], priority?: MessagePriority) => {
382
- const fullContent = buildMessageWithAttachments(content, attachments);
383
- if (!fullContent.trim()) return;
403
+ const images = selectInlineImages(attachments);
404
+ const fullContent = buildMessageWithAttachments(content, attachments, images);
405
+ // Providers that take a file rather than a payload (codex) read these instead.
406
+ const imagePaths = attachments.filter((a) => a.isImage && a.serverPath).map((a) => a.serverPath!);
407
+ if (!fullContent.trim() && images.length === 0) return;
384
408
 
385
409
  if (!sessionId) {
386
410
  try {
@@ -394,14 +418,14 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
394
418
  setSessionId(session.id);
395
419
  setProviderId(session.providerId);
396
420
  // Queue message — will be sent by effect when WS reports isConnected
397
- pendingSendRef.current = { content: fullContent, permissionMode };
421
+ pendingSendRef.current = { content: fullContent, permissionMode, images, imagePaths };
398
422
  return;
399
423
  } catch (e) {
400
424
  console.error("Failed to create session:", e);
401
425
  return;
402
426
  }
403
427
  }
404
- sendMessage(fullContent, { permissionMode, priority });
428
+ sendMessage(fullContent, { permissionMode, priority, ...(images.length > 0 && { images }), ...(imagePaths.length > 0 && { imagePaths }) });
405
429
  },
406
430
  [sessionId, providerId, projectName, sendMessage, buildMessageWithAttachments, permissionMode, metadata],
407
431
  );
@@ -2,6 +2,8 @@ import { useState, useRef, useCallback, useEffect, memo, type KeyboardEvent, typ
2
2
  import { ArrowUp, Square, Paperclip, Loader2, Mic, MicOff, Zap, ListOrdered, Clock, Bot, X } from "lucide-react";
3
3
  import { useVoiceInput } from "@/hooks/use-voice-input";
4
4
  import { api, projectUrl, getAuthToken } from "@/lib/api-client";
5
+ import { downscaleImage } from "@/lib/image-resize";
6
+ import { INLINE_IMAGE_LIMITS } from "@/lib/image-resize-limits";
5
7
  import { randomId } from "@/lib/utils";
6
8
  import { ownsGlobalShortcut } from "@/lib/owns-global-shortcut";
7
9
  import { SEND_TO_CHAT_EVENT, SEND_TO_CHAT_ACK_EVENT, type SendToChatDetail } from "@/lib/send-to-chat";
@@ -17,6 +19,29 @@ import { fetchSlashItems, clearSlashItemsCache } from "@/lib/slash-items-cache";
17
19
  import type { FileNode } from "../../../types/project";
18
20
  import { useFileStore } from "@/stores/file-store";
19
21
 
22
+ /**
23
+ * Base64 payload for an image file, or undefined when it cannot be read.
24
+ *
25
+ * Reads through FileReader rather than assembling the string by hand: a phone photo is
26
+ * millions of characters, and building it in chunks on the main thread freezes the composer
27
+ * for as long as it takes.
28
+ */
29
+ async function readImageData(file: File): Promise<{ data: string; mediaType: string } | undefined> {
30
+ try {
31
+ const dataUrl = await new Promise<string>((resolve, reject) => {
32
+ const reader = new FileReader();
33
+ reader.onload = () => resolve(String(reader.result ?? ""));
34
+ reader.onerror = () => reject(reader.error);
35
+ reader.readAsDataURL(file);
36
+ });
37
+ const comma = dataUrl.indexOf(",");
38
+ const data = comma >= 0 ? dataUrl.slice(comma + 1) : "";
39
+ return data ? { data, mediaType: file.type || "image/png" } : undefined;
40
+ } catch {
41
+ return undefined;
42
+ }
43
+ }
44
+
20
45
  export interface ChatAttachment {
21
46
  id: string;
22
47
  name: string;
@@ -27,6 +52,16 @@ export interface ChatAttachment {
27
52
  serverPath?: string;
28
53
  /** Inline text content (e.g. terminal output) — no upload needed */
29
54
  textContent?: string;
55
+ /**
56
+ * Base64 payload for an image, sent as part of the message itself.
57
+ *
58
+ * Passing the path instead would make the model spend a whole extra round trip calling Read
59
+ * to fetch it, and every round trip re-sends the entire transcript. The uploaded copy is
60
+ * still kept, so removing the payload from a transcript later stays recoverable.
61
+ */
62
+ imageData?: { data: string; mediaType: string };
63
+ /** Dimensions before and after downscaling, when one happened — surfaced in the chip. */
64
+ resized?: { from: { width: number; height: number }; to: { width: number; height: number } };
30
65
  status: "uploading" | "ready" | "error";
31
66
  }
32
67
 
@@ -436,6 +471,27 @@ export const MessageInput = memo(function MessageInput({
436
471
  onExternalPathsConsumed?.();
437
472
  }, [externalPaths]); // eslint-disable-line react-hooks/exhaustive-deps
438
473
 
474
+ /** Delete an upload the user removed before sending. Best effort — nothing depends on it. */
475
+ const discardUpload = useCallback(
476
+ async (serverPath: string) => {
477
+ if (!projectName) return;
478
+ const filename = serverPath.split(/[\\/]/).pop();
479
+ if (!filename) return;
480
+ try {
481
+ const headers: HeadersInit = {};
482
+ const token = getAuthToken();
483
+ if (token) headers["Authorization"] = `Bearer ${token}`;
484
+ await fetch(`${projectUrl(projectName)}/chat/uploads/${encodeURIComponent(filename)}`, {
485
+ method: "DELETE",
486
+ headers,
487
+ });
488
+ } catch {
489
+ // An orphaned file is not worth surfacing to the user.
490
+ }
491
+ },
492
+ [projectName],
493
+ );
494
+
439
495
  /** Upload a single file to the server, return server path */
440
496
  const uploadFile = useCallback(
441
497
  async (file: File): Promise<string | null> => {
@@ -466,32 +522,64 @@ export const MessageInput = memo(function MessageInput({
466
522
  /** Process files — always uploads to server. Path resolution only happens via @ picker. */
467
523
  const processFiles = useCallback(
468
524
  async (files: File[]) => {
469
- for (const file of files) {
525
+ for (const original of files) {
470
526
  const id = randomId();
471
- const isImg = isImageFile(file);
472
- const previewUrl = isImg ? URL.createObjectURL(file) : undefined;
527
+ const isImg = isImageFile(original);
473
528
 
474
529
  const att: ChatAttachment = {
475
530
  id,
476
- name: file.name,
477
- file,
531
+ name: original.name,
532
+ file: original,
478
533
  isImage: isImg,
479
- previewUrl,
534
+ previewUrl: isImg ? URL.createObjectURL(original) : undefined,
480
535
  status: "uploading",
481
536
  };
482
537
 
483
538
  setAttachments((prev) => [...prev, att]);
484
539
 
485
- // Upload in background
486
- uploadFile(file).then((serverPath) => {
540
+ // Shrink first, so the upload, the preview and the message all carry the same
541
+ // already-reduced image — an oversized original must not reach the transcript.
542
+ //
543
+ // Only a `scaled` or `inlineable` result is attached inline. An image that could not
544
+ // be measured or re-encoded travels by path alone: sending unverified dimensions
545
+ // inline risks a payload the API refuses, and that refusal outlives the turn, since
546
+ // the transcript replays it into every later one.
547
+ try {
548
+ const outcome = isImg ? await downscaleImage(original) : null;
549
+ const file = outcome?.file ?? original;
550
+ // Point the thumbnail at the reduced file and let the original go. It is the larger
551
+ // of the two and nothing needs it once the resize is done, but it stayed alive until
552
+ // the message was sent because the preview still referenced it.
553
+ const previewUrl = isImg && file !== original ? URL.createObjectURL(file) : undefined;
554
+ const serverPath = await uploadFile(file);
555
+ const inlineable = outcome ? outcome.kind !== "asis" : false;
556
+ const imageData = inlineable ? await readImageData(file) : undefined;
557
+ const withinPayloadCap =
558
+ !!imageData && imageData.data.length <= INLINE_IMAGE_LIMITS.maxBase64PerImage;
559
+
560
+ if (previewUrl && att.previewUrl) URL.revokeObjectURL(att.previewUrl);
487
561
  setAttachments((prev) =>
488
562
  prev.map((a) =>
489
563
  a.id === id
490
- ? { ...a, serverPath: serverPath ?? undefined, status: serverPath ? "ready" : "error" }
564
+ ? {
565
+ ...a,
566
+ file,
567
+ ...(previewUrl && { previewUrl }),
568
+ serverPath: serverPath ?? undefined,
569
+ imageData: withinPayloadCap ? imageData : undefined,
570
+ resized: outcome?.to ? { from: outcome.from!, to: outcome.to } : undefined,
571
+ // An image can still be sent inline when the upload failed; anything
572
+ // else has nothing left to send without its path.
573
+ status: serverPath || withinPayloadCap ? "ready" : "error",
574
+ }
491
575
  : a,
492
576
  ),
493
577
  );
494
- });
578
+ } catch {
579
+ // Nothing here may leave an attachment stuck on "uploading" — both the send button
580
+ // and the auto-send effect wait on that state, so a swallowed throw hangs the composer.
581
+ setAttachments((prev) => prev.map((a) => (a.id === id ? { ...a, status: "error" } : a)));
582
+ }
495
583
  }
496
584
  (mobileTextareaRef.current ?? textareaRef.current)?.focus();
497
585
  },
@@ -502,9 +590,12 @@ export const MessageInput = memo(function MessageInput({
502
590
  setAttachments((prev) => {
503
591
  const att = prev.find((a) => a.id === id);
504
592
  if (att?.previewUrl) URL.revokeObjectURL(att.previewUrl);
593
+ // Uploads are never swept, because chat history points at them indefinitely, so one
594
+ // abandoned before it was ever sent would sit there with nothing referencing it.
595
+ if (att?.serverPath) void discardUpload(att.serverPath);
505
596
  return prev.filter((a) => a.id !== id);
506
597
  });
507
- }, []);
598
+ }, [discardUpload]);
508
599
 
509
600
  /** Execute the actual send (called directly or after uploads complete) */
510
601
  const executeSend = useCallback(() => {