@hienlh/ppm 0.19.1 → 0.19.3

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 (681) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/CLAUDE.md +1 -0
  3. package/assets/skills/ppm/SKILL.md +1 -1
  4. package/assets/skills/ppm/references/cli-reference.md +4 -4
  5. package/assets/skills/ppm/references/http-api.md +3 -2
  6. package/dist/web/assets/accounts-settings-section-LtSVxNNp.js +1 -0
  7. package/dist/web/assets/ai-resource-editor-BqFT1EUB.js +1 -0
  8. package/dist/web/assets/ai-settings-section-CY6spbEx.js +1 -0
  9. package/dist/web/assets/ai-settings-section-Po3K8j-g.js +1 -0
  10. package/dist/web/assets/{angular-html-VzxSzTCy.js → angular-html-BuHNQIbQ.js} +1 -1
  11. package/dist/web/assets/{angular-ts-CmJ4bmzM.js → angular-ts-DHRWoLc-.js} +1 -1
  12. package/dist/web/assets/api-client-BogqsFna.js +1 -0
  13. package/dist/web/assets/{api-client-qRWu6_s5.js → api-client-oY1tC3tM.js} +1 -1
  14. package/dist/web/assets/api-settings-CTMdz_VF.js +1 -0
  15. package/dist/web/assets/{api-themes-DsAp8m7m.js → api-themes-Cknp3Q-P.js} +1 -1
  16. package/dist/web/assets/{apl-do17bnVx.js → apl-C1VSWVei.js} +1 -1
  17. package/dist/web/assets/appearance-settings-section-L9Z-nej_.js +1 -0
  18. package/dist/web/assets/architecture-PBZL5I3N-Ca5IpdzP.js +1 -0
  19. package/dist/web/assets/arrow-left-CyFcsyzD.js +1 -0
  20. package/dist/web/assets/{astro-ClY0e0OA.js → astro-RX-rChsZ.js} +1 -1
  21. package/dist/web/assets/{audio-preview-Fw935Mok.js → audio-preview-CCd1jdje.js} +1 -1
  22. package/dist/web/assets/{blade-BXSdXDGq.js → blade-CZ6bxMbK.js} +1 -1
  23. package/dist/web/assets/{bot-CWZAuloY.js → bot-D6sx11y0.js} +1 -1
  24. package/dist/web/assets/{pencil-CHKLOgLs.js → brain-KDa9jFRq.js} +1 -1
  25. package/dist/web/assets/{bsl-MxsMKDUZ.js → bsl-BhgCXLWk.js} +1 -1
  26. package/dist/web/assets/{button-CrwgGd1y.js → button-BZtEJcUv.js} +1 -1
  27. package/dist/web/assets/{cairo-4CIrzX_p.js → cairo-BrwygPkn.js} +1 -1
  28. package/dist/web/assets/calendar-clock-C1asT-gk.js +1 -0
  29. package/dist/web/assets/chat-tab-BIO-VhWT.js +15 -0
  30. package/dist/web/assets/check-DN75Ucvz.js +1 -0
  31. package/dist/web/assets/{chevron-down-D7CbB5f6.js → chevron-down-Dp0rpHb8.js} +1 -1
  32. package/dist/web/assets/chevron-right-De-Wz5Lz.js +1 -0
  33. package/dist/web/assets/chevron-up-8PbsNiJp.js +1 -0
  34. package/dist/web/assets/circle-BCy0AUCo.js +1 -0
  35. package/dist/web/assets/{circle-question-mark-BBzFZrNh.js → circle-question-mark-CzR7YRc-.js} +1 -1
  36. package/dist/web/assets/clock-C1CQUQPZ.js +1 -0
  37. package/dist/web/assets/{cobol-phPOE89d.js → cobol-C5TGW1Ir.js} +1 -1
  38. package/dist/web/assets/code-DJgYrFzm.js +1 -0
  39. package/dist/web/assets/code-editor-B9lVjElM.js +7 -0
  40. package/dist/web/assets/{coffee-Cjuukfz3.js → coffee-CFgKlmgI.js} +1 -1
  41. package/dist/web/assets/{conflict-editor-Ni0PPsKu.js → conflict-editor-BoRPwL3L.js} +1 -1
  42. package/dist/web/assets/context-menu-C13b1uT3.js +1 -0
  43. package/dist/web/assets/{copy-CbPdNp7E.js → copy-BewHj66E.js} +1 -1
  44. package/dist/web/assets/{cpp-CT7tGBC3.js → cpp-BhG5q3-d.js} +1 -1
  45. package/dist/web/assets/{createLucideIcon-ConhwJnb.js → createLucideIcon-D8pyde1A.js} +1 -1
  46. package/dist/web/assets/{crystal-D8U1Qnqh.js → crystal-DvuI7_tY.js} +1 -1
  47. package/dist/web/assets/{csv-preview-Coc2oed1.js → csv-preview-BpoJqZQi.js} +1 -1
  48. package/dist/web/assets/{data-grid-overlay-editor-B8WQkZDB.js → data-grid-overlay-editor-DP1xkog4.js} +1 -1
  49. package/dist/web/assets/{data-grid-types-KpjaKAci.js → data-grid-types-CdAg-4W1.js} +1 -1
  50. package/dist/web/assets/{database-BqyeGogo.js → database-Y3ESDK4-.js} +1 -1
  51. package/dist/web/assets/database-viewer-I-9cER1L.js +1 -0
  52. package/dist/web/assets/diff-viewer-_xHZsxvr.js +4 -0
  53. package/dist/web/assets/{dist-C2_dKAa_.js → dist-DonmFWG8.js} +1 -1
  54. package/dist/web/assets/{docx-preview-76KDw1eE.js → docx-preview-CWIDVNZn.js} +1 -1
  55. package/dist/web/assets/{download-DAF7SXcf.js → download-DG3_9xXy.js} +1 -1
  56. package/dist/web/assets/{edge-Db4ze6bw.js → edge-BSIEsmlI.js} +1 -1
  57. package/dist/web/assets/{edit-diff-preview-DyC_-4My.js → edit-diff-preview-DeHb85DN.js} +1 -1
  58. package/dist/web/assets/{elixir-RZPZ0472.js → elixir-BF-lrGXM.js} +1 -1
  59. package/dist/web/assets/{elm-DNZcQt_7.js → elm-8mCsJOeN.js} +1 -1
  60. package/dist/web/assets/{erb-DRONsZXs.js → erb-Dw5PUVKa.js} +1 -1
  61. package/dist/web/assets/{erlang-DeG0-QwJ.js → erlang-BvjBIA6O.js} +1 -1
  62. package/dist/web/assets/{esm-CZy-sSoK.js → esm-Bt9OwSnU.js} +1 -1
  63. package/dist/web/assets/explorer-body-DapwsOnp.js +2 -0
  64. package/dist/web/assets/explorer-window-content-DD6Z15pm.js +1 -0
  65. package/dist/web/assets/extension-manager-section-Cu9D9Udt.js +1 -0
  66. package/dist/web/assets/{extension-webview-BW5GuBnH.js → extension-webview-Diz2OzVq.js} +1 -1
  67. package/dist/web/assets/eye-gWwaF4Lg.js +1 -0
  68. package/dist/web/assets/eye-off-GjXVuAOn.js +1 -0
  69. package/dist/web/assets/{file-exclamation-point-Bpg-zDPP.js → file-exclamation-point-JzhTU2TX.js} +1 -1
  70. package/dist/web/assets/files-settings-section-B1e7BgKQ.js +1 -0
  71. package/dist/web/assets/flask-conical-tp8gyZGh.js +1 -0
  72. package/dist/web/assets/{folder-symlink-LD-mr54V.js → folder-symlink-fQRoG_rV.js} +1 -1
  73. package/dist/web/assets/{fortran-fixed-form-CK5VRJNd.js → fortran-fixed-form-CRjUJe9K.js} +1 -1
  74. package/dist/web/assets/{fsharp-TVVgzHWp.js → fsharp-CjUIHIHp.js} +1 -1
  75. package/dist/web/assets/{gdresource-DiUcyewI.js → gdresource-x_NUQFrZ.js} +1 -1
  76. package/dist/web/assets/general-settings-section-DWdHHRDy.js +1 -0
  77. package/dist/web/assets/{git-commit-CfOruFrg.js → git-commit-BOpxSbns.js} +1 -1
  78. package/dist/web/assets/git-log-panel-CMMsvU64.js +1 -0
  79. package/dist/web/assets/{git-rebase-Bxyr24oc.js → git-rebase-BW4xlK7k.js} +1 -1
  80. package/dist/web/assets/gitGraph-HDMCJU4V-BloZfB0Q.js +1 -0
  81. package/dist/web/assets/glide-data-grid-pKQuyYFj.js +141 -0
  82. package/dist/web/assets/{glimmer-js-CT5aJxz_.js → glimmer-js-CT9jrmFj.js} +1 -1
  83. package/dist/web/assets/{glimmer-ts-CRjP9Yey.js → glimmer-ts-CjQT1lxK.js} +1 -1
  84. package/dist/web/assets/{glsl-BX2--oI_.js → glsl-DsFJvtg5.js} +1 -1
  85. package/dist/web/assets/{graphql-BIQsvBxQ.js → graphql-Dhlnmkn_.js} +1 -1
  86. package/dist/web/assets/group-chat-tab-U5u0Loii.js +1 -0
  87. package/dist/web/assets/{hack-CTWk0k1m.js → hack-BKHbsJ7u.js} +1 -1
  88. package/dist/web/assets/{haml-DGh27hXc.js → haml-C1Ih25Y2.js} +1 -1
  89. package/dist/web/assets/{hand-DKSxDa5c.js → hand-x0ejq3fS.js} +1 -1
  90. package/dist/web/assets/{handlebars-BJL1jNrG.js → handlebars-CaFitvmC.js} +1 -1
  91. package/dist/web/assets/{html-DALyDNqU.js → html-BwpKIE6V.js} +1 -1
  92. package/dist/web/assets/{html-derivative-B4XcU57e.js → html-derivative-uuTEquZU.js} +1 -1
  93. package/dist/web/assets/{http-DfMo7nNu.js → http-Cv4Pg5cR.js} +1 -1
  94. package/dist/web/assets/{hurl-CGc_n2Qf.js → hurl-DubkI6TG.js} +1 -1
  95. package/dist/web/assets/{hxml-40zZ1t4x.js → hxml-B9OVZFAB.js} +1 -1
  96. package/dist/web/assets/{image-preview-Cp3XB8UO.js → image-preview-DRbZ0FA2.js} +1 -1
  97. package/dist/web/assets/index--KWVW-Ga.js +52 -0
  98. package/dist/web/assets/index-CvGx7wBQ.css +2 -0
  99. package/dist/web/assets/info-3K5VOQVL-c0W8wp5o.js +1 -0
  100. package/dist/web/assets/{jinja-D5zuIn9u.js → jinja-BHb1jyl6.js} +1 -1
  101. package/dist/web/assets/jira-watcher-section-uA5oyB8s.js +1 -0
  102. package/dist/web/assets/{jison-Dxut9QwA.js → jison-CtpfXDzp.js} +1 -1
  103. package/dist/web/assets/{julia-Cu85tTPo.js → julia-TYjgpazU.js} +1 -1
  104. package/dist/web/assets/{just-CctM4uLa.js → just-D27bzZuW.js} +1 -1
  105. package/dist/web/assets/key-round-CQpDrDxg.js +1 -0
  106. package/dist/web/assets/keybindings-store-Ccf0etMJ.js +1 -0
  107. package/dist/web/assets/{keyboard-AdwmYDSu.js → keyboard-DiOYrs6C.js} +1 -1
  108. package/dist/web/assets/keyboard-shortcuts-section-B7IxJHiI.js +1 -0
  109. package/dist/web/assets/{latex-kpwS_CC7.js → latex-D_MzopTn.js} +1 -1
  110. package/dist/web/assets/{lib-Ca7ai8xa.js → lib-xrSJ4EZX.js} +1 -1
  111. package/dist/web/assets/{liquid-B8Rr0xue.js → liquid-BG_FG0Pe.js} +1 -1
  112. package/dist/web/assets/loader-circle-DPMrnLQ2.js +1 -0
  113. package/dist/web/assets/{lua-C_3gixwM.js → lua-Bl29oC2L.js} +1 -1
  114. package/dist/web/assets/markdown-renderer-CY1epGRc.js +1 -0
  115. package/dist/web/assets/markdown-renderer-DDcIGQEd.js +3 -0
  116. package/dist/web/assets/{marko-B28rvNL-.js → marko-D-HI7dbm.js} +1 -1
  117. package/dist/web/assets/{mdc-DQ6-STFO.js → mdc-y444JQTQ.js} +1 -1
  118. package/dist/web/assets/{media-url-BjvbfTTl.js → media-url-VzecHk9g.js} +1 -1
  119. package/dist/web/assets/{mobile-bottom-sheet-DXn5g2LB.js → mobile-bottom-sheet-BD5b7KJF.js} +1 -1
  120. package/dist/web/assets/mobile-explorer-sheet-CqkwbCRl.js +1 -0
  121. package/dist/web/assets/{monitor-CXI_XpWJ.js → monitor-C835ucWH.js} +1 -1
  122. package/dist/web/assets/{nextflow-B1xq2O8L.js → nextflow-C5sKcldC.js} +1 -1
  123. package/dist/web/assets/{nginx-xsLxRGxO.js → nginx-bXnEC3wm.js} +1 -1
  124. package/dist/web/assets/{nim-wqcek3WO.js → nim-pevhf4WD.js} +1 -1
  125. package/dist/web/assets/notification-store-BNSYk3E7.js +1 -0
  126. package/dist/web/assets/notifications-settings-section-v_628h4B.js +1 -0
  127. package/dist/web/assets/{number-overlay-editor-1c4sgZJt.js → number-overlay-editor-B7WINIW4.js} +1 -1
  128. package/dist/web/assets/{overview-panel-Cr2xsHc6.js → overview-panel-mhN0qw6Q.js} +1 -1
  129. package/dist/web/assets/packet-RMMSAZCW-D_BoHr_M.js +1 -0
  130. package/dist/web/assets/panel-store-BRrnDO_i.js +1 -0
  131. package/dist/web/assets/pdf-preview-CI3q1MnH.js +1 -0
  132. package/dist/web/assets/{perl-BH54gN9q.js → perl-B1KUtMnW.js} +1 -1
  133. package/dist/web/assets/{php-Br5ZrEZm.js → php-BGmVZBAJ.js} +1 -1
  134. package/dist/web/assets/pie-UPGHQEXC-BaLfuEf8.js +1 -0
  135. package/dist/web/assets/portal-container-context-a399Hd9F.js +1 -0
  136. package/dist/web/assets/{postgres-viewer-BJPr_FYL.js → postgres-viewer-jaR0FYJ_.js} +3 -3
  137. package/dist/web/assets/power-CriQkeK5.js +1 -0
  138. package/dist/web/assets/ppmbot-settings-section-BET4VmKb.js +1 -0
  139. package/dist/web/assets/{process-table-BqTcAJ-h.js → process-table-D1r-Gupi.js} +1 -1
  140. package/dist/web/assets/{project-store-KHjFTCxf.js → project-store-BuY7rxn_.js} +1 -1
  141. package/dist/web/assets/proxy-settings-section-0DvNh1B-.js +3 -0
  142. package/dist/web/assets/{pug-8VCubZ3H.js → pug-BfN8CpEH.js} +1 -1
  143. package/dist/web/assets/{qml-D8AF1RZ6.js → qml-D6fRdLZt.js} +1 -1
  144. package/dist/web/assets/query-audit-section-BA5rT6Sh.js +1 -0
  145. package/dist/web/assets/radar-KQ55EAFF-DBIZEaIU.js +1 -0
  146. package/dist/web/assets/{razor-6v042IJW.js → razor-fg-9AScR.js} +1 -1
  147. package/dist/web/assets/{react-DHs6efqI.js → react-X4jUntEf.js} +1 -1
  148. package/dist/web/assets/remote-desktop-mobile-sheet-CPa3XElX.js +2 -0
  149. package/dist/web/assets/remote-desktop-mobile-view-De_a4skd.js +1 -0
  150. package/dist/web/assets/remote-desktop-readiness-gate-CPrDIv2d.js +1 -0
  151. package/dist/web/assets/remote-desktop-window-content-CG1StfCE.js +1 -0
  152. package/dist/web/assets/{rst-mTGFo4AQ.js → rst-BZgx076b.js} +1 -1
  153. package/dist/web/assets/{ruby-BBaOP636.js → ruby-iUkWDcLo.js} +1 -1
  154. package/dist/web/assets/{run-in-terminal-Zl5iIx-B.js → run-in-terminal-bKRwuO2z.js} +1 -1
  155. package/dist/web/assets/{sas-D5QEkIH2.js → sas-CLXO0lZ6.js} +1 -1
  156. package/dist/web/assets/{save-Dxq-Zepc.js → save-CipGrG1k.js} +1 -1
  157. package/dist/web/assets/schedules-settings-section-B2f7V7Xr.js +1 -0
  158. package/dist/web/assets/{scss-C5Mu6y2u.js → scss-DQqteGaG.js} +1 -1
  159. package/dist/web/assets/send-Bb0jtcR6.js +1 -0
  160. package/dist/web/assets/{send-to-chat-Cv7ikFUC.js → send-to-chat-S8LCNX7V.js} +1 -1
  161. package/dist/web/assets/separator-DqGg3f9c.js +1 -0
  162. package/dist/web/assets/settings-body-omv04jbb.js +2 -0
  163. package/dist/web/assets/settings-store-Brjd0QnO.js +2 -0
  164. package/dist/web/assets/settings-tab-NdnBTqyj.js +1 -0
  165. package/dist/web/assets/settings-window-content-Chou1JGa.js +1 -0
  166. package/dist/web/assets/{shaderlab-mLnpMpN3.js → shaderlab-CeMTqagR.js} +1 -1
  167. package/dist/web/assets/{shellsession-qdcDfUuk.js → shellsession-BAkquZAV.js} +1 -1
  168. package/dist/web/assets/{shield-check-CH7F1P7m.js → shield-check-BsG3LV8i.js} +1 -1
  169. package/dist/web/assets/{shield-off-C9HiF0-6.js → shield-off-7jKWpJBD.js} +1 -1
  170. package/dist/web/assets/{soy-CGGf1G43.js → soy-Bxm9Ez55.js} +1 -1
  171. package/dist/web/assets/{sparkles-BnuLH6Ck.js → sparkles-Dh-_J2Rd.js} +1 -1
  172. package/dist/web/assets/{sparql-tQHB50VD.js → sparql-C1phwdLc.js} +1 -1
  173. package/dist/web/assets/sql-query-editor-Dk9y3jqy.js +1 -0
  174. package/dist/web/assets/sqlite-viewer-4xFhkLYy.js +1 -0
  175. package/dist/web/assets/{stata-DVQNLMcR.js → stata-DGNMQdEb.js} +1 -1
  176. package/dist/web/assets/{surrealql-lu576tBi.js → surrealql-ByVrV320.js} +1 -1
  177. package/dist/web/assets/{svelte-CSCrOBjT.js → svelte-CNWjIMhl.js} +1 -1
  178. package/dist/web/assets/system-monitor-body-B_1iMVOI.js +2 -0
  179. package/dist/web/assets/system-monitor-tab-B2j9mWBW.js +1 -0
  180. package/dist/web/assets/system-monitor-window-content-BOjq53uU.js +1 -0
  181. package/dist/web/assets/{tab-host-window-content-C5kYP0-z.js → tab-host-window-content-CW_dlYlW.js} +1 -1
  182. package/dist/web/assets/{tab-store-DS3YlUwR.js → tab-store-PZsP5ZwG.js} +1 -1
  183. package/dist/web/assets/{table-BXDyImqk.js → table-CRJsmfeg.js} +1 -1
  184. package/dist/web/assets/team-member-sheet-CkR4qO7V.js +2 -0
  185. package/dist/web/assets/team-member-window-content-CmF5ia3n.js +1 -0
  186. package/dist/web/assets/{templ-CPt8AS4Q.js → templ-Cf0gQL4I.js} +1 -1
  187. package/dist/web/assets/{terminal-tab-8bKQ3CWR.js → terminal-tab-HXZkicIc.js} +3 -3
  188. package/dist/web/assets/{tex-BrTByT7i.js → tex-BTIqPw_R.js} +1 -1
  189. package/dist/web/assets/{text-wrap-CzMjB6GD.js → text-wrap-oiekm5SB.js} +1 -1
  190. package/dist/web/assets/tool-cards-jA1BTLZj.js +9 -0
  191. package/dist/web/assets/treemap-KZPCXAKY-DFiG9UYW.js +1 -0
  192. package/dist/web/assets/{ts-tags-DSqEZ7A8.js → ts-tags-B8dN_Yf6.js} +1 -1
  193. package/dist/web/assets/{twig-DW9qc1bf.js → twig-BgemonbJ.js} +1 -1
  194. package/dist/web/assets/use-accounts-data-BI_5qNvr.js +1 -0
  195. package/dist/web/assets/{use-blob-url-DKbV7PM_.js → use-blob-url-Gfi69PtP.js} +1 -1
  196. package/dist/web/assets/{use-coarse-long-press-DGyC48NF.js → use-coarse-long-press-BmI_GVW5.js} +1 -1
  197. package/dist/web/assets/{use-is-mobile-ClE-UcPF.js → use-is-mobile-CYW3FJFE.js} +1 -1
  198. package/dist/web/assets/use-monaco-theme-BcQqKVbE.js +1 -0
  199. package/dist/web/assets/use-open-team-member-CruUTqzD.js +1 -0
  200. package/dist/web/assets/use-remote-desktop-display-choice-DY5BWRTo.js +1 -0
  201. package/dist/web/assets/{use-visual-viewport-CCX21c9w.js → use-visual-viewport-rnvuooQZ.js} +1 -1
  202. package/dist/web/assets/{use-websocket-CWpG7PWN.js → use-websocket-DetaY8OI.js} +1 -1
  203. package/dist/web/assets/vendor-markdown-Ls5AcU91.js +291 -0
  204. package/dist/web/assets/{vendor-mermaid-0Nt-ew76.js → vendor-mermaid-BKWscKt0.js} +1 -1
  205. package/dist/web/assets/{vendor-ui-CXJtBULr.js → vendor-ui-CaeeFO0B.js} +1 -1
  206. package/dist/web/assets/video-preview-C0uH2228.js +1 -0
  207. package/dist/web/assets/{vue-CJNDL1g-.js → vue-D3D4lbXz.js} +1 -1
  208. package/dist/web/assets/{vue-html-CG5BKtq1.js → vue-html-DYEQFAiK.js} +1 -1
  209. package/dist/web/assets/{vue-vine-Dd5HZ7T5.js → vue-vine-D0OPNscN.js} +1 -1
  210. package/dist/web/assets/{window-pip-registry-9H6Y-HqR.js → window-pip-registry-BEN0QNSW.js} +1 -1
  211. package/dist/web/assets/window-store-DGKf9rPG.js +1 -0
  212. package/dist/web/assets/ws-auth-Dawx17PM.js +1 -0
  213. package/dist/web/assets/x-YDu4m2Iq.js +1 -0
  214. package/dist/web/assets/{xml-DTOm5niz.js → xml-DHaWgpya.js} +1 -1
  215. package/dist/web/assets/{xsl-BD_kBC04.js → xsl-DVRN8JsA.js} +1 -1
  216. package/dist/web/index.html +42 -65
  217. package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
  218. package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
  219. package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
  220. package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
  221. package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
  222. package/dist/web/sw.js +1 -1
  223. package/docs/codebase-summary.md +9 -5
  224. package/docs/lessons-learned.md +11 -0
  225. package/package.json +2 -2
  226. package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
  227. package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
  228. package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
  229. package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
  230. package/scripts/check-ppm-dir-usage.sh +0 -0
  231. package/scripts/install.sh +0 -0
  232. package/scripts/release.sh +0 -0
  233. package/src/providers/claude-agent-sdk.ts +51 -43
  234. package/src/providers/codex-app-server/codex-event-mapper.ts +70 -5
  235. package/src/providers/codex-app-server/codex-history.ts +24 -0
  236. package/src/providers/codex-app-server/codex-jsonrpc-client.ts +39 -2
  237. package/src/providers/codex-app-server/codex-protocol.ts +24 -0
  238. package/src/providers/codex-app-server/codex-provider.ts +166 -18
  239. package/src/providers/codex-app-server/codex-rollout-items.ts +149 -0
  240. package/src/providers/codex-app-server/codex-skill-parser.ts +63 -0
  241. package/src/providers/codex-app-server/codex-usage-fetch.ts +22 -20
  242. package/src/providers/codex-app-server/codex-usage-source.ts +37 -0
  243. package/src/server/routes/chat.ts +95 -12
  244. package/src/server/routes/codex-accounts.ts +9 -5
  245. package/src/server/ws/chat.ts +55 -5
  246. package/src/services/claude-usage-source.ts +40 -0
  247. package/src/services/claude-usage.service.ts +77 -146
  248. package/src/services/codex-account-login.ts +123 -55
  249. package/src/services/codex-account.service.ts +27 -4
  250. package/src/services/db.service.ts +161 -20
  251. package/src/services/edge-forwarder.ts +191 -191
  252. package/src/services/fs-credential-path-guard.ts +35 -8
  253. package/src/services/fs-path-guard.service.ts +1 -0
  254. package/src/services/named-tunnel/named-tunnel-retry.ts +26 -0
  255. package/src/services/named-tunnel/network-ready.ts +63 -0
  256. package/src/services/postgres.service.ts +144 -72
  257. package/src/services/provider-usage/index.ts +63 -0
  258. package/src/services/provider-usage/register-usage-sources.ts +18 -0
  259. package/src/services/provider-usage/usage-memory-cache.ts +71 -0
  260. package/src/services/provider-usage/usage-registry.ts +157 -0
  261. package/src/services/provider-usage/usage-scheduler.ts +117 -0
  262. package/src/services/provider-usage/usage-snapshot-store.ts +126 -0
  263. package/src/services/provider-usage/usage-source.ts +82 -0
  264. package/src/services/slash-discovery/builtin-commands.ts +13 -0
  265. package/src/services/slash-discovery/codex-skill-items.ts +57 -0
  266. package/src/services/slash-discovery/provider-skill-sigil.ts +19 -0
  267. package/src/services/slash-discovery/types.ts +19 -0
  268. package/src/services/supervisor.ts +98 -0
  269. package/src/services/transcript-images.ts +14 -7
  270. package/src/services/upgrade.service.ts +7 -2
  271. package/src/services/windows-process-tree.ts +351 -351
  272. package/src/shared/image-limits.ts +37 -0
  273. package/src/types/api.ts +3 -1
  274. package/src/types/chat.ts +13 -1
  275. package/src/types/extension-messages.ts +2 -0
  276. package/src/web/app.tsx +1 -7
  277. package/src/web/components/chat/attachment-chips.tsx +10 -0
  278. package/src/web/components/chat/chat-history-bar.tsx +10 -1
  279. package/src/web/components/chat/chat-tab.tsx +54 -14
  280. package/src/web/components/chat/message-input.tsx +113 -15
  281. package/src/web/components/chat/message-list.tsx +14 -0
  282. package/src/web/components/chat/session-debug-dialog.tsx +2 -1
  283. package/src/web/components/chat/slash-command-picker.tsx +22 -3
  284. package/src/web/components/chat/tool-cards.tsx +47 -4
  285. package/src/web/components/chat/usage-badge.tsx +43 -447
  286. package/src/web/components/chat/user-message-parse.ts +33 -11
  287. package/src/web/components/database/database-viewer.tsx +34 -11
  288. package/src/web/components/database/extract-query-table.ts +24 -0
  289. package/src/web/components/database/glide-data-grid.tsx +11 -4
  290. package/src/web/components/database/glide-grid-types.ts +4 -0
  291. package/src/web/components/database/split-sql-statements.ts +116 -0
  292. package/src/web/components/database/sql-completion-provider.ts +1 -26
  293. package/src/web/components/database/sql-query-editor.tsx +2 -1
  294. package/src/web/components/database/use-database.ts +16 -1
  295. package/src/web/components/database/use-glide-cell-content.ts +11 -6
  296. package/src/web/components/editor/code-editor.tsx +30 -56
  297. package/src/web/components/floating-window/window-content-registry.ts +2 -0
  298. package/src/web/components/floating-window/window-store-types.ts +1 -1
  299. package/src/web/components/layout/command-palette.tsx +2 -2
  300. package/src/web/components/layout/mobile-drawer.tsx +5 -4
  301. package/src/web/components/layout/mobile-nav.tsx +5 -1
  302. package/src/web/components/layout/nav-section-rail.tsx +2 -1
  303. package/src/web/components/layout/project-bottom-sheet.tsx +3 -9
  304. package/src/web/components/layout/sidebar.tsx +0 -2
  305. package/src/web/components/layout/tab-pop-out-menu-item.tsx +3 -2
  306. package/src/web/components/settings/accounts/account-add-dialog.tsx +155 -0
  307. package/src/web/components/settings/accounts/account-backup-format.ts +19 -0
  308. package/src/web/components/settings/accounts/account-bucket-row.tsx +55 -0
  309. package/src/web/components/settings/accounts/account-card.tsx +127 -0
  310. package/src/web/components/settings/accounts/account-delete-confirm.tsx +62 -0
  311. package/src/web/components/settings/accounts/account-export-dialog.tsx +154 -0
  312. package/src/web/components/settings/accounts/account-export-simulation.tsx +196 -0
  313. package/src/web/components/settings/accounts/account-import-dialog.tsx +72 -0
  314. package/src/web/components/settings/accounts/account-profile-panel.tsx +52 -0
  315. package/src/web/components/{chat → settings/accounts}/account-rotation-settings.tsx +1 -1
  316. package/src/web/components/settings/accounts/account-token-test-dialog.tsx +147 -0
  317. package/src/web/components/settings/accounts/account-usage-format.ts +92 -0
  318. package/src/web/components/{chat/usage-pattern-chart.tsx → settings/accounts/account-usage-pattern-chart.tsx} +1 -1
  319. package/src/web/components/settings/accounts/accounts-pane-header.tsx +93 -0
  320. package/src/web/components/settings/accounts/accounts-settings-section.tsx +80 -0
  321. package/src/web/components/settings/accounts/claude-accounts-section.tsx +168 -0
  322. package/src/web/components/settings/accounts/codex-accounts-section.tsx +137 -0
  323. package/src/web/components/settings/accounts/codex-add-account-dialog.tsx +130 -0
  324. package/src/web/components/settings/accounts/codex-backup-dialog.tsx +101 -0
  325. package/src/web/components/settings/accounts/codex-rotation-dialog.tsx +65 -0
  326. package/src/web/components/settings/accounts/use-accounts-data.ts +81 -0
  327. package/src/web/components/settings/accounts/use-codex-accounts.ts +151 -0
  328. package/src/web/components/settings/ai-settings-section.tsx +14 -4
  329. package/src/web/components/settings/appearance-settings-section.tsx +125 -0
  330. package/src/web/components/settings/general-settings-section.tsx +87 -0
  331. package/src/web/components/settings/jira-watcher-section.tsx +34 -0
  332. package/src/web/components/settings/notifications-settings-section.tsx +37 -0
  333. package/src/web/components/settings/open-settings.ts +42 -0
  334. package/src/web/components/settings/resolve-open-settings-action.ts +45 -0
  335. package/src/web/components/settings/settings-body.tsx +110 -0
  336. package/src/web/components/settings/settings-categories.ts +102 -0
  337. package/src/web/components/settings/settings-category-list.tsx +56 -0
  338. package/src/web/components/settings/settings-category-rail.tsx +62 -0
  339. package/src/web/components/settings/settings-section-content.tsx +43 -0
  340. package/src/web/components/settings/settings-tab.tsx +15 -329
  341. package/src/web/components/settings/settings-window-content.tsx +24 -0
  342. package/src/web/hooks/use-chat.ts +44 -4
  343. package/src/web/hooks/use-global-keybindings.ts +3 -4
  344. package/src/web/hooks/use-url-sync.ts +30 -0
  345. package/src/web/hooks/use-usage.ts +23 -11
  346. package/src/web/lib/attachment-marker-split.ts +44 -0
  347. package/src/web/lib/image-animation.ts +51 -0
  348. package/src/web/lib/image-resize-limits.ts +47 -0
  349. package/src/web/lib/image-resize.ts +133 -0
  350. package/src/web/lib/sidebar-tabs/tab-registry.ts +9 -9
  351. package/src/web/lib/slash-items-cache.ts +27 -7
  352. package/src/web/stores/panel-utils.ts +11 -0
  353. package/src/web/stores/settings-store.ts +4 -2
  354. package/src/web/stores/window-panel-actions.ts +1 -3
  355. package/src/web/stores/window-panel-persistence.ts +9 -4
  356. package/dist/web/assets/adaptive-context-menu-JfuUSqbe.js +0 -1
  357. package/dist/web/assets/ai-resource-editor-MfdJKVM5.js +0 -1
  358. package/dist/web/assets/ai-settings-section-CJE7jFsj.js +0 -1
  359. package/dist/web/assets/api-client-DLWwuUkJ.js +0 -1
  360. package/dist/web/assets/api-settings-BFuaZrNC.js +0 -1
  361. package/dist/web/assets/api-settings-CPg4yGvS.js +0 -1
  362. package/dist/web/assets/architecture-PBZL5I3N-6konx-J1.js +0 -1
  363. package/dist/web/assets/chat-tab-bSOVoC5N.js +0 -15
  364. package/dist/web/assets/chevron-right-D7Pn5agw.js +0 -1
  365. package/dist/web/assets/circle-4g2coECH.js +0 -1
  366. package/dist/web/assets/clipboard-B-gwJ8DF.js +0 -1
  367. package/dist/web/assets/clock-DbiNiwCG.js +0 -1
  368. package/dist/web/assets/code-DdKXqtf_.js +0 -1
  369. package/dist/web/assets/code-editor-COTa37oe.js +0 -10
  370. package/dist/web/assets/context-menu-BEAa1qhd.js +0 -1
  371. package/dist/web/assets/database-viewer-Dta7lSSk.js +0 -1
  372. package/dist/web/assets/dialog-DrL3WEXd.js +0 -1
  373. package/dist/web/assets/diff-viewer-CT0lmDc9.js +0 -4
  374. package/dist/web/assets/explorer-body-BGjR2Gbt.js +0 -2
  375. package/dist/web/assets/explorer-window-content-DAhHh5EQ.js +0 -1
  376. package/dist/web/assets/extension-store-ClQpnxQr.js +0 -1
  377. package/dist/web/assets/external-link-ojDHcQxQ.js +0 -1
  378. package/dist/web/assets/eye-off-Rq1n2-oe.js +0 -1
  379. package/dist/web/assets/file-store-28e44KP3.js +0 -1
  380. package/dist/web/assets/git-log-panel-D1-YXZ0O.js +0 -1
  381. package/dist/web/assets/gitGraph-HDMCJU4V-ikUwsekd.js +0 -1
  382. package/dist/web/assets/glide-data-grid-BSsqxe2v.js +0 -138
  383. package/dist/web/assets/group-chat-tab-CMtzoaFc.js +0 -1
  384. package/dist/web/assets/index-BkHlMfPD.js +0 -52
  385. package/dist/web/assets/index-SFFv2-VM.css +0 -2
  386. package/dist/web/assets/info-3K5VOQVL-BAgmboBp.js +0 -1
  387. package/dist/web/assets/input-DTmI80l2.js +0 -1
  388. package/dist/web/assets/keybindings-store-Bi0q9x4Q.js +0 -1
  389. package/dist/web/assets/keybindings-store-_KfovSz7.js +0 -1
  390. package/dist/web/assets/loader-circle-DD65e9-7.js +0 -1
  391. package/dist/web/assets/lock-BEtt_eTO.js +0 -1
  392. package/dist/web/assets/markdown-renderer-DroTmKbJ.js +0 -1
  393. package/dist/web/assets/markdown-renderer-ntDi3Tx-.js +0 -3
  394. package/dist/web/assets/mobile-explorer-sheet-BP6iU4ma.js +0 -1
  395. package/dist/web/assets/monitor-smartphone-C1wUPOSg.js +0 -1
  396. package/dist/web/assets/notification-store-II5Kw9rD.js +0 -1
  397. package/dist/web/assets/packet-RMMSAZCW-RVlbbDRS.js +0 -1
  398. package/dist/web/assets/panel-store-Dv28vz3L.js +0 -1
  399. package/dist/web/assets/pdf-preview-DKeIIJTv.js +0 -1
  400. package/dist/web/assets/pie-UPGHQEXC-ZWb4izpk.js +0 -1
  401. package/dist/web/assets/play-CPbVzGgq.js +0 -1
  402. package/dist/web/assets/plus-BxqD2FIZ.js +0 -1
  403. package/dist/web/assets/portal-container-context-BmG4--I-.js +0 -1
  404. package/dist/web/assets/radar-KQ55EAFF-bsP5x1Bx.js +0 -1
  405. package/dist/web/assets/refresh-cw-BALzw7L3.js +0 -1
  406. package/dist/web/assets/remote-desktop-mobile-sheet-CYZUjYTO.js +0 -2
  407. package/dist/web/assets/remote-desktop-mobile-view-DaoB49k4.js +0 -1
  408. package/dist/web/assets/remote-desktop-readiness-gate--LJgsuoj.js +0 -1
  409. package/dist/web/assets/remote-desktop-window-content-Cihx4PNZ.js +0 -1
  410. package/dist/web/assets/rotate-ccw-CuvNBM80.js +0 -1
  411. package/dist/web/assets/scroll-area-B5yhPpZo.js +0 -1
  412. package/dist/web/assets/send-Cits8q0l.js +0 -1
  413. package/dist/web/assets/settings-store-Dp4i8zeA.js +0 -2
  414. package/dist/web/assets/settings-tab-BtMvvKAF.js +0 -3
  415. package/dist/web/assets/settings-tab-UPPycGRF.js +0 -1
  416. package/dist/web/assets/shallow-BBJ_qoEF.js +0 -1
  417. package/dist/web/assets/sql-query-editor-DZqnYIB7.js +0 -1
  418. package/dist/web/assets/sqlite-viewer-DXutos0e.js +0 -1
  419. package/dist/web/assets/switch-BPol3BFC.js +0 -1
  420. package/dist/web/assets/system-monitor-body-Ct8_GX_f.js +0 -2
  421. package/dist/web/assets/system-monitor-tab-DC_EerdI.js +0 -1
  422. package/dist/web/assets/system-monitor-window-content-D25DMNpw.js +0 -1
  423. package/dist/web/assets/team-member-sheet-DvmioeQL.js +0 -2
  424. package/dist/web/assets/team-member-window-content-BJ_XFw8-.js +0 -1
  425. package/dist/web/assets/tool-cards-pIu_cmEL.js +0 -8
  426. package/dist/web/assets/trash-2-BjShZ7if.js +0 -1
  427. package/dist/web/assets/treemap-KZPCXAKY-Dh1oxNdQ.js +0 -1
  428. package/dist/web/assets/triangle-alert-qQ6Plydp.js +0 -1
  429. package/dist/web/assets/upload-7Zs6oaK5.js +0 -1
  430. package/dist/web/assets/use-monaco-theme-DaxAk3gB.js +0 -1
  431. package/dist/web/assets/use-open-team-member-CopUuDHG.js +0 -1
  432. package/dist/web/assets/use-remote-desktop-display-choice-BUA_1eaH.js +0 -1
  433. package/dist/web/assets/vendor-markdown-CKTlmXbz.js +0 -291
  434. package/dist/web/assets/video-preview-C2_fdunE.js +0 -1
  435. package/dist/web/assets/window-store-CNzkeUyO.js +0 -1
  436. package/dist/web/assets/x-Dzu9ecti.js +0 -1
  437. package/src/web/components/chat/account-dialogs.tsx +0 -384
  438. package/src/web/components/settings/accounts-settings-section.tsx +0 -1090
  439. package/src/web/components/settings/codex-accounts-section.tsx +0 -198
  440. package/src/web/components/settings/telegram-settings-section.tsx +0 -113
  441. /package/dist/web/assets/{abap-Cjj2olMr.js → abap-h3QyH8MW.js} +0 -0
  442. /package/dist/web/assets/{actionscript-3-DCkW7kWC.js → actionscript-3-mkxkq-FN.js} +0 -0
  443. /package/dist/web/assets/{ada-AEWvZXuX.js → ada-DhISK_EH.js} +0 -0
  444. /package/dist/web/assets/{andromeeda-D7cSpME6.js → andromeeda-DnlWWesX.js} +0 -0
  445. /package/dist/web/assets/{apache-DjvX0RSe.js → apache-P5AK3sfA.js} +0 -0
  446. /package/dist/web/assets/{apex-70xIZ0pH.js → apex-CJUPehg2.js} +0 -0
  447. /package/dist/web/assets/{applescript-C3qKqxmA.js → applescript-Dx6GGbay.js} +0 -0
  448. /package/dist/web/assets/{apply-theme-DJ794zSQ.js → apply-theme-DxTXOAcV.js} +0 -0
  449. /package/dist/web/assets/{ara-iGt2zixk.js → ara-95Kx3qKb.js} +0 -0
  450. /package/dist/web/assets/{asciidoc-Bs2t-06W.js → asciidoc-Dp0RbIej.js} +0 -0
  451. /package/dist/web/assets/{asm-B-T0eqhE.js → asm-C82coUjU.js} +0 -0
  452. /package/dist/web/assets/{aurora-x-D3h_9M3E.js → aurora-x-BFc3zeop.js} +0 -0
  453. /package/dist/web/assets/{awk-CZwfwpPi.js → awk-ChjC0icr.js} +0 -0
  454. /package/dist/web/assets/{ayu-dark-DulkglEA.js → ayu-dark-DYfUf_PB.js} +0 -0
  455. /package/dist/web/assets/{ayu-light-DyNQiRpn.js → ayu-light-Bh6O0plO.js} +0 -0
  456. /package/dist/web/assets/{ayu-mirage-CcEt4xkI.js → ayu-mirage-DlNhHTGq.js} +0 -0
  457. /package/dist/web/assets/{ballerina-OhQPjyHS.js → ballerina-Ceoll3Sk.js} +0 -0
  458. /package/dist/web/assets/{bat-DqKcJTUn.js → bat-C7QS6Lfq.js} +0 -0
  459. /package/dist/web/assets/{beancount-BdXlMi8T.js → beancount-5NMEAZgs.js} +0 -0
  460. /package/dist/web/assets/{berry-DvgY6BkY.js → berry-BBL83Vbk.js} +0 -0
  461. /package/dist/web/assets/{bibtex-BSRJq0yM.js → bibtex-L7rPkPSG.js} +0 -0
  462. /package/dist/web/assets/{bicep-DpAdumKt.js → bicep-BOwHIINh.js} +0 -0
  463. /package/dist/web/assets/{bird2-rlbtp8di.js → bird2-CWLsMom2.js} +0 -0
  464. /package/dist/web/assets/{c-AjOLBXIs.js → c-CD4Ku5Cr.js} +0 -0
  465. /package/dist/web/assets/{c3-DqIXz4Ia.js → c3-CpMl-w3G.js} +0 -0
  466. /package/dist/web/assets/{cadence-DAJtUpOE.js → cadence-CvgF6ehN.js} +0 -0
  467. /package/dist/web/assets/{can-open-in-ppm-CdCK2c2a.js → can-open-in-ppm-j3jh7fUo.js} +0 -0
  468. /package/dist/web/assets/{catppuccin-frappe-DUPoeZfb.js → catppuccin-frappe-eo_dTm3F.js} +0 -0
  469. /package/dist/web/assets/{catppuccin-latte-CK3U68Ql.js → catppuccin-latte-DoiMCU_7.js} +0 -0
  470. /package/dist/web/assets/{catppuccin-macchiato-BzteLnIX.js → catppuccin-macchiato-UQxsK2Vh.js} +0 -0
  471. /package/dist/web/assets/{catppuccin-mocha-ozLd0aBR.js → catppuccin-mocha-J0i-8RNy.js} +0 -0
  472. /package/dist/web/assets/{clarity-6KqEoBfz.js → clarity-DXYM8-SA.js} +0 -0
  473. /package/dist/web/assets/{clojure-B_uBL5C7.js → clojure-DzamsL69.js} +0 -0
  474. /package/dist/web/assets/{cmake-B3nxBNmj.js → cmake-CR6RP_T_.js} +0 -0
  475. /package/dist/web/assets/{codeowners-DkS-51at.js → codeowners-D1s6-IyY.js} +0 -0
  476. /package/dist/web/assets/{codeql-rpr1z_mR.js → codeql-DAqNWMkS.js} +0 -0
  477. /package/dist/web/assets/{common-lisp-BfNbv_W6.js → common-lisp-8e-KtitB.js} +0 -0
  478. /package/dist/web/assets/{coq-EAW-1nw0.js → coq-UEq0WUcO.js} +0 -0
  479. /package/dist/web/assets/{csharp-BE3Gn5bq.js → csharp-wxJ0TuCm.js} +0 -0
  480. /package/dist/web/assets/{css-Cr27CfPY.js → css-Cerub-yB.js} +0 -0
  481. /package/dist/web/assets/{csv-B5VtaPtH.js → csv-Bh9_WD_h.js} +0 -0
  482. /package/dist/web/assets/{csv-parser-B2v8MoUy.js → csv-parser-BPIDt6ni.js} +0 -0
  483. /package/dist/web/assets/{cue-jm6lYNrx.js → cue-rzM1ztqU.js} +0 -0
  484. /package/dist/web/assets/{cypher-DXkvK-pn.js → cypher-DGaTQ8m0.js} +0 -0
  485. /package/dist/web/assets/{d-CgG1_yzd.js → d-DbkZGuhB.js} +0 -0
  486. /package/dist/web/assets/{dark-plus-CALzcIVB.js → dark-plus-BO2bzZcA.js} +0 -0
  487. /package/dist/web/assets/{dart-BEVoLlWg.js → dart-BDhCoqVg.js} +0 -0
  488. /package/dist/web/assets/{dax-H0BW3Q_P.js → dax-Bh03stWh.js} +0 -0
  489. /package/dist/web/assets/{desktop-BtYq1l_0.js → desktop-BOdr_fwq.js} +0 -0
  490. /package/dist/web/assets/{diff-CnBYdfTg.js → diff-Bb_gTsS2.js} +0 -0
  491. /package/dist/web/assets/{dist-CJJKpgR7.js → dist-BUMDu0av.js} +0 -0
  492. /package/dist/web/assets/{docker-COBitpld.js → docker-BDsTF5ce.js} +0 -0
  493. /package/dist/web/assets/{dotenv-CG0x1m7c.js → dotenv-CbCWp9Ti.js} +0 -0
  494. /package/dist/web/assets/{dracula-CLCi47X-.js → dracula-Cd1fxvoX.js} +0 -0
  495. /package/dist/web/assets/{dracula-soft-BMv5cCAV.js → dracula-soft-B7eXkfXG.js} +0 -0
  496. /package/dist/web/assets/{dream-maker-1Bd4OSww.js → dream-maker-XlzuS21R.js} +0 -0
  497. /package/dist/web/assets/{emacs-lisp-B-2zKLn-.js → emacs-lisp-CXV1Ki_7.js} +0 -0
  498. /package/dist/web/assets/{everforest-dark-BGVynCbV.js → everforest-dark-DuqR60Dg.js} +0 -0
  499. /package/dist/web/assets/{everforest-light--rn3R7Cm.js → everforest-light-q4BPnenD.js} +0 -0
  500. /package/dist/web/assets/{fennel-SWg9Us_u.js → fennel-B-e0TX2Q.js} +0 -0
  501. /package/dist/web/assets/{fish-B04oqgKk.js → fish-Bzb6-TtF.js} +0 -0
  502. /package/dist/web/assets/{fluent-BefzAGMN.js → fluent-B-szH2-f.js} +0 -0
  503. /package/dist/web/assets/{format-bytes-gQw5wzqa.js → format-bytes-CKXk9k2d.js} +0 -0
  504. /package/dist/web/assets/{fortran-free-form-alw7jMxb.js → fortran-free-form-OS6SGYC0.js} +0 -0
  505. /package/dist/web/assets/{gdscript-BAyhvjCx.js → gdscript-FYq4OP2d.js} +0 -0
  506. /package/dist/web/assets/{gdshader-Crl09X-B.js → gdshader-BpG6P8rw.js} +0 -0
  507. /package/dist/web/assets/{genie-BSIGRHu7.js → genie-BS9RLAna.js} +0 -0
  508. /package/dist/web/assets/{gherkin-DJhSnSWH.js → gherkin-B_9FdtGs.js} +0 -0
  509. /package/dist/web/assets/{github-dark-r8C6qwug.js → github-dark-cfaIlnBV.js} +0 -0
  510. /package/dist/web/assets/{github-dark-default-FVtXjcm8.js → github-dark-default-B2Wv65He.js} +0 -0
  511. /package/dist/web/assets/{github-dark-dimmed-CzrUPgSb.js → github-dark-dimmed-CtM9X76s.js} +0 -0
  512. /package/dist/web/assets/{github-dark-high-contrast-1s9rF5EB.js → github-dark-high-contrast-DLHyk0tG.js} +0 -0
  513. /package/dist/web/assets/{github-light-BvIWCkl7.js → github-light-DWEI10iG.js} +0 -0
  514. /package/dist/web/assets/{github-light-default-CVdhzrxV.js → github-light-default-DcJkK1YN.js} +0 -0
  515. /package/dist/web/assets/{github-light-high-contrast-CBAf6hjF.js → github-light-high-contrast-Bcx9iKDt.js} +0 -0
  516. /package/dist/web/assets/{gleam-DvL1q7aa.js → gleam-DVpiM6b2.js} +0 -0
  517. /package/dist/web/assets/{gn-BG5NvwAf.js → gn-B80DNc0i.js} +0 -0
  518. /package/dist/web/assets/{gnuplot-BARnM-zG.js → gnuplot-D9i3MaT-.js} +0 -0
  519. /package/dist/web/assets/{go-DXvjjq4q.js → go-CwCBf_Pt.js} +0 -0
  520. /package/dist/web/assets/{groovy-CRylSKhr.js → groovy-D7mEfwmW.js} +0 -0
  521. /package/dist/web/assets/{gruvbox-dark-hard-BVPjnqm7.js → gruvbox-dark-hard-C4R4MOoH.js} +0 -0
  522. /package/dist/web/assets/{gruvbox-dark-medium-U_PDgq0a.js → gruvbox-dark-medium-CZnFC0s8.js} +0 -0
  523. /package/dist/web/assets/{gruvbox-dark-soft-CkzNtpbr.js → gruvbox-dark-soft-DFcJlEBl.js} +0 -0
  524. /package/dist/web/assets/{gruvbox-light-hard-DfMmR2G8.js → gruvbox-light-hard-B6IF_1L-.js} +0 -0
  525. /package/dist/web/assets/{gruvbox-light-medium-BGCak-dd.js → gruvbox-light-medium-C19AZJv2.js} +0 -0
  526. /package/dist/web/assets/{gruvbox-light-soft-DnpM3fkN.js → gruvbox-light-soft-BAnpDm9B.js} +0 -0
  527. /package/dist/web/assets/{haskell-DXgGIDD_.js → haskell-CsXSYXRG.js} +0 -0
  528. /package/dist/web/assets/{haxe-B7qlTpRz.js → haxe-B1uyiI9t.js} +0 -0
  529. /package/dist/web/assets/{hcl-ADWfVe_Q.js → hcl-DlYKn5q9.js} +0 -0
  530. /package/dist/web/assets/{hjson-BaM9gLbq.js → hjson-C_0VMGJJ.js} +0 -0
  531. /package/dist/web/assets/{hlsl-iqaRA4MG.js → hlsl-zt2PEzbg.js} +0 -0
  532. /package/dist/web/assets/{horizon-BPpTnhB9.js → horizon-6wQfyOK0.js} +0 -0
  533. /package/dist/web/assets/{horizon-bright-yBLg3QbE.js → horizon-bright-CqI2qTim.js} +0 -0
  534. /package/dist/web/assets/{houston-eFOp0ySL.js → houston-BljpHDoG.js} +0 -0
  535. /package/dist/web/assets/{hy-BALeMn3i.js → hy-XzuL55dw.js} +0 -0
  536. /package/dist/web/assets/{image-gallery-Bqb-wjNw.js → image-gallery-Cj8nd4xW.js} +0 -0
  537. /package/dist/web/assets/{imba-CdGUm_GD.js → imba-CqhhjBrP.js} +0 -0
  538. /package/dist/web/assets/{ini-DG7imsqe.js → ini-CiFDi6cY.js} +0 -0
  539. /package/dist/web/assets/{java-D5GTT474.js → java-OsQgTL5-.js} +0 -0
  540. /package/dist/web/assets/{javascript-V7WwJvRm.js → javascript-qWAOScxl.js} +0 -0
  541. /package/dist/web/assets/{json-B9FhwAgR.js → json-RiAKsaD1.js} +0 -0
  542. /package/dist/web/assets/{json5--Abk7MUy.js → json5-BeCnS3H2.js} +0 -0
  543. /package/dist/web/assets/{jsonc-C8jb-Z6x.js → jsonc-CbTW1Mka.js} +0 -0
  544. /package/dist/web/assets/{jsonl-CaJNxfkm.js → jsonl-BLSi_669.js} +0 -0
  545. /package/dist/web/assets/{jsonnet-CPrKMSCU.js → jsonnet-C3xAggou.js} +0 -0
  546. /package/dist/web/assets/{jssm-DEu-9_uo.js → jssm-CLdiagC9.js} +0 -0
  547. /package/dist/web/assets/{jsx-MfAsCWDW.js → jsx-lYE1lb50.js} +0 -0
  548. /package/dist/web/assets/{kanagawa-dragon-DnbcAduF.js → kanagawa-dragon-Cd1vcrpD.js} +0 -0
  549. /package/dist/web/assets/{kanagawa-lotus-yxsY3ew8.js → kanagawa-lotus-Dw3QKtk9.js} +0 -0
  550. /package/dist/web/assets/{kanagawa-wave-C2RyvZaM.js → kanagawa-wave-DIq5Zv5G.js} +0 -0
  551. /package/dist/web/assets/{kdl-CvssmvcU.js → kdl-BfDV0825.js} +0 -0
  552. /package/dist/web/assets/{kotlin-jufO49mU.js → kotlin-DONxR4wD.js} +0 -0
  553. /package/dist/web/assets/{kusto-BAFnylR7.js → kusto-BDP63Fh5.js} +0 -0
  554. /package/dist/web/assets/{laserwave-BgxFC4Ge.js → laserwave-BeBrWApL.js} +0 -0
  555. /package/dist/web/assets/{lean-Bgt24V1D.js → lean-DOZc0N1A.js} +0 -0
  556. /package/dist/web/assets/{less-Bk_UaqsM.js → less-C3H9NlDv.js} +0 -0
  557. /package/dist/web/assets/{libesm-dcbw4_18.js → libesm-DLBE59ul.js} +0 -0
  558. /package/dist/web/assets/{light-plus-nMxFfInN.js → light-plus-C_kTL608.js} +0 -0
  559. /package/dist/web/assets/{llvm-3_PASDOO.js → llvm-Dsp27U3P.js} +0 -0
  560. /package/dist/web/assets/{log-CKm0CxB1.js → log-BywQaTCy.js} +0 -0
  561. /package/dist/web/assets/{logo-DFP1_5U6.js → logo-BirJgciO.js} +0 -0
  562. /package/dist/web/assets/{luau-BcZnoyh3.js → luau-Btrz5kJB.js} +0 -0
  563. /package/dist/web/assets/{make-CpNSwgwT.js → make-Kl7nFQrS.js} +0 -0
  564. /package/dist/web/assets/{markdown-DhU3Djpv.js → markdown-qsC4P8S-.js} +0 -0
  565. /package/dist/web/assets/{material-theme-DD5L3OqF.js → material-theme-CC__ui3p.js} +0 -0
  566. /package/dist/web/assets/{material-theme-darker-CG2_tD3R.js → material-theme-darker-D4FxG2FZ.js} +0 -0
  567. /package/dist/web/assets/{material-theme-lighter-CzNkUc9Y.js → material-theme-lighter-zGqiWcxn.js} +0 -0
  568. /package/dist/web/assets/{material-theme-ocean-BtLiAaF_.js → material-theme-ocean-CZIdTTB1.js} +0 -0
  569. /package/dist/web/assets/{material-theme-palenight-Brvjz035.js → material-theme-palenight-CEIs0GcK.js} +0 -0
  570. /package/dist/web/assets/{matlab-CCY0KtMD.js → matlab-Cf0aUjN9.js} +0 -0
  571. /package/dist/web/assets/{mdx-CH6YY0aS.js → mdx-CIsXjH86.js} +0 -0
  572. /package/dist/web/assets/{mermaid-Bqh3gFI0.js → mermaid-B06WO9Qc.js} +0 -0
  573. /package/dist/web/assets/{min-dark-BUwfTzI6.js → min-dark-z8fyp0FT.js} +0 -0
  574. /package/dist/web/assets/{min-light-oLanXCOk.js → min-light-Dr-qesbW.js} +0 -0
  575. /package/dist/web/assets/{mipsasm-CTh3hPWK.js → mipsasm-CWHzYLcE.js} +0 -0
  576. /package/dist/web/assets/{mojo-DXdszZKa.js → mojo-D9zXrKmv.js} +0 -0
  577. /package/dist/web/assets/{monokai-TNE_tRxc.js → monokai-4b-AKOpX.js} +0 -0
  578. /package/dist/web/assets/{moonbit-jrk-Gx1X.js → moonbit-Dl4dhOZJ.js} +0 -0
  579. /package/dist/web/assets/{move-DItn61bH.js → move-DTYKJmXy.js} +0 -0
  580. /package/dist/web/assets/{narrat-3kB4dkh0.js → narrat-DnvXOS-o.js} +0 -0
  581. /package/dist/web/assets/{nextflow-groovy-CSKtwETr.js → nextflow-groovy-Di8QnEXg.js} +0 -0
  582. /package/dist/web/assets/{night-owl-Dm91nSIq.js → night-owl-Czh0yq-H.js} +0 -0
  583. /package/dist/web/assets/{night-owl-light-riwrcPRg.js → night-owl-light-D_OpZqOR.js} +0 -0
  584. /package/dist/web/assets/{nix-sLgl9zGF.js → nix-2MvcjpK3.js} +0 -0
  585. /package/dist/web/assets/{nord-TWkJPI6M.js → nord-CG_r4cyk.js} +0 -0
  586. /package/dist/web/assets/{nushell-CDZi4-Gf.js → nushell-DINskMcU.js} +0 -0
  587. /package/dist/web/assets/{objective-c-DW9PboNp.js → objective-c-Dv7BIicR.js} +0 -0
  588. /package/dist/web/assets/{objective-cpp-2XCCX6q1.js → objective-cpp-CpqsCxPb.js} +0 -0
  589. /package/dist/web/assets/{ocaml-CxPdnjrh.js → ocaml-DwTsYSmW.js} +0 -0
  590. /package/dist/web/assets/{odin-BJjlKpkM.js → odin-G-4O4k4t.js} +0 -0
  591. /package/dist/web/assets/{one-dark-pro-K1KOmBZ7.js → one-dark-pro-13Pv5SjI.js} +0 -0
  592. /package/dist/web/assets/{one-light-DQB-vUyZ.js → one-light-Bi2e86mS.js} +0 -0
  593. /package/dist/web/assets/{openscad-DCoLwn4j.js → openscad-D0WsOTij.js} +0 -0
  594. /package/dist/web/assets/{pascal-z2ktbbWp.js → pascal-DmDStuXr.js} +0 -0
  595. /package/dist/web/assets/{pkl-Dyq4-v6z.js → pkl-Bl6Qjtuo.js} +0 -0
  596. /package/dist/web/assets/{plastic-C4Rq5PnD.js → plastic-UeSVSjTI.js} +0 -0
  597. /package/dist/web/assets/{plsql-BCIjO63y.js → plsql-DreFC689.js} +0 -0
  598. /package/dist/web/assets/{po-_kXpd_0n.js → po-ZIjccxsX.js} +0 -0
  599. /package/dist/web/assets/{poimandres-C9XggjB3.js → poimandres-CvQHJPrI.js} +0 -0
  600. /package/dist/web/assets/{polar-DV_1L04z.js → polar-BrMB0Kgh.js} +0 -0
  601. /package/dist/web/assets/{postcss-BuqS91mQ.js → postcss-pgUsil1T.js} +0 -0
  602. /package/dist/web/assets/{powerquery-DK13B2Dc.js → powerquery-CTsZkCg_.js} +0 -0
  603. /package/dist/web/assets/{powershell-tkuhhtuP.js → powershell-CcwVuBAp.js} +0 -0
  604. /package/dist/web/assets/{prisma-WNXfBtpr.js → prisma-spjnAI5W.js} +0 -0
  605. /package/dist/web/assets/{prolog-BzEdNKfO.js → prolog-ChCkZpDO.js} +0 -0
  606. /package/dist/web/assets/{proto-B5meVsrA.js → proto-wVGqK4gB.js} +0 -0
  607. /package/dist/web/assets/{puppet-C7h_p4r2.js → puppet-CED7iwPQ.js} +0 -0
  608. /package/dist/web/assets/{purescript-qH1KxAs6.js → purescript-u9lGiQKV.js} +0 -0
  609. /package/dist/web/assets/{python-DeTIYd5V.js → python-DeqTx6ef.js} +0 -0
  610. /package/dist/web/assets/{qmldir-4Rm5AvD3.js → qmldir-BH1uoPrX.js} +0 -0
  611. /package/dist/web/assets/{qss-YRbeNtr0.js → qss-CUVKA_13.js} +0 -0
  612. /package/dist/web/assets/{r-DHwHK0Jh.js → r-BqdPzqYH.js} +0 -0
  613. /package/dist/web/assets/{racket-gKn4SFLQ.js → racket-C2eTgPN3.js} +0 -0
  614. /package/dist/web/assets/{raku-DWTmAj3A.js → raku-uAay1sWx.js} +0 -0
  615. /package/dist/web/assets/{red-BFuHRZFS.js → red-DTZP1ZfC.js} +0 -0
  616. /package/dist/web/assets/{reg-DYoOF5tH.js → reg-Cdk3YsxY.js} +0 -0
  617. /package/dist/web/assets/{regexp-6rXQHD2-.js → regexp-Bpa2aS7K.js} +0 -0
  618. /package/dist/web/assets/{rel-Z_tTruCU.js → rel-bNsb1-3L.js} +0 -0
  619. /package/dist/web/assets/{resolve-theme-4fexHWl6.js → resolve-theme-DQk_YcsT.js} +0 -0
  620. /package/dist/web/assets/{riscv-DfTfKuvU.js → riscv-DSaubByW.js} +0 -0
  621. /package/dist/web/assets/{ron-CsUtW8gQ.js → ron-DbQzYM6i.js} +0 -0
  622. /package/dist/web/assets/{rose-pine-Dng_vCo6.js → rose-pine-C7RsprTu.js} +0 -0
  623. /package/dist/web/assets/{rose-pine-dawn-Ds88Nezf.js → rose-pine-dawn-VP976YzL.js} +0 -0
  624. /package/dist/web/assets/{rose-pine-moon-DWpzrqNz.js → rose-pine-moon-XHZNhOqd.js} +0 -0
  625. /package/dist/web/assets/{rosmsg-h2sO84oD.js → rosmsg-BVQlx0Lj.js} +0 -0
  626. /package/dist/web/assets/{rust-Dx86yfCi.js → rust-VHaQ3rh3.js} +0 -0
  627. /package/dist/web/assets/{sass-BwwVhdT9.js → sass-CO-kMkwD.js} +0 -0
  628. /package/dist/web/assets/{scala-WJqNEN-w.js → scala-cY0GXkiz.js} +0 -0
  629. /package/dist/web/assets/{scheme-uwj-t1Rp.js → scheme-D4LNZE0X.js} +0 -0
  630. /package/dist/web/assets/{sdbl-BYy5hgEz.js → sdbl-Bh2Fd2cn.js} +0 -0
  631. /package/dist/web/assets/{shellscript-DWTW_w2K.js → shellscript-KKclmCnt.js} +0 -0
  632. /package/dist/web/assets/{slack-dark-BvhGq5vQ.js → slack-dark-D6YQ-fqf.js} +0 -0
  633. /package/dist/web/assets/{slack-ochin-C9g0ZRFr.js → slack-ochin-CDf66CDj.js} +0 -0
  634. /package/dist/web/assets/{smalltalk-ZEiZ6i7U.js → smalltalk-D2OH_QIl.js} +0 -0
  635. /package/dist/web/assets/{snazzy-light-29OA24q5.js → snazzy-light-CQeF8G1K.js} +0 -0
  636. /package/dist/web/assets/{solarized-dark-Bnq9IOn_.js → solarized-dark-LTPf1iLZ.js} +0 -0
  637. /package/dist/web/assets/{solarized-light-CYdI7T5g.js → solarized-light-Ose1n88k.js} +0 -0
  638. /package/dist/web/assets/{solidity-DcuIDB-_.js → solidity-lFuOMHki.js} +0 -0
  639. /package/dist/web/assets/{splunk-D5ONE91g.js → splunk-Bov-sB5S.js} +0 -0
  640. /package/dist/web/assets/{sql-DIwJJDU-.js → sql-BKyZ6wmk.js} +0 -0
  641. /package/dist/web/assets/{ssh-config-D8717tLI.js → ssh-config-CqJoMGMb.js} +0 -0
  642. /package/dist/web/assets/{stylus-Cm1lO4Y6.js → stylus-Bf5pWRLI.js} +0 -0
  643. /package/dist/web/assets/{swift-X8oWpSeU.js → swift-D5d1hn_u.js} +0 -0
  644. /package/dist/web/assets/{synthwave-84-DF-t1UKY.js → synthwave-84-B4c6YAgO.js} +0 -0
  645. /package/dist/web/assets/{system-verilog-JYyHFpPf.js → system-verilog-D7fXyDsI.js} +0 -0
  646. /package/dist/web/assets/{systemd-Da-5Ma_c.js → systemd-oNagzlBK.js} +0 -0
  647. /package/dist/web/assets/{talonscript-CXraVPho.js → talonscript-C6Nqm9Dm.js} +0 -0
  648. /package/dist/web/assets/{tasl-BCmWezH9.js → tasl-CaWy-cBX.js} +0 -0
  649. /package/dist/web/assets/{tcl-BTufzfbf.js → tcl-C435P5Mq.js} +0 -0
  650. /package/dist/web/assets/{terraform-CtUA9c8W.js → terraform-CVDxqdPf.js} +0 -0
  651. /package/dist/web/assets/{tokyo-night-BCThYXmz.js → tokyo-night-Uhs17tQ3.js} +0 -0
  652. /package/dist/web/assets/{toml-D5YlDr6Y.js → toml-j6fPOJ6P.js} +0 -0
  653. /package/dist/web/assets/{tsv-CBB-P_Sl.js → tsv-h78O6xrL.js} +0 -0
  654. /package/dist/web/assets/{tsx-D5B4juDp.js → tsx-Bol-vCrQ.js} +0 -0
  655. /package/dist/web/assets/{turtle-D8PCeJ-v.js → turtle-Dw8eFf04.js} +0 -0
  656. /package/dist/web/assets/{typescript-CMPnTawp.js → typescript-Dtz7DPci.js} +0 -0
  657. /package/dist/web/assets/{typespec-CHxldxIW.js → typespec-BIOQ7dMP.js} +0 -0
  658. /package/dist/web/assets/{typst-Q8CqX5OR.js → typst-CoaGMjzu.js} +0 -0
  659. /package/dist/web/assets/{utils-QHe_Q6UF.js → utils-C9OjCrou.js} +0 -0
  660. /package/dist/web/assets/{v-Bf2hKAMu.js → v-BoqFdpJ7.js} +0 -0
  661. /package/dist/web/assets/{vala-Cbete-UE.js → vala-COBpNnCC.js} +0 -0
  662. /package/dist/web/assets/{vb-CA-xWAcX.js → vb-DxI86znh.js} +0 -0
  663. /package/dist/web/assets/{vendor-xterm-BHKF_Nsf.js → vendor-xterm-BaMOj65g.js} +0 -0
  664. /package/dist/web/assets/{verilog-B3wb8CfT.js → verilog-DcTAamuQ.js} +0 -0
  665. /package/dist/web/assets/{vesper-D6r3VJq_.js → vesper-DO298JSv.js} +0 -0
  666. /package/dist/web/assets/{vhdl-C42S_797.js → vhdl-Dq-bDVSN.js} +0 -0
  667. /package/dist/web/assets/{viml-BePrIAs8.js → viml-zHTRWuQh.js} +0 -0
  668. /package/dist/web/assets/{vitesse-black-DRqn61Ck.js → vitesse-black-jlYMJCBf.js} +0 -0
  669. /package/dist/web/assets/{vitesse-dark-39_8UE-w.js → vitesse-dark-DS0sUlSZ.js} +0 -0
  670. /package/dist/web/assets/{vitesse-light-B1jwrxDm.js → vitesse-light-HSeZ2YhG.js} +0 -0
  671. /package/dist/web/assets/{vyper-tDJv3imF.js → vyper-BQfKVO3M.js} +0 -0
  672. /package/dist/web/assets/{wasm-ChtccNfv.js → wasm-B1xp9bLp.js} +0 -0
  673. /package/dist/web/assets/{wasm-DR7eskZH.js → wasm-B6mCuI8h.js} +0 -0
  674. /package/dist/web/assets/{wenyan-CezVfqN3.js → wenyan-CfpS05sP.js} +0 -0
  675. /package/dist/web/assets/{wgsl-CwOcIiIG.js → wgsl-14GDsWRD.js} +0 -0
  676. /package/dist/web/assets/{wikitext-C5coomYc.js → wikitext-Ccbq5EVX.js} +0 -0
  677. /package/dist/web/assets/{wit-C9yyX6-U.js → wit-C19hb3R5.js} +0 -0
  678. /package/dist/web/assets/{wolfram-BWi0rMAa.js → wolfram-B-ZSwGwP.js} +0 -0
  679. /package/dist/web/assets/{yaml-CGXhKUtT.js → yaml-mw_JUeio.js} +0 -0
  680. /package/dist/web/assets/{zenscript-Do7WLoJh.js → zenscript-WbzDRjvF.js} +0 -0
  681. /package/dist/web/assets/{zig-Cr80Qrru.js → zig-F0vvImTJ.js} +0 -0
@@ -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
@@ -45,8 +51,14 @@ export interface AIProvider {
45
51
  markAsResumed?(sessionId: string): void;
46
52
  isAvailable?(): Promise<boolean>;
47
53
  listModels?(): Promise<ModelOption[]>;
54
+ /**
55
+ * Skills the provider's own runtime would resolve for this session, for
56
+ * providers that own a skill system PPM cannot read off disk. Implemented by
57
+ * codex; absent for Claude, whose skills come from the shared disk discovery.
58
+ */
59
+ listSkills?(sessionId?: string): Promise<import("../providers/codex-app-server/codex-protocol").CodexSkill[]>;
48
60
  /** Provider-specific usage/quota (rate limits). Used by GET /chat/usage. */
49
- getUsage?(): Promise<UsageInfo>;
61
+ getUsage?(sessionId?: string): Promise<UsageInfo>;
50
62
  /** True when a live streaming subprocess exists for this session */
51
63
  hasStreamingSession?(sessionId: string): boolean;
52
64
  /** Prompt-cache lifetime for this session, in ms — how long holding its subprocess pays. */
@@ -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[];
package/src/web/app.tsx CHANGED
@@ -58,15 +58,9 @@ export function App() {
58
58
  const [authState, setAuthState] = useState<AuthState>("checking");
59
59
  const isMobileViewport = useIsMobile();
60
60
  const [drawerOpen, setDrawerOpen] = useState(false);
61
- const [drawerTab, setDrawerTab] = useState<"explorer" | "git" | "settings" | undefined>();
61
+ const [drawerTab, setDrawerTab] = useState<"explorer" | "git" | undefined>();
62
62
  const [projectSheetOpen, setProjectSheetOpen] = useState(false);
63
63
 
64
- // Listen for "open-mobile-settings" event (from project bottom sheet)
65
- useEffect(() => {
66
- const handler = () => { setDrawerTab("settings"); setDrawerOpen(true); };
67
- window.addEventListener("open-mobile-settings", handler);
68
- return () => window.removeEventListener("open-mobile-settings", handler);
69
- }, []);
70
64
  const [mountedProjects, setMountedProjects] = useState<Set<string>>(
71
65
  () => new Set(["__global__"]),
72
66
  );
@@ -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 */}
@@ -325,7 +325,16 @@ export function ChatHistoryBar({
325
325
  >
326
326
  <Activity className="size-3" />
327
327
  {usageInfo.activeAccountLabel && (
328
- <span className="text-text-secondary font-normal truncate max-w-[60px]">[{usageInfo.activeAccountLabel}]</span>
328
+ // Which account is serving this session, per provider. An email
329
+ // label is unreadable cut to 60px ("hienlh1298@…"), and the
330
+ // account is the thing a multi-account user checks here, so it
331
+ // gets the room and the full string on hover.
332
+ <span
333
+ className="text-text-secondary font-normal truncate max-w-[110px]"
334
+ title={usageInfo.activeAccountLabel}
335
+ >
336
+ [{usageInfo.activeAccountLabel}]
337
+ </span>
329
338
  )}
330
339
  <span>5h:{fiveHourPct != null ? `${fiveHourPct}%` : "--%"}</span>
331
340
  <span className="text-text-subtle">·</span>
@@ -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";
@@ -33,6 +35,21 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
33
35
  const [sessionId, setSessionId] = useState<string | null>(
34
36
  (metadata?.sessionId as string) ?? null,
35
37
  );
38
+
39
+ /**
40
+ * Follow the session id when the provider adopts its own.
41
+ *
42
+ * PPM creates a session under a uuid it mints, then codex (or the Claude SDK)
43
+ * reports the real id and the server re-keys to it. Everything written from
44
+ * that point — transcript, title, account binding — lands under the new id, so
45
+ * a tab left holding the original would reopen into an empty conversation even
46
+ * though the history is on disk. Setting it here also persists it to the tab's
47
+ * metadata through the effect below, which is what makes the fix survive a
48
+ * reload rather than only lasting the turn.
49
+ */
50
+ const handleSessionMigrated = useCallback((newSessionId: string) => {
51
+ setSessionId(newSessionId);
52
+ }, []);
36
53
  const [providerId, setProviderId] = useState<string>(
37
54
  (metadata?.providerId as string) ?? "claude",
38
55
  );
@@ -56,7 +73,7 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
56
73
  );
57
74
 
58
75
  // Pending message to send after WS connects (replaces unreliable setTimeout)
59
- const pendingSendRef = useRef<{ content: string; permissionMode?: string } | null>(null);
76
+ const pendingSendRef = useRef<{ content: string; permissionMode?: string; images?: Array<{ data: string; mediaType: string }>; imagePaths?: string[] } | null>(null);
60
77
 
61
78
  // Drag-and-drop state
62
79
  const [isDragging, setIsDragging] = useState(false);
@@ -131,7 +148,7 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
131
148
  bashPartialOutput,
132
149
  backgroundShells,
133
150
  killBackgroundShell,
134
- } = useChat(sessionId, providerId, projectName);
151
+ } = useChat(sessionId, providerId, projectName, handleSessionMigrated);
135
152
 
136
153
  // Teammates keep working long after their spawn card scrolled away — a resume
137
154
  // arrives by SendMessage and writes no card at all. Poll the roster whenever this
@@ -142,9 +159,9 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
142
159
  // Flush pending message once WS connects (replaces unreliable setTimeout)
143
160
  useEffect(() => {
144
161
  if (isConnected && pendingSendRef.current) {
145
- const { content, permissionMode: pm } = pendingSendRef.current;
162
+ const { content, permissionMode: pm, images: pendingImages, imagePaths: pendingPaths } = pendingSendRef.current;
146
163
  pendingSendRef.current = null;
147
- sendMessage(content, { permissionMode: pm });
164
+ sendMessage(content, { permissionMode: pm, ...(pendingImages?.length && { images: pendingImages }), ...(pendingPaths?.length && { imagePaths: pendingPaths }) });
148
165
  }
149
166
  }, [isConnected, sendMessage]);
150
167
 
@@ -350,7 +367,11 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
350
367
 
351
368
  /** Build message content with file references and inline text snippets prepended */
352
369
  const buildMessageWithAttachments = useCallback(
353
- (content: string, attachments: ChatAttachment[]): string => {
370
+ (
371
+ content: string,
372
+ attachments: ChatAttachment[],
373
+ inlineImages: Array<{ data: string; mediaType: string }> = [],
374
+ ): string => {
354
375
  if (attachments.length === 0) return content;
355
376
 
356
377
  const parts: string[] = [];
@@ -360,12 +381,27 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
360
381
  if (att.textContent) parts.push(att.textContent);
361
382
  }
362
383
 
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");
384
+ // Server-uploaded file references. An image keeps its path even when the payload rides
385
+ // along with the message, because the transcript is what the chat re-renders from and
386
+ // the path is the only thing in it a thumbnail can be drawn from.
387
+ //
388
+ // An image sent inline says so in the marker. A bare path in a user message reads as an
389
+ // invitation to open it, and a model that takes it pays for the same picture twice —
390
+ // once inline, once as a tool result, both replayed on every later turn.
391
+ //
392
+ // Which marker an image gets follows the payloads that are actually going out, not
393
+ // `imageData` alone: the per-message caps can leave one behind, and announcing that its
394
+ // contents are included would hand the model neither the picture nor a reason to open
395
+ // the file. `inlineImages` defaults to none, so a caller that sends no payloads at all
396
+ // (the edit-fork path takes only text) gets the plain markers throughout.
397
+ const { inlineImagePaths, pathOnlyPaths } = splitAttachmentMarkers(attachments, inlineImages);
398
+ for (const path of inlineImagePaths) {
399
+ parts.push(`[Attached image (contents included in this message): ${path}]`);
400
+ }
401
+ if (pathOnlyPaths.length > 0) {
402
+ const fileRefs = pathOnlyPaths.join("\n");
367
403
  parts.push(
368
- fileAtts.length === 1
404
+ pathOnlyPaths.length === 1
369
405
  ? `[Attached file: ${fileRefs}]`
370
406
  : `[Attached files:\n${fileRefs}\n]`,
371
407
  );
@@ -379,8 +415,11 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
379
415
 
380
416
  const handleSend = useCallback(
381
417
  async (content: string, attachments: ChatAttachment[] = [], priority?: MessagePriority) => {
382
- const fullContent = buildMessageWithAttachments(content, attachments);
383
- if (!fullContent.trim()) return;
418
+ const images = selectInlineImages(attachments);
419
+ const fullContent = buildMessageWithAttachments(content, attachments, images);
420
+ // Providers that take a file rather than a payload (codex) read these instead.
421
+ const imagePaths = attachments.filter((a) => a.isImage && a.serverPath).map((a) => a.serverPath!);
422
+ if (!fullContent.trim() && images.length === 0) return;
384
423
 
385
424
  if (!sessionId) {
386
425
  try {
@@ -394,14 +433,14 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
394
433
  setSessionId(session.id);
395
434
  setProviderId(session.providerId);
396
435
  // Queue message — will be sent by effect when WS reports isConnected
397
- pendingSendRef.current = { content: fullContent, permissionMode };
436
+ pendingSendRef.current = { content: fullContent, permissionMode, images, imagePaths };
398
437
  return;
399
438
  } catch (e) {
400
439
  console.error("Failed to create session:", e);
401
440
  return;
402
441
  }
403
442
  }
404
- sendMessage(fullContent, { permissionMode, priority });
443
+ sendMessage(fullContent, { permissionMode, priority, ...(images.length > 0 && { images }), ...(imagePaths.length > 0 && { imagePaths }) });
405
444
  },
406
445
  [sessionId, providerId, projectName, sendMessage, buildMessageWithAttachments, permissionMode, metadata],
407
446
  );
@@ -730,6 +769,7 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
730
769
  permissionMode={permissionMode}
731
770
  onModeChange={setPermissionMode}
732
771
  providerId={providerId}
772
+ sessionId={sessionId ?? undefined}
733
773
  onProviderChange={!sessionId ? setProviderId : undefined}
734
774
  model={model}
735
775
  onModelChange={setModel}
@@ -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
 
@@ -72,6 +107,9 @@ interface MessageInputProps {
72
107
  onModeChange?: (mode: string) => void;
73
108
  /** Current provider ID */
74
109
  providerId?: string;
110
+ /** Live session id, when the tab has one. Scopes the slash list to the session's
111
+ * own skill runtime (codex resolves skills per account home + project cwd). */
112
+ sessionId?: string;
75
113
  /** Provider change handler — undefined when session is active (locked) */
76
114
  onProviderChange?: (providerId: string) => void;
77
115
  /** Current per-session model (null = provider default) */
@@ -112,6 +150,7 @@ export const MessageInput = memo(function MessageInput({
112
150
  permissionMode,
113
151
  onModeChange,
114
152
  providerId,
153
+ sessionId,
115
154
  onProviderChange,
116
155
  model,
117
156
  onModelChange,
@@ -293,7 +332,7 @@ export const MessageInput = memo(function MessageInput({
293
332
  onSlashItemsLoaded?.([], []);
294
333
  return;
295
334
  }
296
- fetchSlashItems(projectName)
335
+ fetchSlashItems(projectName, providerId, sessionId)
297
336
  .then((data) => {
298
337
  slashItemsRef.current = data.items;
299
338
  onSlashItemsLoaded?.(data.items, data.recentNames);
@@ -302,7 +341,7 @@ export const MessageInput = memo(function MessageInput({
302
341
  slashItemsRef.current = [];
303
342
  onSlashItemsLoaded?.([], []);
304
343
  });
305
- }, [projectName, onSlashItemsLoaded]);
344
+ }, [projectName, providerId, sessionId, onSlashItemsLoaded]);
306
345
 
307
346
  // Load when projectName changes (cache hit after the first tab in a project)
308
347
  useEffect(() => { loadSlashItems(); }, [loadSlashItems]);
@@ -369,10 +408,13 @@ export const MessageInput = memo(function MessageInput({
369
408
  return;
370
409
  }
371
410
 
372
- // Find the /query pattern before cursor and replace it with the command name
411
+ // Find the /query pattern before cursor and replace it with the command name.
412
+ // The item's own sigil is used, not a hardcoded `/`: a codex skill is invoked
413
+ // as `$imagegen`, so the composer must show the text that will actually be
414
+ // sent rather than leaving the server to silently rewrite it.
373
415
  const replaced = textBefore.replace(/(?:^|\s)\/\S*$/, (match) => {
374
416
  const prefix = stripTrigger(match);
375
- return `${prefix}/${slashSelected.name} `;
417
+ return `${prefix}${slashSelected.invokeSigil ?? "/"}${slashSelected.name} `;
376
418
  });
377
419
  writeTextareas(replaced + textAfter);
378
420
  onSlashStateChange?.(false, "");
@@ -436,6 +478,27 @@ export const MessageInput = memo(function MessageInput({
436
478
  onExternalPathsConsumed?.();
437
479
  }, [externalPaths]); // eslint-disable-line react-hooks/exhaustive-deps
438
480
 
481
+ /** Delete an upload the user removed before sending. Best effort — nothing depends on it. */
482
+ const discardUpload = useCallback(
483
+ async (serverPath: string) => {
484
+ if (!projectName) return;
485
+ const filename = serverPath.split(/[\\/]/).pop();
486
+ if (!filename) return;
487
+ try {
488
+ const headers: HeadersInit = {};
489
+ const token = getAuthToken();
490
+ if (token) headers["Authorization"] = `Bearer ${token}`;
491
+ await fetch(`${projectUrl(projectName)}/chat/uploads/${encodeURIComponent(filename)}`, {
492
+ method: "DELETE",
493
+ headers,
494
+ });
495
+ } catch {
496
+ // An orphaned file is not worth surfacing to the user.
497
+ }
498
+ },
499
+ [projectName],
500
+ );
501
+
439
502
  /** Upload a single file to the server, return server path */
440
503
  const uploadFile = useCallback(
441
504
  async (file: File): Promise<string | null> => {
@@ -466,32 +529,64 @@ export const MessageInput = memo(function MessageInput({
466
529
  /** Process files — always uploads to server. Path resolution only happens via @ picker. */
467
530
  const processFiles = useCallback(
468
531
  async (files: File[]) => {
469
- for (const file of files) {
532
+ for (const original of files) {
470
533
  const id = randomId();
471
- const isImg = isImageFile(file);
472
- const previewUrl = isImg ? URL.createObjectURL(file) : undefined;
534
+ const isImg = isImageFile(original);
473
535
 
474
536
  const att: ChatAttachment = {
475
537
  id,
476
- name: file.name,
477
- file,
538
+ name: original.name,
539
+ file: original,
478
540
  isImage: isImg,
479
- previewUrl,
541
+ previewUrl: isImg ? URL.createObjectURL(original) : undefined,
480
542
  status: "uploading",
481
543
  };
482
544
 
483
545
  setAttachments((prev) => [...prev, att]);
484
546
 
485
- // Upload in background
486
- uploadFile(file).then((serverPath) => {
547
+ // Shrink first, so the upload, the preview and the message all carry the same
548
+ // already-reduced image — an oversized original must not reach the transcript.
549
+ //
550
+ // Only a `scaled` or `inlineable` result is attached inline. An image that could not
551
+ // be measured or re-encoded travels by path alone: sending unverified dimensions
552
+ // inline risks a payload the API refuses, and that refusal outlives the turn, since
553
+ // the transcript replays it into every later one.
554
+ try {
555
+ const outcome = isImg ? await downscaleImage(original) : null;
556
+ const file = outcome?.file ?? original;
557
+ // Point the thumbnail at the reduced file and let the original go. It is the larger
558
+ // of the two and nothing needs it once the resize is done, but it stayed alive until
559
+ // the message was sent because the preview still referenced it.
560
+ const previewUrl = isImg && file !== original ? URL.createObjectURL(file) : undefined;
561
+ const serverPath = await uploadFile(file);
562
+ const inlineable = outcome ? outcome.kind !== "asis" : false;
563
+ const imageData = inlineable ? await readImageData(file) : undefined;
564
+ const withinPayloadCap =
565
+ !!imageData && imageData.data.length <= INLINE_IMAGE_LIMITS.maxBase64PerImage;
566
+
567
+ if (previewUrl && att.previewUrl) URL.revokeObjectURL(att.previewUrl);
487
568
  setAttachments((prev) =>
488
569
  prev.map((a) =>
489
570
  a.id === id
490
- ? { ...a, serverPath: serverPath ?? undefined, status: serverPath ? "ready" : "error" }
571
+ ? {
572
+ ...a,
573
+ file,
574
+ ...(previewUrl && { previewUrl }),
575
+ serverPath: serverPath ?? undefined,
576
+ imageData: withinPayloadCap ? imageData : undefined,
577
+ resized: outcome?.to ? { from: outcome.from!, to: outcome.to } : undefined,
578
+ // An image can still be sent inline when the upload failed; anything
579
+ // else has nothing left to send without its path.
580
+ status: serverPath || withinPayloadCap ? "ready" : "error",
581
+ }
491
582
  : a,
492
583
  ),
493
584
  );
494
- });
585
+ } catch {
586
+ // Nothing here may leave an attachment stuck on "uploading" — both the send button
587
+ // and the auto-send effect wait on that state, so a swallowed throw hangs the composer.
588
+ setAttachments((prev) => prev.map((a) => (a.id === id ? { ...a, status: "error" } : a)));
589
+ }
495
590
  }
496
591
  (mobileTextareaRef.current ?? textareaRef.current)?.focus();
497
592
  },
@@ -502,9 +597,12 @@ export const MessageInput = memo(function MessageInput({
502
597
  setAttachments((prev) => {
503
598
  const att = prev.find((a) => a.id === id);
504
599
  if (att?.previewUrl) URL.revokeObjectURL(att.previewUrl);
600
+ // Uploads are never swept, because chat history points at them indefinitely, so one
601
+ // abandoned before it was ever sent would sit there with nothing referencing it.
602
+ if (att?.serverPath) void discardUpload(att.serverPath);
505
603
  return prev.filter((a) => a.id !== id);
506
604
  });
507
- }, []);
605
+ }, [discardUpload]);
508
606
 
509
607
  /** Execute the actual send (called directly or after uploads complete) */
510
608
  const executeSend = useCallback(() => {
@@ -1115,6 +1115,20 @@ function InterleavedEvents({ events, isStreaming, projectName, bashPartialOutput
1115
1115
  if (event.type === "text") {
1116
1116
  textBuffer += event.content;
1117
1117
  } else if (event.type === "tool_use") {
1118
+ // A call may be announced before it has anything to show and re-announced
1119
+ // once it does — image generation starts with no file and no prompt, and
1120
+ // only learns both when it finishes. The later event describes the same
1121
+ // call, so it replaces the card rather than adding a second one.
1122
+ const useId = (event as any).toolUseId;
1123
+ const existing = useId
1124
+ ? groups.find(
1125
+ (g) => g.kind === "tool" && g.tool.type === "tool_use" && (g.tool as any).toolUseId === useId,
1126
+ ) as (EventGroup & { kind: "tool" }) | undefined
1127
+ : undefined;
1128
+ if (existing) {
1129
+ existing.tool = event;
1130
+ continue;
1131
+ }
1118
1132
  if (textBuffer) {
1119
1133
  groups.push({ kind: "text", content: textBuffer });
1120
1134
  textBuffer = "";
@@ -147,7 +147,8 @@ function TranscriptImages({ sessionId, projectName }: { sessionId: string; proje
147
147
  {audit.oversizedAttachments} of those {audit.oversizedAttachments === 1 ? "is an image you" : "are images you"}
148
148
  {" "}attached yourself, so the buttons below leave {audit.oversizedAttachments === 1 ? "it" : "them"} in place.
149
149
  While {audit.oversizedAttachments === 1 ? "it stays" : "they stay"}, every turn in this session keeps failing.
150
- The transcript holds the only copy — removing {audit.oversizedAttachments === 1 ? "it" : "them"} cannot be undone.
150
+ Removing {audit.oversizedAttachments === 1 ? "it" : "them"} cannot be undone: an attachment PPM sent keeps
151
+ a copy on disk with its path still in the message, but one from another client does not.
151
152
  </span>
152
153
  </div>
153
154
  <Button
@@ -15,6 +15,12 @@ export interface SlashItem {
15
15
  model?: string;
16
16
  /** Built-in only: which layer executes the command */
17
17
  handler?: "ppm" | "sdk" | "client";
18
+ /** Sigil the runtime needs; absent means `/`. Codex skills carry `$`. */
19
+ invokeSigil?: "/" | "$";
20
+ /** Pretty label the runtime supplies for itself (codex system skills). */
21
+ displayName?: string;
22
+ /** Runtime-supplied icon, inlined as a data URI by the server. */
23
+ iconDataUri?: string;
18
24
  }
19
25
 
20
26
  interface SlashCommandPickerProps {
@@ -181,7 +187,11 @@ export function SlashCommandPicker({
181
187
  onClick={() => onSelect(item)}
182
188
  >
183
189
  <span className="shrink-0 mt-0.5">
184
- {item.type === "builtin" ? (
190
+ {item.iconDataUri ? (
191
+ // The runtime shipped its own artwork (codex system skills).
192
+ // Decorative: the name beside it already labels the row.
193
+ <img src={item.iconDataUri} alt="" className="size-4 object-contain" />
194
+ ) : item.type === "builtin" ? (
185
195
  <Zap className="size-4 text-emerald-500" />
186
196
  ) : item.type === "skill" ? (
187
197
  <Sparkles className="size-4 text-warning" />
@@ -193,12 +203,21 @@ export function SlashCommandPicker({
193
203
  </span>
194
204
  <div className="min-w-0 flex-1">
195
205
  <div className="flex items-baseline gap-2 min-w-0">
196
- <span className="font-medium text-sm truncate">/{item.name}</span>
206
+ {/* Invocation string, shown with the sigil that will actually be
207
+ sent — codex resolves its skills from `$name`, not `/name`. */}
208
+ <span className="font-medium text-sm truncate">
209
+ {item.invokeSigil ?? "/"}{item.name}
210
+ </span>
211
+ {item.displayName && item.displayName !== item.name && (
212
+ <span className="text-xs text-text-2 truncate">{item.displayName}</span>
213
+ )}
197
214
  {item.argumentHint && (
198
215
  <span className="text-xs text-text-subtle truncate">{item.argumentHint}</span>
199
216
  )}
200
217
  <span className="text-xs text-text-subtle capitalize ml-auto shrink-0 whitespace-nowrap">
201
- {item.scope === "bundled" ? "PPM" : item.scope === "user" ? "global" : item.type}
218
+ {item.invokeSigil === "$"
219
+ ? "Codex"
220
+ : item.scope === "bundled" ? "PPM" : item.scope === "user" ? "global" : item.type}
202
221
  </span>
203
222
  </div>
204
223
  {item.description && (