@hienlh/ppm 0.21.0 → 0.22.0
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.
- package/CHANGELOG.md +41 -0
- package/CLAUDE.md +29 -4
- package/THIRD-PARTY-NOTICES.md +1 -1
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +1 -1
- package/bun.lock +16 -1
- package/dist/web/assets/accounts-settings-section-BVr9-FZJ.js +1 -0
- package/dist/web/assets/{ai-resource-editor-F09pf8Hd.js → ai-resource-editor-CdDpOwjh.js} +1 -1
- package/dist/web/assets/ai-settings-section-B1hQyjAd.js +1 -0
- package/dist/web/assets/ai-settings-section-X2UOfSaS.js +1 -0
- package/dist/web/assets/{angular-html-DkR4iCth.js → angular-html-G66FNlyn.js} +1 -1
- package/dist/web/assets/{angular-ts-DcyL4Chx.js → angular-ts-B-PnUMyt.js} +1 -1
- package/dist/web/assets/{api-client-bfB7cXRM.js → api-client-C1ZwRL4Y.js} +1 -1
- package/dist/web/assets/api-client-pQL53cDQ.js +1 -0
- package/dist/web/assets/{api-settings-BcNjZIvG.js → api-settings-_7Q-ULxp.js} +1 -1
- package/dist/web/assets/{api-themes-DBfyVUtX.js → api-themes-DRAERo4Q.js} +1 -1
- package/dist/web/assets/{apl-BVlrVtjt.js → apl-BmDqqwE8.js} +1 -1
- package/dist/web/assets/{appearance-settings-section-whgnq3ss.js → appearance-settings-section-DLoPK0LD.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-Dx6nBEKT.js +1 -0
- package/dist/web/assets/{astro-MjJlozLj.js → astro-BfwMNfxr.js} +1 -1
- package/dist/web/assets/{audio-preview-Bc54GCAV.js → audio-preview-DsYvgKqj.js} +1 -1
- package/dist/web/assets/{blade-DnJWi1fz.js → blade-BsFV4pnN.js} +1 -1
- package/dist/web/assets/branch-review-tab-G84Ojzae.js +1 -0
- package/dist/web/assets/{bsl-DCmJxSIz.js → bsl-Cw9Outng.js} +1 -1
- package/dist/web/assets/{button-VW4QGKCn.js → button-B9CTrsEf.js} +1 -1
- package/dist/web/assets/{cairo-C8dDenub.js → cairo-DOK5Wqnd.js} +1 -1
- package/dist/web/assets/chat-tab-DQJfjm8E.js +15 -0
- package/dist/web/assets/{cobol-DPx_nifl.js → cobol-DRaRy4FK.js} +1 -1
- package/dist/web/assets/code-editor-D04iUPsZ.js +7 -0
- package/dist/web/assets/{codex-daily-guard-CfybHv_c.js → codex-daily-guard-BSccLRGT.js} +1 -1
- package/dist/web/assets/{coffee-DvFzqhgN.js → coffee-DvXuBmEJ.js} +1 -1
- package/dist/web/assets/{conflict-editor-DenQUOT2.js → conflict-editor-C4ay676e.js} +2 -2
- package/dist/web/assets/context-menu-C6pIsf83.js +1 -0
- package/dist/web/assets/{cpp-BLJN0umi.js → cpp-zCmm8rKA.js} +1 -1
- package/dist/web/assets/{crystal-BXR-9fGw.js → crystal-CdQx6lB6.js} +1 -1
- package/dist/web/assets/{csv-preview-CClrJOuR.js → csv-preview-DbwEmG7o.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-D4fhcMfT.js → data-grid-overlay-editor-6xUCeLHh.js} +1 -1
- package/dist/web/assets/{database-viewer-DItjm8wB.js → database-viewer-S6LsALjX.js} +1 -1
- package/dist/web/assets/{dialog-DQTn55Mt.js → dialog-B84_DJQ8.js} +1 -1
- package/dist/web/assets/diff-viewer-3oBA7vgQ.js +4 -0
- package/dist/web/assets/{docx-preview-DYBkiUaE.js → docx-preview-CXuk-emm.js} +1 -1
- package/dist/web/assets/{edge-rWOlCmXT.js → edge-E8QLt7oi.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-COtmo54G.js → edit-diff-preview-BGGGecVw.js} +1 -1
- package/dist/web/assets/{editor-language-service-DxkZ51E5.js → editor-language-service-DJmaKVY0.js} +1 -1
- package/dist/web/assets/{elixir-E7Y82okW.js → elixir-DPb_-72P.js} +1 -1
- package/dist/web/assets/{elm-DTPGbc-w.js → elm-BMzJY5b0.js} +1 -1
- package/dist/web/assets/{erb-DHqLHrE6.js → erb-AABQNtMK.js} +1 -1
- package/dist/web/assets/{erlang-Byg0nNiV.js → erlang-CEIKOkfP.js} +1 -1
- package/dist/web/assets/{esm-D_DmOM7n.js → esm-B9RVMpwV.js} +1 -1
- package/dist/web/assets/explorer-body-BQ0jyAGv.js +2 -0
- package/dist/web/assets/explorer-window-content-jdkWt34J.js +1 -0
- package/dist/web/assets/{extension-manager-section-BhwB6nqr.js → extension-manager-section-DG1guILi.js} +1 -1
- package/dist/web/assets/{extension-webview-DI4VL2tJ.js → extension-webview-BGSDUbdK.js} +1 -1
- package/dist/web/assets/file-icons-BXdvggK2.js +1 -0
- package/dist/web/assets/file-icons.generated-BtBErEwb.css +1 -0
- package/dist/web/assets/file-store-ojgMXgAw.js +1 -0
- package/dist/web/assets/files-settings-section-Gnyd7han.js +1 -0
- package/dist/web/assets/{format-file-meta-DOpzJJhS.js → format-file-meta-C77h04cZ.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-CY1n0Xf6.js → fortran-fixed-form-CrXvLHyi.js} +1 -1
- package/dist/web/assets/{fsharp-_vEDSCJz.js → fsharp-DCI4qAz_.js} +1 -1
- package/dist/web/assets/{gdresource-B6XT0wAC.js → gdresource-Vb2kO1MG.js} +1 -1
- package/dist/web/assets/{general-settings-section-BcxwiZ0X.js → general-settings-section-V6Khh15V.js} +1 -1
- package/dist/web/assets/{git-commit-Wk9ovq59.js → git-commit-BMI_OWn-.js} +1 -1
- package/dist/web/assets/git-log-panel-Bi513qjT.js +1 -0
- package/dist/web/assets/{git-rebase-DSarGPvK.js → git-rebase-D-d_2iqZ.js} +1 -1
- package/dist/web/assets/{git-repo-store-Dy69YCHN.js → git-repo-store-88Z8v-pF.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-C6qRruHI.js +1 -0
- package/dist/web/assets/{glide-data-grid-BvgAqSUr.js → glide-data-grid-DnQwYSmQ.js} +7 -7
- package/dist/web/assets/{glimmer-js-CkYI35_P.js → glimmer-js-BmNNiR0Z.js} +1 -1
- package/dist/web/assets/{glimmer-ts-BqmIZcds.js → glimmer-ts-DQE2Ufha.js} +1 -1
- package/dist/web/assets/{glsl-Be-yBCF5.js → glsl-BcIDsxRJ.js} +1 -1
- package/dist/web/assets/{graphql-a5DrENHY.js → graphql-DGKSLmGH.js} +1 -1
- package/dist/web/assets/group-chat-tab-DpczdwyV.js +1 -0
- package/dist/web/assets/{hack-BLAg20aD.js → hack-YiC8bEIb.js} +1 -1
- package/dist/web/assets/{haml-Dj6bGOQH.js → haml-DjblxAsz.js} +1 -1
- package/dist/web/assets/{handlebars-dmEa36Oh.js → handlebars-BQepOXaU.js} +1 -1
- package/dist/web/assets/{html-yacZYzSE.js → html-Cayj_2GP.js} +1 -1
- package/dist/web/assets/{html-derivative-lLi3-RNT.js → html-derivative-Weq79bL6.js} +1 -1
- package/dist/web/assets/{http-1KaxcE4e.js → http-BeLfdGdz.js} +1 -1
- package/dist/web/assets/{hurl-ST-dEEZR.js → hurl-D9rypCLK.js} +1 -1
- package/dist/web/assets/{hxml-CpDtuq__.js → hxml-clG8mmSH.js} +1 -1
- package/dist/web/assets/{image-preview-CJVxL0uH.js → image-preview-CzCc4_Do.js} +1 -1
- package/dist/web/assets/image-preview-geometry-9lgeLYB3.js +1 -0
- package/dist/web/assets/index-BH99TIE0.css +1 -0
- package/dist/web/assets/index-DU68xk06.js +52 -0
- package/dist/web/assets/info-3K5VOQVL-BOSpdwlz.js +1 -0
- package/dist/web/assets/{jinja-Cjw-evAn.js → jinja-BShBWfs5.js} +1 -1
- package/dist/web/assets/{jira-watcher-section-DIC1yIV3.js → jira-watcher-section-f2HUbGDY.js} +1 -1
- package/dist/web/assets/{jison-DfATMYoY.js → jison-dN2Nf_Dr.js} +1 -1
- package/dist/web/assets/{julia-DDCaxiqT.js → julia-5U0vR-uJ.js} +1 -1
- package/dist/web/assets/{just-CJL529u6.js → just-BMppVRy6.js} +1 -1
- package/dist/web/assets/keybindings-store-DCRsnKg5.js +1 -0
- package/dist/web/assets/{keyboard-shortcuts-section-BW03P3AF.js → keyboard-shortcuts-section-BpvVEw_Y.js} +1 -1
- package/dist/web/assets/{latex-Zc4EpLpW.js → latex-DSSOKjis.js} +1 -1
- package/dist/web/assets/{liquid-B_6KMNYS.js → liquid-C24dKoyI.js} +1 -1
- package/dist/web/assets/{lua-BtS2idK7.js → lua-DNOsnyhn.js} +1 -1
- package/dist/web/assets/markdown-renderer-Bcaf9_bA.js +1 -0
- package/dist/web/assets/markdown-renderer-DGJp8HC3.js +3 -0
- package/dist/web/assets/{marko-Wk6LBnck.js → marko-26MHO_Q9.js} +1 -1
- package/dist/web/assets/{mdc-cRaXBrzL.js → mdc-Dhw9mGrC.js} +1 -1
- package/dist/web/assets/{media-url-DL8OCGGg.js → media-url-yfqHpPz0.js} +1 -1
- package/dist/web/assets/{mobile-bottom-sheet-CoUauvLF.js → mobile-bottom-sheet-C3TniEP-.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-Bol3XAO1.js +1 -0
- package/dist/web/assets/{nextflow-Bos2wvDV.js → nextflow-xH4oJTex.js} +1 -1
- package/dist/web/assets/{nginx-DMlOwzdr.js → nginx-CMz4w0M8.js} +1 -1
- package/dist/web/assets/{nim-D2dZQA88.js → nim-D8FnIYsx.js} +1 -1
- package/dist/web/assets/notification-store-BXt54VIn.js +1 -0
- package/dist/web/assets/{notifications-settings-section-DPUROMw-.js → notifications-settings-section-DqlPDfNX.js} +1 -1
- package/dist/web/assets/{number-overlay-editor-DZ2i_Bnb.js → number-overlay-editor-CRGwULhR.js} +1 -1
- package/dist/web/assets/{overview-panel-R146OIom.js → overview-panel-Di-3aV9p.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-BGIE1QdU.js +1 -0
- package/dist/web/assets/panel-store-CboGJAwh.js +1 -0
- package/dist/web/assets/{pdf-preview-BWUPGLp6.js → pdf-preview-CmBI6E7d.js} +1 -1
- package/dist/web/assets/{perl-C5-Z6fPn.js → perl-D9iNWYwb.js} +1 -1
- package/dist/web/assets/{php-BXNNhkMH.js → php-Bw3Qo0Zv.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-Dg-QTbXX.js +1 -0
- package/dist/web/assets/{postgres-viewer-CjOvaruz.js → postgres-viewer-8ZAqEALv.js} +3 -3
- package/dist/web/assets/{ppmbot-settings-section-DrvWh8l8.js → ppmbot-settings-section-DKJUA3Kq.js} +1 -1
- package/dist/web/assets/problems-panel-CFcoUx8h.js +1 -0
- package/dist/web/assets/{process-table-BRMS1MrX.js → process-table-EKr2dqG6.js} +1 -1
- package/dist/web/assets/project-framework-store-B70Q3dNM.js +1 -0
- package/dist/web/assets/project-store-B87hLRSk.js +1 -0
- package/dist/web/assets/{proxy-settings-section-Bv0Ny-Yy.js → proxy-settings-section-B7qRVl8w.js} +1 -1
- package/dist/web/assets/{pug-C1zf1yWy.js → pug-fT5JC6Vm.js} +1 -1
- package/dist/web/assets/{qml-OW58jGpD.js → qml-P_wEDB1w.js} +1 -1
- package/dist/web/assets/{query-audit-section-BcHJZcVC.js → query-audit-section-By6qyqdB.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-DZ6IVu2s.js +1 -0
- package/dist/web/assets/{razor-DzjSaobM.js → razor-Drrt2_Ph.js} +1 -1
- package/dist/web/assets/remote-desktop-clipboard-notice-0WDYsyCA.js +1 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-COYDg16H.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-view-2DqSuS2U.js +1 -0
- package/dist/web/assets/{remote-desktop-readiness-gate-BTzss2wX.js → remote-desktop-readiness-gate-Dm_qMJMH.js} +1 -1
- package/dist/web/assets/remote-desktop-window-content-UodhsF7W.js +1 -0
- package/dist/web/assets/{rst-RfuihOUW.js → rst-Dr2mW4-h.js} +1 -1
- package/dist/web/assets/{ruby-CNO3pli1.js → ruby-C6EpUsTY.js} +1 -1
- package/dist/web/assets/{run-in-terminal-Cb_hVU8x.js → run-in-terminal-CdefEe_R.js} +1 -1
- package/dist/web/assets/{sas-DZY5BtXx.js → sas-D8ZEeP6v.js} +1 -1
- package/dist/web/assets/{schedules-settings-section-DO8xAOVj.js → schedules-settings-section-bG2lYfGv.js} +1 -1
- package/dist/web/assets/{scroll-area-LtNV8g5o.js → scroll-area-BzeiOd-B.js} +1 -1
- package/dist/web/assets/{scss--4g-REcN.js → scss-ITL7jQ1f.js} +1 -1
- package/dist/web/assets/{send-to-chat-Cnpru1M0.js → send-to-chat-F207yH3u.js} +1 -1
- package/dist/web/assets/{separator-Cds1yxy8.js → separator-TRMsJUYe.js} +1 -1
- package/dist/web/assets/settings-body-C06bFarv.js +2 -0
- package/dist/web/assets/settings-store-krC6bc79.js +2 -0
- package/dist/web/assets/settings-tab-34RSaASd.js +1 -0
- package/dist/web/assets/settings-window-content-Dr_2lmHH.js +1 -0
- package/dist/web/assets/{shaderlab-Cu4sBiVn.js → shaderlab-mW-IIlhk.js} +1 -1
- package/dist/web/assets/{shellsession-DWzJfEv7.js → shellsession-DhNl5STt.js} +1 -1
- package/dist/web/assets/{soy-BnqE-5Ff.js → soy-SdY0Qjtr.js} +1 -1
- package/dist/web/assets/{sparql-Ctg__NhZ.js → sparql-ftqZo2i-.js} +1 -1
- package/dist/web/assets/{sql-query-editor-D9k19T_M.js → sql-query-editor-BgRH0cHD.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-DoV9hnWJ.js → sqlite-viewer-DUxGslpa.js} +1 -1
- package/dist/web/assets/{stata-Qk2h66XW.js → stata-Bj7Yjb4b.js} +1 -1
- package/dist/web/assets/{surrealql-8QEX7EfH.js → surrealql-CLhYGAfB.js} +1 -1
- package/dist/web/assets/{svelte-D_mubC5-.js → svelte-DTAlRHhK.js} +1 -1
- package/dist/web/assets/system-monitor-body-IZuLn7Qh.js +2 -0
- package/dist/web/assets/system-monitor-tab-DiSA490Z.js +1 -0
- package/dist/web/assets/{system-monitor-window-content-qXLcicFM.js → system-monitor-window-content-CEaFISgj.js} +1 -1
- package/dist/web/assets/{tab-host-window-content-7fPZNEni.js → tab-host-window-content-D0TN8Wrq.js} +1 -1
- package/dist/web/assets/{tab-store-DGJGnbwv.js → tab-store-OJa1GPCq.js} +1 -1
- package/dist/web/assets/team-member-sheet-DDPNcCjd.js +2 -0
- package/dist/web/assets/{team-member-window-content-BgvJFypM.js → team-member-window-content-DCiW_1-E.js} +1 -1
- package/dist/web/assets/{templ-C1jV0fsO.js → templ-BMfhACxb.js} +1 -1
- package/dist/web/assets/{terminal-tab-Cs0sGljx.js → terminal-tab-4ypP0NDY.js} +3 -3
- package/dist/web/assets/{tex-D_StFXy_.js → tex-BcssM-QA.js} +1 -1
- package/dist/web/assets/tool-cards-Dd6E7Q3k.js +9 -0
- package/dist/web/assets/treemap-KZPCXAKY-CE30wCXa.js +1 -0
- package/dist/web/assets/{ts-tags-B5m47rEm.js → ts-tags-CrR5IYI8.js} +1 -1
- package/dist/web/assets/{twig-C_br6GIl.js → twig-DchfUmyI.js} +1 -1
- package/dist/web/assets/{use-blob-url-C-SGi_Hb.js → use-blob-url-CPtCcY3i.js} +1 -1
- package/dist/web/assets/{use-inline-blame-B4trxGcR.js → use-inline-blame-vrfFHuaR.js} +1 -1
- package/dist/web/assets/{use-lsp-CV0vzILO.js → use-lsp-CsuZu49X.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-DBqbWjWX.js → use-monaco-theme-BIVYJBIn.js} +1 -1
- package/dist/web/assets/{use-open-team-member-DENsjp0s.js → use-open-team-member-C3mrUzIC.js} +1 -1
- package/dist/web/assets/{use-websocket-LgXiX1qN.js → use-websocket-BMbufhX7.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-Cbig5zA6.js → vendor-mermaid-BitiXJlL.js} +1 -1
- package/dist/web/assets/{vendor-ui-BBdkypKW.js → vendor-ui-DmZ-Mb-H.js} +1 -1
- package/dist/web/assets/video-preview-Byj8P_jA.js +1 -0
- package/dist/web/assets/{vue-DQwCnLdr.js → vue-1EL6Pw1h.js} +1 -1
- package/dist/web/assets/{vue-html-T_EoGpPJ.js → vue-html-BPQNwPC6.js} +1 -1
- package/dist/web/assets/{vue-vine-DmxLqX50.js → vue-vine-Bb_dsWLt.js} +1 -1
- package/dist/web/assets/{window-pip-registry-3l4hoxuD.js → window-pip-registry-yD6esSju.js} +1 -1
- package/dist/web/assets/ws-auth-CEYVQ8NH.js +1 -0
- package/dist/web/assets/{xml-Bs6Bw8is.js → xml-dxkLNA0K.js} +1 -1
- package/dist/web/assets/{xsl-f5ZNhRCc.js → xsl-Bw2adwrl.js} +1 -1
- package/dist/web/index.html +27 -26
- package/dist/web/sw.js +1 -1
- package/docs/architecture/plugins-and-tracing.md +285 -0
- package/docs/lessons-learned.md +31 -0
- package/docs/system-architecture.md +1 -0
- package/package.json +2 -1
- package/packages/ext-git-graph/src/compare-diff-base.test.ts +173 -0
- package/packages/ext-git-graph/src/compare-view.ts +22 -3
- package/packages/ext-git-graph/src/extension.ts +6 -0
- package/packages/ext-git-graph/src/types.ts +19 -0
- package/packages/ext-git-graph/src/webview-html.test.ts +134 -1
- package/packages/ext-git-graph/src/webview-html.ts +277 -27
- package/scripts/gen-file-icons.ts +493 -242
- package/src/providers/claude-agent-sdk.ts +42 -1
- package/src/providers/codex-app-server/codex-history.ts +77 -23
- package/src/server/index.ts +42 -7
- package/src/server/routes/chat.ts +15 -11
- package/src/server/routes/git.ts +83 -3
- package/src/server/routes/remote-desktop.ts +7 -2
- package/src/server/ws/global.ts +1 -1
- package/src/server/ws/remote-desktop.ts +12 -5
- package/src/services/binary-content.ts +25 -0
- package/src/services/chat-search.service.ts +1 -1
- package/src/services/chat.service.ts +9 -0
- package/src/services/event-loop-lag.ts +208 -0
- package/src/services/file-lines.ts +114 -0
- package/src/services/file-watcher/recreated-dir-poller.ts +108 -6
- package/src/services/file-watcher/watch-tree.ts +131 -10
- package/src/services/file-watcher.service.ts +73 -23
- package/src/services/git-branch-diff/branch-diff-parse.ts +151 -0
- package/src/services/git-branch-diff/branch-diff.service.ts +79 -0
- package/src/services/git-hunks/git-hunks.service.ts +26 -11
- package/src/services/git-hunks/unified-diff.ts +15 -1
- package/src/services/git.service.ts +74 -24
- package/src/services/jsonl-transcript-parser.ts +138 -6
- package/src/services/log-rotate.ts +142 -0
- package/src/services/media-transcode/ffmpeg-capabilities.ts +72 -2
- package/src/services/remote-desktop/remote-desktop-audio.ts +193 -0
- package/src/services/remote-desktop/remote-desktop-capture-input.ts +87 -14
- package/src/services/remote-desktop/remote-desktop-capture.ts +40 -7
- package/src/services/remote-desktop/remote-desktop-clipboard.ts +166 -0
- package/src/services/remote-desktop/remote-desktop-displays.ts +77 -1
- package/src/services/remote-desktop/remote-desktop-encoder-args.ts +25 -6
- package/src/services/remote-desktop/remote-desktop-evdev-key-map.ts +69 -0
- package/src/services/remote-desktop/remote-desktop-input-linux.ts +253 -0
- package/src/services/remote-desktop/remote-desktop-input-uinput.ts +242 -0
- package/src/services/remote-desktop/remote-desktop-input.ts +40 -8
- package/src/services/remote-desktop/remote-desktop-linux-session.ts +146 -0
- package/src/services/remote-desktop/remote-desktop-ogg-opus.ts +144 -0
- package/src/services/remote-desktop/remote-desktop-privacy.ts +163 -0
- package/src/services/remote-desktop/remote-desktop-quality.ts +138 -0
- package/src/services/remote-desktop/remote-desktop-requirements.ts +158 -5
- package/src/services/remote-desktop/remote-desktop-resolution.ts +302 -0
- package/src/services/remote-desktop/remote-desktop-session.ts +523 -21
- package/src/services/remote-desktop/remote-desktop-x11.ts +250 -0
- package/src/services/supervisor.ts +47 -4
- package/src/shared/remote-desktop-custom-quality.ts +117 -0
- package/src/shared/remote-desktop-quality.ts +127 -0
- package/src/types/chat.ts +4 -0
- package/src/types/git.ts +52 -0
- package/src/web/components/branch-review/branch-review-tab.tsx +360 -0
- package/src/web/components/branch-review/branch-review-tree-row.tsx +138 -0
- package/src/web/components/chat/attachment-chips.tsx +30 -6
- package/src/web/components/chat/chat-tab.tsx +12 -1
- package/src/web/components/chat/message-events.tsx +273 -0
- package/src/web/components/chat/message-list.tsx +107 -861
- package/src/web/components/chat/message-markdown.tsx +34 -0
- package/src/web/components/chat/message-media.tsx +174 -0
- package/src/web/components/chat/message-user-bubble.tsx +399 -0
- package/src/web/components/chat/provider-selector.tsx +30 -22
- package/src/web/components/chat/session-history-list.tsx +2 -2
- package/src/web/components/chat/session-list-panel.tsx +3 -2
- package/src/web/components/editor/binary-diff-view.tsx +212 -0
- package/src/web/components/editor/diff-viewer.tsx +76 -3
- package/src/web/components/explorer/file-tree.tsx +235 -134
- package/src/web/components/explorer/flatten-visible-tree.ts +35 -40
- package/src/web/components/explorer/tree-node.tsx +69 -69
- package/src/web/components/explorer/use-tree-row-dnd.ts +3 -4
- package/src/web/components/git/git-status-panel.tsx +25 -0
- package/src/web/components/layout/draggable-tab.tsx +4 -2
- package/src/web/components/layout/nav-section-rail.tsx +12 -3
- package/src/web/components/layout/status-bar.tsx +10 -6
- package/src/web/components/layout/tab-content.tsx +5 -0
- package/src/web/components/layout/tab-pool.tsx +1 -0
- package/src/web/components/layout/upgrade-button.tsx +5 -1
- package/src/web/components/remote-desktop/remote-desktop-clipboard-client.ts +67 -0
- package/src/web/components/remote-desktop/remote-desktop-clipboard-notice.tsx +127 -0
- package/src/web/components/remote-desktop/remote-desktop-codec-labels.ts +28 -0
- package/src/web/components/remote-desktop/remote-desktop-custom-quality-dialog.tsx +144 -0
- package/src/web/components/remote-desktop/remote-desktop-mobile-key-bar.tsx +13 -2
- package/src/web/components/remote-desktop/remote-desktop-mobile-settings.tsx +232 -0
- package/src/web/components/remote-desktop/remote-desktop-mobile-toolbar.tsx +21 -8
- package/src/web/components/remote-desktop/remote-desktop-mobile-view.tsx +82 -3
- package/src/web/components/remote-desktop/remote-desktop-recording.ts +76 -0
- package/src/web/components/remote-desktop/remote-desktop-resolution-list.ts +100 -0
- package/src/web/components/remote-desktop/remote-desktop-toolbar.tsx +497 -0
- package/src/web/components/remote-desktop/remote-desktop-view-style.ts +145 -0
- package/src/web/components/remote-desktop/remote-desktop-window-content.tsx +126 -35
- package/src/web/components/remote-desktop/use-h264-canvas-decoder.ts +11 -0
- package/src/web/components/remote-desktop/use-opus-audio-player.ts +148 -0
- package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +284 -1
- package/src/web/components/remote-desktop/use-remote-desktop-readiness.ts +75 -1
- package/src/web/components/remote-desktop/use-remote-desktop-recorder.ts +103 -0
- package/src/web/components/remote-desktop/use-remote-desktop-virtual-keyboard.ts +19 -2
- package/src/web/components/remote-desktop/use-remote-input-capture.ts +39 -3
- package/src/web/components/settings/theme-picker.tsx +2 -1
- package/src/web/components/ui/tooltip.tsx +15 -2
- package/src/web/hooks/use-delayed-flag.ts +35 -0
- package/src/web/lib/branch-review-state.ts +175 -0
- package/src/web/lib/file-icon-name.ts +62 -0
- package/src/web/lib/file-icons.generated.ts +3287 -25
- package/src/web/lib/file-icons.tsx +71 -73
- package/src/web/lib/flatten-expansions.ts +18 -5
- package/src/web/lib/message-ordinals.ts +23 -0
- package/src/web/lib/provider-logos.tsx +143 -0
- package/src/web/lib/tab-type-icons.ts +11 -0
- package/src/web/stores/file-store.ts +73 -35
- package/src/web/stores/panel-utils.ts +5 -0
- package/src/web/stores/project-framework-store.ts +82 -0
- package/src/web/stores/project-store.ts +7 -1
- package/src/web/stores/settings-store.ts +128 -0
- package/src/web/stores/tab-store.ts +1 -0
- package/src/web/stores/window-panel-persistence.ts +1 -0
- package/src/web/styles/file-icons.generated.css +3282 -234
- package/src/web/styles/globals.css +26 -0
- package/dist/web/assets/accounts-settings-section-BuxD9zH3.js +0 -1
- package/dist/web/assets/ai-settings-section-Dd9tOUp0.js +0 -1
- package/dist/web/assets/ai-settings-section-FrU2X8Fu.js +0 -1
- package/dist/web/assets/api-client-BUCo1lms.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-5K0wEkAn.js +0 -1
- package/dist/web/assets/chat-tab-Cr6_KSGO.js +0 -15
- package/dist/web/assets/code-editor-DfmlGamv.js +0 -7
- package/dist/web/assets/context-menu-DOODKZdF.js +0 -1
- package/dist/web/assets/diff-viewer-BpNj7pN9.js +0 -4
- package/dist/web/assets/explorer-body-D-5z464e.js +0 -2
- package/dist/web/assets/explorer-window-content-7-1wuRul.js +0 -1
- package/dist/web/assets/file-icons-EzPL0q-k.js +0 -1
- package/dist/web/assets/file-icons.generated-CfaJuvZu.css +0 -1
- package/dist/web/assets/file-store-DMABxOkV.js +0 -1
- package/dist/web/assets/files-settings-section-CZzABuKz.js +0 -1
- package/dist/web/assets/git-log-panel-CRN4JVOx.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-DkVT2MAM.js +0 -1
- package/dist/web/assets/group-chat-tab-DFa5mAx5.js +0 -1
- package/dist/web/assets/index-D7DbXD3G.css +0 -1
- package/dist/web/assets/index-ZtfxZOyb.js +0 -52
- package/dist/web/assets/info-3K5VOQVL-BBsmKGzA.js +0 -1
- package/dist/web/assets/keybindings-store-tVfhPANS.js +0 -1
- package/dist/web/assets/markdown-renderer-CvL1pWeK.js +0 -3
- package/dist/web/assets/markdown-renderer-sJNNVhlS.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-CZ0HZ2E_.js +0 -1
- package/dist/web/assets/notification-store-BZbsPnW0.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-DwmYPOlx.js +0 -1
- package/dist/web/assets/panel-store-BVLBRn6M.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-D0oogIFi.js +0 -1
- package/dist/web/assets/problems-panel-B7BWVhdy.js +0 -1
- package/dist/web/assets/project-store-BhMbVln3.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-CQIJOQGa.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-ByjaeZKX.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-view-D13mtr9F.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-CbqTCaC6.js +0 -1
- package/dist/web/assets/settings-body-Rnu-FcX-.js +0 -2
- package/dist/web/assets/settings-store-DZPgPtEA.js +0 -2
- package/dist/web/assets/settings-tab-C6PHxs0Z.js +0 -1
- package/dist/web/assets/settings-window-content-CQ4gT2qj.js +0 -1
- package/dist/web/assets/system-monitor-body-2vQ1kMxE.js +0 -2
- package/dist/web/assets/system-monitor-tab-krAcvwF7.js +0 -1
- package/dist/web/assets/team-member-sheet-2FnRmk1a.js +0 -2
- package/dist/web/assets/tool-cards-DU_S2o51.js +0 -9
- package/dist/web/assets/treemap-KZPCXAKY-BhEtjljr.js +0 -1
- package/dist/web/assets/use-remote-desktop-display-choice-BK_f787P.js +0 -1
- package/dist/web/assets/video-preview-BDNwumbW.js +0 -1
- package/dist/web/assets/ws-auth-DEAJiGCR.js +0 -1
- /package/dist/web/assets/{abap-tkptwpsK.js → abap-IN2yINdN.js} +0 -0
- /package/dist/web/assets/{actionscript-3-Di9TMGwg.js → actionscript-3-C_t772eR.js} +0 -0
- /package/dist/web/assets/{ada-CB9F079D.js → ada-DPme3emm.js} +0 -0
- /package/dist/web/assets/{andromeeda-Dcl8jpb7.js → andromeeda-DSV4i4mZ.js} +0 -0
- /package/dist/web/assets/{apache-D0Nk0qsH.js → apache-CWr8s8I8.js} +0 -0
- /package/dist/web/assets/{apex-mQIoWncD.js → apex-Z20JnDF5.js} +0 -0
- /package/dist/web/assets/{applescript-C6ljbcsI.js → applescript-BKNPu3ga.js} +0 -0
- /package/dist/web/assets/{apply-theme-BwIMNXyX.js → apply-theme-CbR31K6I.js} +0 -0
- /package/dist/web/assets/{ara-WFqRPGPe.js → ara-hWdY3O96.js} +0 -0
- /package/dist/web/assets/{asciidoc-bNd8w4nf.js → asciidoc-eynhq496.js} +0 -0
- /package/dist/web/assets/{asm-BQ1_F580.js → asm-Bu02Kq4P.js} +0 -0
- /package/dist/web/assets/{aurora-x-Ciqs_Dbu.js → aurora-x-BHBGXbIZ.js} +0 -0
- /package/dist/web/assets/{awk-GCR-6KZV.js → awk-B4ojezyv.js} +0 -0
- /package/dist/web/assets/{ayu-dark-DxJXuj99.js → ayu-dark-D0GQzFi9.js} +0 -0
- /package/dist/web/assets/{ayu-light-jvSyJWDV.js → ayu-light-BVYb5Yn8.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-Cg3BPO6H.js → ayu-mirage-BGFW6oNb.js} +0 -0
- /package/dist/web/assets/{ballerina-BPz4CrX6.js → ballerina-B6Og3IKT.js} +0 -0
- /package/dist/web/assets/{bat-CSTs2hIG.js → bat-D8YFILfx.js} +0 -0
- /package/dist/web/assets/{beancount-DMateAZX.js → beancount-BjlbBBuX.js} +0 -0
- /package/dist/web/assets/{berry-9lXg0GRM.js → berry-Cz1ygeoj.js} +0 -0
- /package/dist/web/assets/{bibtex-DIbqmvFg.js → bibtex-CVkJceJh.js} +0 -0
- /package/dist/web/assets/{bicep-02WGsoJ8.js → bicep-DrhYI5GX.js} +0 -0
- /package/dist/web/assets/{bird2-BY0tbu3k.js → bird2-BV-KJtmc.js} +0 -0
- /package/dist/web/assets/{c-jMBX_9Je.js → c-CuLlvxbG.js} +0 -0
- /package/dist/web/assets/{c3-B8JVbNjW.js → c3-CI7-1CJ0.js} +0 -0
- /package/dist/web/assets/{cadence-DUIf1tvL.js → cadence-DjxhNP3Q.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-DGEc0s4P.js → can-open-in-ppm-cv5--yHX.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-FhUCVxex.js → catppuccin-frappe-CEozpJRh.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-CzfrtmgL.js → catppuccin-latte-C4sXQFMc.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-6xL0lOtj.js → catppuccin-macchiato-Ci-fDhXy.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-D39-xS3P.js → catppuccin-mocha-CH7YXw8d.js} +0 -0
- /package/dist/web/assets/{clarity-D8TeGvoT.js → clarity-BWrJ7UmT.js} +0 -0
- /package/dist/web/assets/{clojure-Dx_acSX7.js → clojure-C94fh2hb.js} +0 -0
- /package/dist/web/assets/{cmake-CyeYPq8o.js → cmake-CtvCkMsG.js} +0 -0
- /package/dist/web/assets/{codeowners-DrTI0lmI.js → codeowners-CJnubqzH.js} +0 -0
- /package/dist/web/assets/{codeql-CPkFxVW-.js → codeql-BOoP5HY2.js} +0 -0
- /package/dist/web/assets/{common-lisp-B0k-3LTj.js → common-lisp-B9nlMFJd.js} +0 -0
- /package/dist/web/assets/{coq-uygpo7to.js → coq-CgzIQiSH.js} +0 -0
- /package/dist/web/assets/{csharp-DKUYGfSp.js → csharp-4xAIx3jw.js} +0 -0
- /package/dist/web/assets/{css-DoOUp7v4.js → css-D4YoLYGn.js} +0 -0
- /package/dist/web/assets/{csv-B3YE13gi.js → csv-C2v-ZakW.js} +0 -0
- /package/dist/web/assets/{csv-parser-DXePqmXe.js → csv-parser-_oDBUkwQ.js} +0 -0
- /package/dist/web/assets/{cue-DViO2wvn.js → cue-Bbznh_an.js} +0 -0
- /package/dist/web/assets/{cypher-4BMCIW5N.js → cypher-BKhVEARF.js} +0 -0
- /package/dist/web/assets/{d-BT_sTHas.js → d-D6IhWopl.js} +0 -0
- /package/dist/web/assets/{dark-plus-DVVXu-1N.js → dark-plus-xAWu790O.js} +0 -0
- /package/dist/web/assets/{dart-DHz6f6_6.js → dart-Bj6S9O1j.js} +0 -0
- /package/dist/web/assets/{data-grid-types-OgUxMLdQ.js → data-grid-types-C5bthITf.js} +0 -0
- /package/dist/web/assets/{dax-ChYCjweK.js → dax-CF2zvaqq.js} +0 -0
- /package/dist/web/assets/{desktop-D6Lt4p04.js → desktop-CnmyxMKE.js} +0 -0
- /package/dist/web/assets/{diff-D8TDe4hP.js → diff-Qi8WuJsI.js} +0 -0
- /package/dist/web/assets/{dist-DOTlFQJA.js → dist-Bjseolz4.js} +0 -0
- /package/dist/web/assets/{dist-CrBOCtCW.js → dist-Czq7BUMy.js} +0 -0
- /package/dist/web/assets/{docker-Bsp0YbPx.js → docker-vLbbNP5d.js} +0 -0
- /package/dist/web/assets/{dotenv-B_7giSY_.js → dotenv-DJMvvNzn.js} +0 -0
- /package/dist/web/assets/{dracula-TrQL2iB5.js → dracula-CtAh2rhB.js} +0 -0
- /package/dist/web/assets/{dracula-soft-n2XOlgh-.js → dracula-soft-CXtuAAf6.js} +0 -0
- /package/dist/web/assets/{dream-maker-CdIEdhpT.js → dream-maker-D5YQyK49.js} +0 -0
- /package/dist/web/assets/{editor-font-BDrdiGf1.js → editor-font-ClTC9gaD.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-CSbixs6u.js → emacs-lisp-CGTjbiqF.js} +0 -0
- /package/dist/web/assets/{everforest-dark-BuZahZYp.js → everforest-dark-D59p7vAQ.js} +0 -0
- /package/dist/web/assets/{everforest-light-DPH53z5H.js → everforest-light-B3Qtud3V.js} +0 -0
- /package/dist/web/assets/{fennel-B1-K_hlJ.js → fennel-BCyHNUwf.js} +0 -0
- /package/dist/web/assets/{fish-Cy12Dbua.js → fish-DDDlLV2G.js} +0 -0
- /package/dist/web/assets/{fluent-B_82ys8E.js → fluent-By54R9tl.js} +0 -0
- /package/dist/web/assets/{format-bytes-D5sBgIqU.js → format-bytes-D1AQyUCt.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-BRQdNzZF.js → fortran-free-form-tP8mRym_.js} +0 -0
- /package/dist/web/assets/{gdscript-XBDmLUm9.js → gdscript-nHC2z6Te.js} +0 -0
- /package/dist/web/assets/{gdshader-DhVfB3u4.js → gdshader-K-cSmPlW.js} +0 -0
- /package/dist/web/assets/{genie-X3K29Y49.js → genie-BAcRhvEe.js} +0 -0
- /package/dist/web/assets/{gherkin-DKIx-6cX.js → gherkin-CcifxMLb.js} +0 -0
- /package/dist/web/assets/{github-dark-Bf7zdp5s.js → github-dark-67KKlMdB.js} +0 -0
- /package/dist/web/assets/{github-dark-default-DqsBBV11.js → github-dark-default-tja8pidR.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-X2gqRz6w.js → github-dark-dimmed-CtjhYQtx.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-Z7tvbGX4.js → github-dark-high-contrast-Bf-LPFgn.js} +0 -0
- /package/dist/web/assets/{github-light-BKxt-7zZ.js → github-light-CbJxE7n3.js} +0 -0
- /package/dist/web/assets/{github-light-default-B0liF4d4.js → github-light-default-DRQmdHxt.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-F4encJwr.js → github-light-high-contrast-CJkDgTeM.js} +0 -0
- /package/dist/web/assets/{gleam-Pz2bHgBB.js → gleam-D4LCl2ri.js} +0 -0
- /package/dist/web/assets/{gn-CgmdNsPU.js → gn-COY8zZmC.js} +0 -0
- /package/dist/web/assets/{gnuplot-qLNF5yZ9.js → gnuplot-ROk1VitR.js} +0 -0
- /package/dist/web/assets/{go-CSDUNhE7.js → go-H30c3p0C.js} +0 -0
- /package/dist/web/assets/{groovy-B3NEnOHF.js → groovy-DBLCaelV.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-DcoftiUX.js → gruvbox-dark-hard-DJy4irps.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-D1wVSuD8.js → gruvbox-dark-medium-Dhfw1QTO.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-DWpunymH.js → gruvbox-dark-soft-BncJU-8w.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-rHMvyUdS.js → gruvbox-light-hard-Cq12_YvM.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-3O2KiXO7.js → gruvbox-light-medium-DqVja5_v.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-lNlXJPwH.js → gruvbox-light-soft-IvwhveGF.js} +0 -0
- /package/dist/web/assets/{haskell-aaG3aC0h.js → haskell-DA1JV-MC.js} +0 -0
- /package/dist/web/assets/{haxe-CPcHhUDd.js → haxe-V9RTRc5E.js} +0 -0
- /package/dist/web/assets/{hcl-CMQNlR_4.js → hcl-hr1lyZ10.js} +0 -0
- /package/dist/web/assets/{hjson-DeuklPFj.js → hjson-D3vW3i7X.js} +0 -0
- /package/dist/web/assets/{hlsl-BI3hlsaM.js → hlsl-BB85gfJT.js} +0 -0
- /package/dist/web/assets/{horizon-C7-gOcjs.js → horizon-DU8OOnj8.js} +0 -0
- /package/dist/web/assets/{horizon-bright-Cxt9bRW7.js → horizon-bright-DNMUclW6.js} +0 -0
- /package/dist/web/assets/{houston-DbF2G7iD.js → houston-DAiZKf6T.js} +0 -0
- /package/dist/web/assets/{hy-DRG6Hs2I.js → hy-C6Zv0LMe.js} +0 -0
- /package/dist/web/assets/{image-gallery-CObzTisL.js → image-gallery-CGvlv8zR.js} +0 -0
- /package/dist/web/assets/{imba-CxKv_2TW.js → imba-Didnp6Y-.js} +0 -0
- /package/dist/web/assets/{ini-BiHsyCdO.js → ini-m0T7q4gQ.js} +0 -0
- /package/dist/web/assets/{input-D-dR8RI6.js → input-C7Q8FxeL.js} +0 -0
- /package/dist/web/assets/{java-D3RZzgDB.js → java-Buy07jGn.js} +0 -0
- /package/dist/web/assets/{javascript-4FmSqG-T.js → javascript-Cjq3CbRL.js} +0 -0
- /package/dist/web/assets/{json-CjB236_E.js → json-BSVu8IqD.js} +0 -0
- /package/dist/web/assets/{json5-bkfO-Rco.js → json5-NN_Vb7e6.js} +0 -0
- /package/dist/web/assets/{jsonc-BwaTeLyv.js → jsonc-BdyZhBUw.js} +0 -0
- /package/dist/web/assets/{jsonl-CfxNSwGd.js → jsonl-BsDSNvwi.js} +0 -0
- /package/dist/web/assets/{jsonnet-CQETCYrp.js → jsonnet-Gm963F-U.js} +0 -0
- /package/dist/web/assets/{jssm-D5o3Zzdz.js → jssm-Ds3bKWgJ.js} +0 -0
- /package/dist/web/assets/{jsx-BAMILPLo.js → jsx-BT5mW58X.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-BhGVxu3R.js → kanagawa-dragon-BvNvf7xd.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-FH4Y59ro.js → kanagawa-lotus-CuSiliSn.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-eilvP9MB.js → kanagawa-wave-C6g-d6jT.js} +0 -0
- /package/dist/web/assets/{kdl-BQ-yvXUK.js → kdl-Bx4kZef-.js} +0 -0
- /package/dist/web/assets/{kotlin-BuCx0-VT.js → kotlin-qvaVvCcm.js} +0 -0
- /package/dist/web/assets/{kusto-DAN_n_Cy.js → kusto-BPLyNCN6.js} +0 -0
- /package/dist/web/assets/{laserwave-Cq2BlxrT.js → laserwave-Sf8ZEhWP.js} +0 -0
- /package/dist/web/assets/{lean-Dhq7uXKw.js → lean-DGe6uqgk.js} +0 -0
- /package/dist/web/assets/{less-CxaMQb8j.js → less-CqM85x-9.js} +0 -0
- /package/dist/web/assets/{lib-BVwmtv2i.js → lib-CD6pOS4u.js} +0 -0
- /package/dist/web/assets/{libesm-DdtEjhIi.js → libesm-CPh1Av0r.js} +0 -0
- /package/dist/web/assets/{light-plus-B4sMAend.js → light-plus-DBtKwhFA.js} +0 -0
- /package/dist/web/assets/{llvm-DQ6-B_6Y.js → llvm-Dpbq3gky.js} +0 -0
- /package/dist/web/assets/{log-Bb_vqvu7.js → log-qKMcPm9S.js} +0 -0
- /package/dist/web/assets/{logo-BzfYv3A_.js → logo-DFo7i5KH.js} +0 -0
- /package/dist/web/assets/{luau-BnAeNR77.js → luau-DGlqWFyL.js} +0 -0
- /package/dist/web/assets/{make-B6IFuxpC.js → make-CixdvUQe.js} +0 -0
- /package/dist/web/assets/{markdown-Z0xi77Fn.js → markdown-CmRnbngE.js} +0 -0
- /package/dist/web/assets/{material-theme-1scy1SYn.js → material-theme-WDUbafB-.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-G-JykXQP.js → material-theme-darker-u8y2xpOk.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-BtKkCMVq.js → material-theme-lighter-cyQZK3Tg.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-irrDC89L.js → material-theme-ocean-LBAVvw7k.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-Eq5_K4LM.js → material-theme-palenight-BJqhBxL0.js} +0 -0
- /package/dist/web/assets/{matlab-BE3oCXZC.js → matlab-B38sOJee.js} +0 -0
- /package/dist/web/assets/{mdx-DYiuFTGw.js → mdx-C8QrE_IW.js} +0 -0
- /package/dist/web/assets/{mermaid-DVlaEvtw.js → mermaid-BQ-eeU8A.js} +0 -0
- /package/dist/web/assets/{min-dark-DSkeTpQa.js → min-dark-D4gNa5tv.js} +0 -0
- /package/dist/web/assets/{min-light-CFv2Q0uF.js → min-light-Dfbhn_PL.js} +0 -0
- /package/dist/web/assets/{mipsasm-CiAFuZQ2.js → mipsasm-bLOhJkdm.js} +0 -0
- /package/dist/web/assets/{mojo-ARshcaSQ.js → mojo-B0BCOMQW.js} +0 -0
- /package/dist/web/assets/{monaco-dotenv-language-BDYnKcnr.js → monaco-dotenv-language-uB-wok6C.js} +0 -0
- /package/dist/web/assets/{monokai-CPTV-SOg.js → monokai-Bdw_07OM.js} +0 -0
- /package/dist/web/assets/{moonbit-D57B0dZr.js → moonbit-BFdGaeuk.js} +0 -0
- /package/dist/web/assets/{move-qgy3G_o9.js → move-CyXQFGPM.js} +0 -0
- /package/dist/web/assets/{narrat-CZ3rC-oc.js → narrat-9DvTD3_3.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-BxxIOcI4.js → nextflow-groovy-C4VY3kNY.js} +0 -0
- /package/dist/web/assets/{night-owl-B1ryGCda.js → night-owl-feYm66V0.js} +0 -0
- /package/dist/web/assets/{night-owl-light-BZvNnvMF.js → night-owl-light-DALqFjvx.js} +0 -0
- /package/dist/web/assets/{nix-BVBQi8yv.js → nix-C1kPA9Sw.js} +0 -0
- /package/dist/web/assets/{nord-3NXw613h.js → nord-1dvrj1Al.js} +0 -0
- /package/dist/web/assets/{nushell-BMOLRalb.js → nushell-DSQ_9Uqj.js} +0 -0
- /package/dist/web/assets/{objective-c-AxruEUgh.js → objective-c-Dhcn5ZK8.js} +0 -0
- /package/dist/web/assets/{objective-cpp-cFT0qRsD.js → objective-cpp-CGVYCSrp.js} +0 -0
- /package/dist/web/assets/{ocaml-_8JH6O8J.js → ocaml-rxXW21An.js} +0 -0
- /package/dist/web/assets/{odin-nwYvR0O6.js → odin-ClSJX2DE.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-C1LaYJaH.js → one-dark-pro-3gbWDfFW.js} +0 -0
- /package/dist/web/assets/{one-light-B7rMc__c.js → one-light-C2q5ZZAw.js} +0 -0
- /package/dist/web/assets/{openscad-Cwes8bKO.js → openscad-eHXeafBl.js} +0 -0
- /package/dist/web/assets/{pascal-DMt-6tdZ.js → pascal-kKha3Txs.js} +0 -0
- /package/dist/web/assets/{pkl-QBTyYQmB.js → pkl-CTiPytg-.js} +0 -0
- /package/dist/web/assets/{plastic-BTSUp8Fk.js → plastic-D1Ygezi4.js} +0 -0
- /package/dist/web/assets/{plsql-DQv3IFXP.js → plsql-D_gbgZIr.js} +0 -0
- /package/dist/web/assets/{po-CNwZx4tW.js → po-BkhbDUnq.js} +0 -0
- /package/dist/web/assets/{poimandres-8Os7Qequ.js → poimandres-Do6NywrA.js} +0 -0
- /package/dist/web/assets/{polar-b4yK8oD8.js → polar-B3B23wca.js} +0 -0
- /package/dist/web/assets/{postcss-sFctwpxx.js → postcss-Dv_YBvWd.js} +0 -0
- /package/dist/web/assets/{powerquery-Cg_WrtuN.js → powerquery-fKJWI-Sh.js} +0 -0
- /package/dist/web/assets/{powershell-C7Ifz95h.js → powershell-B4Y7KIgx.js} +0 -0
- /package/dist/web/assets/{prisma-Z52HUJzQ.js → prisma-J--XdTXX.js} +0 -0
- /package/dist/web/assets/{prolog-DLqW-zR1.js → prolog-BBTg4Vb3.js} +0 -0
- /package/dist/web/assets/{proto-BOIzxCCJ.js → proto-DWlum3T8.js} +0 -0
- /package/dist/web/assets/{puppet-De7TjZIf.js → puppet-ZiUfc9JV.js} +0 -0
- /package/dist/web/assets/{purescript-R76ou1WT.js → purescript-CmT8SCHB.js} +0 -0
- /package/dist/web/assets/{python-Dprm0tNP.js → python-BURpj-fq.js} +0 -0
- /package/dist/web/assets/{qmldir-Clp_jpNw.js → qmldir-CpaiJywQ.js} +0 -0
- /package/dist/web/assets/{qss-eQPYYzSo.js → qss-C-zdz0i5.js} +0 -0
- /package/dist/web/assets/{r-CCcRHIf5.js → r-K56BeS-s.js} +0 -0
- /package/dist/web/assets/{racket-CzE472yp.js → racket-gkn6Z_Jw.js} +0 -0
- /package/dist/web/assets/{raku-DQwXrTSi.js → raku-C0ZBuKlx.js} +0 -0
- /package/dist/web/assets/{red-KCS6wyop.js → red-BWZ6x-3p.js} +0 -0
- /package/dist/web/assets/{reg-CcrAddUh.js → reg-Dt_EsyGR.js} +0 -0
- /package/dist/web/assets/{regexp-DDbud-Ri.js → regexp-CT6BLG2C.js} +0 -0
- /package/dist/web/assets/{rel-99GDgaHp.js → rel-MJ2LcyzJ.js} +0 -0
- /package/dist/web/assets/{resolve-theme-CW2niGV2.js → resolve-theme-B_ukrg_8.js} +0 -0
- /package/dist/web/assets/{riscv-Dm2eNv0R.js → riscv-BZYWzMAA.js} +0 -0
- /package/dist/web/assets/{ron-CzFt0ygH.js → ron-CLbwHWMn.js} +0 -0
- /package/dist/web/assets/{rose-pine-Ck6lCrCP.js → rose-pine-BPiI_daR.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-vtALRJNj.js → rose-pine-dawn-BjkJc7dW.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-DPto8qXw.js → rose-pine-moon-BQSxkt7g.js} +0 -0
- /package/dist/web/assets/{rosmsg-NtS6429H.js → rosmsg-yt4151pJ.js} +0 -0
- /package/dist/web/assets/{rust-BjIXvF9h.js → rust-1dAcengL.js} +0 -0
- /package/dist/web/assets/{sass-CQ23wf5E.js → sass-DUgtQJsg.js} +0 -0
- /package/dist/web/assets/{scala-LfKLM143.js → scala-CxoGYpS0.js} +0 -0
- /package/dist/web/assets/{scheme-D3SLBXlF.js → scheme-B2K3u9e3.js} +0 -0
- /package/dist/web/assets/{sdbl-ODRd2rGN.js → sdbl-C0Ygk0n6.js} +0 -0
- /package/dist/web/assets/{shellscript-3KB1G_oA.js → shellscript-C2CvhqMk.js} +0 -0
- /package/dist/web/assets/{slack-dark-DTtyWmIy.js → slack-dark-DYdgzRGj.js} +0 -0
- /package/dist/web/assets/{slack-ochin-Bu0PApk3.js → slack-ochin-X-WD0WCg.js} +0 -0
- /package/dist/web/assets/{smalltalk-haMT-Sbe.js → smalltalk-Dp7g_zb9.js} +0 -0
- /package/dist/web/assets/{snazzy-light-DXJzefE9.js → snazzy-light-DWHWr-a6.js} +0 -0
- /package/dist/web/assets/{solarized-dark-B6LyYa18.js → solarized-dark-CxdLsjvb.js} +0 -0
- /package/dist/web/assets/{solarized-light-QLADebkv.js → solarized-light-Cz9rAvG5.js} +0 -0
- /package/dist/web/assets/{solidity-mzfq1aDc.js → solidity-D33gHHdn.js} +0 -0
- /package/dist/web/assets/{splunk-CyH4RWBp.js → splunk-BFbOroYw.js} +0 -0
- /package/dist/web/assets/{sql-CWVlT5D3.js → sql-ChpxvmdY.js} +0 -0
- /package/dist/web/assets/{ssh-config-DNvQJ1Qx.js → ssh-config-CUuSQ6JG.js} +0 -0
- /package/dist/web/assets/{streaming-store-uDu3PoN1.js → streaming-store-BYukdB56.js} +0 -0
- /package/dist/web/assets/{stylus-ByQQdtmf.js → stylus-DDihP0_W.js} +0 -0
- /package/dist/web/assets/{swift-D1cpY4ow.js → swift-XoalUn4x.js} +0 -0
- /package/dist/web/assets/{synthwave-84-BDF_-QiW.js → synthwave-84-DjkG0Bff.js} +0 -0
- /package/dist/web/assets/{system-verilog-BYrkMydC.js → system-verilog-f_pLKMYd.js} +0 -0
- /package/dist/web/assets/{systemd-CQm3zyK0.js → systemd-DjDcLfxp.js} +0 -0
- /package/dist/web/assets/{talonscript-DMW5CzE1.js → talonscript-BpUzYcDl.js} +0 -0
- /package/dist/web/assets/{tasl-BhFzMCr2.js → tasl-DP-cUbg8.js} +0 -0
- /package/dist/web/assets/{tcl-D7VEAF-9.js → tcl-BHoy-dfE.js} +0 -0
- /package/dist/web/assets/{terraform-DdT5A0Vy.js → terraform-vCVxN25B.js} +0 -0
- /package/dist/web/assets/{tokyo-night-WGLMM6Q9.js → tokyo-night-D0u4xZba.js} +0 -0
- /package/dist/web/assets/{toml-CZ7ipXHZ.js → toml-BS8D0__A.js} +0 -0
- /package/dist/web/assets/{tsv-B1zVvoTr.js → tsv-DCb-NQh3.js} +0 -0
- /package/dist/web/assets/{tsx-D4HwRUBq.js → tsx-DF8YeRgG.js} +0 -0
- /package/dist/web/assets/{turtle-B4r8KEJH.js → turtle-Bhx1GlIy.js} +0 -0
- /package/dist/web/assets/{typescript-BnHhxMtp.js → typescript-CAVD2Wb6.js} +0 -0
- /package/dist/web/assets/{typespec-DP2q46GT.js → typespec-D7mchdY_.js} +0 -0
- /package/dist/web/assets/{typst-DFsD_yaD.js → typst-CeXzq6Yw.js} +0 -0
- /package/dist/web/assets/{use-is-mobile-BQ9Bv5Ga.js → use-is-mobile-TkcwP1Gx.js} +0 -0
- /package/dist/web/assets/{use-visual-viewport-CbwnktSG.js → use-visual-viewport-Lp878uzs.js} +0 -0
- /package/dist/web/assets/{v-Bji8hSVT.js → v-DhzO54je.js} +0 -0
- /package/dist/web/assets/{vala-D3TIgGmU.js → vala-Dw_RAk3E.js} +0 -0
- /package/dist/web/assets/{vb-DvcxFS2q.js → vb-CiXdNh2Y.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-BqPDsE9b.js → vendor-xterm-BHEY6Ctu.js} +0 -0
- /package/dist/web/assets/{verilog-Dy7cF35d.js → verilog-fITdxqwV.js} +0 -0
- /package/dist/web/assets/{vesper-BzjwUv9J.js → vesper-B1wm_At7.js} +0 -0
- /package/dist/web/assets/{vhdl-DuFnFNmW.js → vhdl-ebHjqKp9.js} +0 -0
- /package/dist/web/assets/{viml-yAPgKlbz.js → viml-Dgmnoxoo.js} +0 -0
- /package/dist/web/assets/{vitesse-black-oSSQdyIZ.js → vitesse-black-BZ5gMb7l.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-BZxwPihB.js → vitesse-dark-Lij7kkOt.js} +0 -0
- /package/dist/web/assets/{vitesse-light-uIigycvt.js → vitesse-light-DW6Wq7VC.js} +0 -0
- /package/dist/web/assets/{vyper-CX3NAxm5.js → vyper-6FxDnVNg.js} +0 -0
- /package/dist/web/assets/{wasm-6mEOOvVa.js → wasm-BDxaNPCj.js} +0 -0
- /package/dist/web/assets/{wasm-D9td37qO.js → wasm-DKhAtzfe.js} +0 -0
- /package/dist/web/assets/{wenyan-lwNIHgk6.js → wenyan-C0k0DYG_.js} +0 -0
- /package/dist/web/assets/{wgsl-DQAzCfog.js → wgsl-Y2zagbdn.js} +0 -0
- /package/dist/web/assets/{wikitext-DTXmt5NE.js → wikitext-C4QGWzkY.js} +0 -0
- /package/dist/web/assets/{wit-Bdw_7WDH.js → wit-CQESzjHy.js} +0 -0
- /package/dist/web/assets/{wolfram-DqyHDZ07.js → wolfram-B-ia69Ia.js} +0 -0
- /package/dist/web/assets/{yaml-CPXhdByd.js → yaml-CEYYTQPm.js} +0 -0
- /package/dist/web/assets/{zenscript-6s_i5zek.js → zenscript-BggWXIeJ.js} +0 -0
- /package/dist/web/assets/{zig-B2htezJ_.js → zig-CbaUfBQe.js} +0 -0
|
@@ -27,7 +27,7 @@ import { SUBSCRIPTION_PROMPT_CACHE_TTL_MS, API_KEY_PROMPT_CACHE_TTL_MS } from ".
|
|
|
27
27
|
import { buildTurnUsage, formatTurnUsageLog } from "../shared/turn-usage.ts";
|
|
28
28
|
import { accountSelector } from "../services/account-selector.service.ts";
|
|
29
29
|
import { accountService, type AccountWithTokens } from "../services/account.service.ts";
|
|
30
|
-
import { parseSessionMessage, nestChildEventsAcrossMessages } from "../services/jsonl-transcript-parser.ts";
|
|
30
|
+
import { parseSessionMessage, nestChildEventsAcrossMessages, parseJsonlTranscript, fullParseWindow } from "../services/jsonl-transcript-parser.ts";
|
|
31
31
|
import { applyBackgroundAgentStatus } from "../shared/background-agent-status.ts";
|
|
32
32
|
import { mergeSubagentChildren, resolveSessionDir } from "../services/subagent-transcript-merger.ts";
|
|
33
33
|
import { stringifyToolResultContent } from "../shared/tool-result-content.ts";
|
|
@@ -2133,6 +2133,47 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
2133
2133
|
}
|
|
2134
2134
|
}
|
|
2135
2135
|
|
|
2136
|
+
/**
|
|
2137
|
+
* Every message in the transcript, compacted-away segments included.
|
|
2138
|
+
*
|
|
2139
|
+
* `getSessionMessages` walks back from the newest message through
|
|
2140
|
+
* `parentUuid`, and the `compact_boundary` record Claude Code writes when it
|
|
2141
|
+
* compacts carries `parentUuid: null` — so the walk stops there and
|
|
2142
|
+
* `getMessages` answers with the last segment only. That is right for the
|
|
2143
|
+
* chat view, which shows the compact summary with a "Load previous
|
|
2144
|
+
* conversation" button beside it. The search index has no such affordance:
|
|
2145
|
+
* asking it the same question indexed 179 of one session's 1084 messages and
|
|
2146
|
+
* left the oldest 18 hours of it unfindable by any query. Reading the file
|
|
2147
|
+
* linearly ignores the `parentUuid` chain and costs less than the SDK call
|
|
2148
|
+
* (40ms for a 6.9MB transcript).
|
|
2149
|
+
*/
|
|
2150
|
+
async getFullMessages(sessionId: string, opts?: { maxBytes?: number }): Promise<ChatMessage[]> {
|
|
2151
|
+
const transcriptDir = resolveSessionDir(sessionId, getSessionProjectPath(sessionId));
|
|
2152
|
+
if (transcriptDir) {
|
|
2153
|
+
const file = `${transcriptDir}.jsonl`;
|
|
2154
|
+
// Nothing else bounds this path. `validateJsonlPath` guards the route
|
|
2155
|
+
// that serves one chat's pre-compact scroll; the search indexer arrives
|
|
2156
|
+
// here instead, unattended and once per stale session — and a bumped
|
|
2157
|
+
// INDEXER_VERSION makes that every session on disk. Parsing costs about
|
|
2158
|
+
// 4.3× the file in transient allocator growth (measured: 88.6MB → +355MB
|
|
2159
|
+
// RSS, which neither a forced GC nor dropping the array gives back), so
|
|
2160
|
+
// past the bound the newest `FULL_PARSE_MAX_BYTES` are read and the rest
|
|
2161
|
+
// of the history is left out of the index rather than out of memory.
|
|
2162
|
+
// `maxBytes` is a parameter only so a test can assert the window against a
|
|
2163
|
+
// one-byte-over fixture: a real 128MB transcript is a 12s read even when
|
|
2164
|
+
// the point is that most of it goes unread.
|
|
2165
|
+
const fromByte = fullParseWindow(file, opts?.maxBytes);
|
|
2166
|
+
if (fromByte > 0) {
|
|
2167
|
+
console.warn(`[sdk] getFullMessages: ${sessionId} transcript is over the full-parse bound — indexing from byte ${fromByte} on, older segments stay out of the index`);
|
|
2168
|
+
}
|
|
2169
|
+
try {
|
|
2170
|
+
const fromFile = await parseJsonlTranscript(file, undefined, { fromByte });
|
|
2171
|
+
if (fromFile.length > 0) return fromFile;
|
|
2172
|
+
} catch { /* unreadable or malformed — fall back to the conversation */ }
|
|
2173
|
+
}
|
|
2174
|
+
return this.getMessages(sessionId);
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2136
2177
|
async getMessages(sessionId: string): Promise<ChatMessage[]> {
|
|
2137
2178
|
try {
|
|
2138
2179
|
const messages = await getSessionMessages(sessionId);
|
|
@@ -97,7 +97,8 @@ function fnOutputToToolResult(p: Record<string, unknown>): ChatEvent {
|
|
|
97
97
|
export function parseRolloutJsonl(
|
|
98
98
|
text: string,
|
|
99
99
|
opts?: {
|
|
100
|
-
|
|
100
|
+
/** Which compaction boundary to stop at, counting from the start of the file. */
|
|
101
|
+
preCompactIndex?: number;
|
|
101
102
|
/** Reads a spawned thread's transcript so its card can nest it. */
|
|
102
103
|
loadSubagent?: (threadId: string) => SubagentTranscript | null;
|
|
103
104
|
},
|
|
@@ -105,6 +106,7 @@ export function parseRolloutJsonl(
|
|
|
105
106
|
const messages: ChatMessage[] = [];
|
|
106
107
|
let i = 0;
|
|
107
108
|
let pendingEvents: ChatEvent[] = [];
|
|
109
|
+
let compactionsSeen = 0;
|
|
108
110
|
// Kept so the completion can answer the card the spawn opened, with the
|
|
109
111
|
// report the child ended on.
|
|
110
112
|
const subagentTranscripts = new Map<string, SubagentTranscript | null>();
|
|
@@ -184,8 +186,19 @@ export function parseRolloutJsonl(
|
|
|
184
186
|
else if (typeof p.type === "string" && p.type.endsWith("_call_output")) pendingEvents.push(fnOutputToToolResult(p));
|
|
185
187
|
else if (typeof p.type === "string" && p.type.endsWith("_call")) pendingEvents.push(genericCallToToolUse(p));
|
|
186
188
|
} else if (rec.type === "compacted") {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
+
if (opts?.preCompactIndex) {
|
|
190
|
+
// One segment per request, the way the Claude parser's `oneSegment` does.
|
|
191
|
+
// The requested boundary ends the walk; every boundary before it closes a
|
|
192
|
+
// stretch nobody asked for, so what was collected is dropped rather than
|
|
193
|
+
// prepended — the card at the head of the segment is what leads back to it.
|
|
194
|
+
if (++compactionsSeen === opts.preCompactIndex) {
|
|
195
|
+
if (pendingEvents.length) flushAssistant("", ts);
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
pendingEvents = [];
|
|
199
|
+
messages.length = 0;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
189
202
|
// In-place compaction: `replacement_history` REPLACES everything before this
|
|
190
203
|
// point. Reset to the post-compact base; turns after it append normally.
|
|
191
204
|
pendingEvents = [];
|
|
@@ -374,16 +387,42 @@ function subagentLoader(
|
|
|
374
387
|
};
|
|
375
388
|
}
|
|
376
389
|
|
|
377
|
-
/**
|
|
378
|
-
|
|
390
|
+
/**
|
|
391
|
+
* Every compaction's summary text, in file order — `[]` when never compacted.
|
|
392
|
+
*
|
|
393
|
+
* All of them rather than the first, because a thread can be compacted more than
|
|
394
|
+
* once and each boundary has its own summary. The *last* is what the post-compact
|
|
395
|
+
* view's card shows, since that is the compaction which produced the view being
|
|
396
|
+
* rendered; reading the first showed a twice-compacted thread the summary of work
|
|
397
|
+
* two segments ago. The earlier ones head the segments the "load more" walk opens.
|
|
398
|
+
*/
|
|
399
|
+
function compactionSummaries(text: string): string[] {
|
|
400
|
+
const out: string[] = [];
|
|
379
401
|
for (const line of completeLines(text)) {
|
|
380
402
|
const rec = parseLine(line);
|
|
381
|
-
if (rec?.type
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
}
|
|
403
|
+
if (rec?.type !== "compacted") continue;
|
|
404
|
+
const msg = (rec.payload as Record<string, unknown> | undefined)?.message;
|
|
405
|
+
out.push(typeof msg === "string" ? msg : "");
|
|
385
406
|
}
|
|
386
|
-
return
|
|
407
|
+
return out;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* The "earlier conversation was compacted" card that heads a compacted view.
|
|
412
|
+
*
|
|
413
|
+
* `#<index>` in the id is load-bearing, not decoration: the client sends the card's
|
|
414
|
+
* id back as `before`, and on a thread compacted more than once it is the only thing
|
|
415
|
+
* saying *which* boundary to walk back from. `#` rather than another `-` because a
|
|
416
|
+
* thread id is a uuid whose last group can be all digits, and a trailing `-12` would
|
|
417
|
+
* then be indistinguishable from an index.
|
|
418
|
+
*/
|
|
419
|
+
function compactCard(summary: string, threadId: string, index: number, file: string): ChatMessage {
|
|
420
|
+
return {
|
|
421
|
+
id: `codex-compact-${threadId}#${index}`,
|
|
422
|
+
role: "assistant",
|
|
423
|
+
content: `${summary || "_Earlier conversation was compacted to save context._"}\n\nread the full transcript at: ${file}`,
|
|
424
|
+
timestamp: new Date().toISOString(),
|
|
425
|
+
};
|
|
387
426
|
}
|
|
388
427
|
|
|
389
428
|
/**
|
|
@@ -400,14 +439,9 @@ export function getRolloutMessages(sessionsDir: string, threadId: string, reques
|
|
|
400
439
|
const msgs = parseRolloutJsonl(text, {
|
|
401
440
|
loadSubagent: subagentLoader(sessionsDir, requestedCwd, new Set([threadId]), SUBAGENT_DEPTH),
|
|
402
441
|
});
|
|
403
|
-
const
|
|
404
|
-
if (
|
|
405
|
-
msgs.unshift(
|
|
406
|
-
id: `codex-compact-${threadId}`,
|
|
407
|
-
role: "assistant",
|
|
408
|
-
content: `${summary || "_Earlier conversation was compacted to save context._"}\n\nread the full transcript at: ${file}`,
|
|
409
|
-
timestamp: new Date().toISOString(),
|
|
410
|
-
});
|
|
442
|
+
const summaries = compactionSummaries(text);
|
|
443
|
+
if (summaries.length) {
|
|
444
|
+
msgs.unshift(compactCard(summaries[summaries.length - 1]!, threadId, summaries.length, file));
|
|
411
445
|
}
|
|
412
446
|
return msgs;
|
|
413
447
|
} catch { return []; }
|
|
@@ -422,17 +456,37 @@ export function isCodexRolloutPath(p: string): boolean {
|
|
|
422
456
|
}
|
|
423
457
|
|
|
424
458
|
/**
|
|
425
|
-
*
|
|
426
|
-
* fail-closed on cwd
|
|
459
|
+
* One pre-compact segment for the "load more" feature. Jails to ~/.codex/sessions,
|
|
460
|
+
* fail-closed on cwd.
|
|
461
|
+
*
|
|
462
|
+
* One segment per request rather than everything before the first boundary, which is
|
|
463
|
+
* what this used to return. A thread compacted twice has three stretches, and the old
|
|
464
|
+
* answer handed the newest card the *oldest* stretch while leaving the middle one
|
|
465
|
+
* reachable from nothing at all — not a short answer, a lost one. `before` names the
|
|
466
|
+
* card that was clicked, and the segment it opens is itself headed by the previous
|
|
467
|
+
* boundary's card, so the walk continues the way the Claude path's does.
|
|
427
468
|
*/
|
|
428
|
-
export function getCodexPreCompactMessages(file: string, requestedCwd?: string): ChatMessage[] {
|
|
469
|
+
export function getCodexPreCompactMessages(file: string, requestedCwd?: string, beforeId?: string): ChatMessage[] {
|
|
429
470
|
if (!isCodexRolloutPath(file)) throw new Error("Access denied: not a codex rollout");
|
|
430
471
|
const resolved = resolve(file);
|
|
431
472
|
let text: string;
|
|
432
473
|
try { text = readFileSync(resolved, "utf-8"); } catch { throw new Error("File not found"); }
|
|
474
|
+
// Read once and reused below. `readSessionMeta` opens and reads the whole rollout again, and
|
|
475
|
+
// this path is already reading a file that reaches tens of megabytes on the very sessions
|
|
476
|
+
// the feature exists for — asking for it twice per click was a third of the cost for nothing.
|
|
477
|
+
const meta = readSessionMeta(resolved);
|
|
433
478
|
if (requestedCwd != null) {
|
|
434
|
-
const meta = readSessionMeta(resolved);
|
|
435
479
|
if (!meta?.cwd || normPath(meta.cwd) !== normPath(requestedCwd)) return []; // fail-closed
|
|
436
480
|
}
|
|
437
|
-
|
|
481
|
+
const summaries = compactionSummaries(text);
|
|
482
|
+
if (!summaries.length) return [];
|
|
483
|
+
// A client holding a card from before the index existed sends the bare id. The newest
|
|
484
|
+
// boundary is the one that card came from, and on a thread compacted once — which is
|
|
485
|
+
// every thread that path was ever correct for — the two agree.
|
|
486
|
+
const asked = Number(beforeId?.match(/#(\d+)$/)?.[1]);
|
|
487
|
+
const index = asked >= 1 && asked <= summaries.length ? asked : summaries.length;
|
|
488
|
+
const segment = parseRolloutJsonl(text, { preCompactIndex: index });
|
|
489
|
+
if (index < 2) return segment;
|
|
490
|
+
const threadId = meta?.id ?? threadIdFromName(resolved) ?? "";
|
|
491
|
+
return [compactCard(summaries[index - 2]!, threadId, index - 1, resolved), ...segment];
|
|
438
492
|
}
|
package/src/server/index.ts
CHANGED
|
@@ -45,6 +45,7 @@ async function setupLogFile() {
|
|
|
45
45
|
const { resolve } = await import("node:path");
|
|
46
46
|
const { appendFileSync, mkdirSync, existsSync } = await import("node:fs");
|
|
47
47
|
const { getPpmDir } = await import("../services/ppm-dir.ts");
|
|
48
|
+
const { stdioIsLogFile, consumeStdioIsLogEnv } = await import("../services/log-rotate.ts");
|
|
48
49
|
|
|
49
50
|
const ppmDir = getPpmDir();
|
|
50
51
|
if (!existsSync(ppmDir)) mkdirSync(ppmDir, { recursive: true });
|
|
@@ -70,9 +71,26 @@ async function setupLogFile() {
|
|
|
70
71
|
try { appendFileSync(logPath, `[${ts}] [${level}] ${redact(msg)}\n`); } catch {}
|
|
71
72
|
};
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
// The supervisor spawns this process with `stdio: ["ignore", logFd, logFd]`
|
|
75
|
+
// where logFd is ppm.log itself, so console output already lands in the log
|
|
76
|
+
// through fd 1 — with no timestamp, no level, and crucially *unredacted*,
|
|
77
|
+
// because `redact()` above only ever ran on the appended copy. That is how
|
|
78
|
+
// ppm.log came to hold 1,395,012 lines of which only 532,792 carried the
|
|
79
|
+
// prefix: the other 862,220 were the same events arriving raw.
|
|
80
|
+
//
|
|
81
|
+
// Where the console already reaches the file, the original call is dropped
|
|
82
|
+
// and only the formatted, redacted line is written — one copy per event, and
|
|
83
|
+
// the safe one. Run from a terminal (`bun dev:server`) stdout is a tty, both
|
|
84
|
+
// checks are false, and the terminal keeps its output exactly as before.
|
|
85
|
+
const stdoutIsLogFile = stdioIsLogFile(1, logPath);
|
|
86
|
+
const stderrIsLogFile = stdioIsLogFile(2, logPath);
|
|
87
|
+
// Read once, then dropped: the terminals, SDK children and `ppm` invocations this server
|
|
88
|
+
// spawns inherit its environment, and none of their stdouts is the log file.
|
|
89
|
+
consumeStdioIsLogEnv();
|
|
90
|
+
|
|
91
|
+
console.log = (...args: unknown[]) => { if (!stdoutIsLogFile) origLog(...args); writeLog("INFO", args); };
|
|
92
|
+
console.error = (...args: unknown[]) => { if (!stderrIsLogFile) origError(...args); writeLog("ERROR", args); };
|
|
93
|
+
console.warn = (...args: unknown[]) => { if (!stderrIsLogFile) origWarn(...args); writeLog("WARN", args); };
|
|
76
94
|
|
|
77
95
|
// Capture uncaught errors — count-based exit for supervisor restart
|
|
78
96
|
let exceptionCount = 0;
|
|
@@ -131,16 +149,21 @@ app.get("/api/info", (c) => c.json(ok({
|
|
|
131
149
|
tunnel_active: !!tunnelService.getTunnelUrl(),
|
|
132
150
|
})));
|
|
133
151
|
|
|
134
|
-
// Public: recent logs for bug reports (last 30 lines)
|
|
152
|
+
// Public: recent logs for bug reports (last 30 lines).
|
|
153
|
+
//
|
|
154
|
+
// Reads a bounded tail, never the file. This route sits *before*
|
|
155
|
+
// authMiddleware, and `readFileSync` + `split("\n")` on the 276 MB ppm.log this
|
|
156
|
+
// was found on cost 356 ms of blocked event loop and a 644 MB resident spike —
|
|
157
|
+
// per call, unauthenticated, and growing with the log.
|
|
135
158
|
app.get("/api/logs/recent", async (c) => {
|
|
136
159
|
const { resolve } = await import("node:path");
|
|
137
|
-
const { existsSync
|
|
160
|
+
const { existsSync } = await import("node:fs");
|
|
138
161
|
const { getPpmDir } = await import("../services/ppm-dir.ts");
|
|
139
162
|
const { redactSecrets } = await import("../services/redact-secrets.ts");
|
|
163
|
+
const { tailLines } = await import("../services/file-lines.ts");
|
|
140
164
|
const logFile = resolve(getPpmDir(), "ppm.log");
|
|
141
165
|
if (!existsSync(logFile)) return c.json(ok({ logs: "" }));
|
|
142
|
-
const
|
|
143
|
-
const lines = content.split("\n").slice(-30).join("\n").trim();
|
|
166
|
+
const lines = await tailLines(logFile, 30);
|
|
144
167
|
// Double-redact in case old logs have unredacted content
|
|
145
168
|
return c.json(ok({ logs: redactSecrets(lines) }));
|
|
146
169
|
});
|
|
@@ -179,6 +202,15 @@ app.route("/api/system", resourceRoutes);
|
|
|
179
202
|
import { hostInfoRoutes } from "./routes/host-info.ts";
|
|
180
203
|
app.route("/api/system", hostInfoRoutes);
|
|
181
204
|
|
|
205
|
+
// Event-loop lag. One process serves every request and all of the chat work, so
|
|
206
|
+
// a stall here is a stall everywhere; the report says whether the time was spent
|
|
207
|
+
// on our own synchronous work or off the CPU entirely, which is what decides
|
|
208
|
+
// whether the answer is in this repository at all.
|
|
209
|
+
app.get("/api/system/event-loop", async (c) => {
|
|
210
|
+
const { lagReport } = await import("../services/event-loop-lag.ts");
|
|
211
|
+
return c.json(ok(lagReport()));
|
|
212
|
+
});
|
|
213
|
+
|
|
182
214
|
// Remote desktop (video capture + input) — on by default, opt-out via REMOTE_DESKTOP_ENABLED=0, see remote-desktop-flag.ts
|
|
183
215
|
import { remoteDesktopRoutes } from "./routes/remote-desktop.ts";
|
|
184
216
|
app.route("/api/remote-desktop", remoteDesktopRoutes);
|
|
@@ -970,6 +1002,9 @@ if (process.argv.includes("__serve__")) {
|
|
|
970
1002
|
// Start background usage limit polling (every 5 min)
|
|
971
1003
|
import("../services/claude-usage.service.ts").then(({ startUsagePolling }) => startUsagePolling()).catch(() => {});
|
|
972
1004
|
|
|
1005
|
+
// Watch how long the loop is unavailable for, and whose fault that is
|
|
1006
|
+
import("../services/event-loop-lag.ts").then(({ startLagMonitor }) => startLagMonitor()).catch(() => {});
|
|
1007
|
+
|
|
973
1008
|
// Discover + activate enabled extensions
|
|
974
1009
|
import("../services/extension.service.ts").then(({ extensionService }) => extensionService.startup()).catch((e) => {
|
|
975
1010
|
console.error("[ExtService] Startup error:", e);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import { resolve, join, basename } from "node:path";
|
|
3
|
-
import { existsSync, readdirSync, statSync,
|
|
3
|
+
import { existsSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { unlink } from "node:fs/promises";
|
|
5
|
+
import { countLines } from "../../services/file-lines.ts";
|
|
5
6
|
import { ensureUploadsDir, resolveUploadPath } from "../../services/chat-upload-storage.service.ts";
|
|
6
7
|
import { chatService } from "../../services/chat.service.ts";
|
|
7
8
|
import { draftService } from "../../services/draft.service.ts";
|
|
@@ -818,24 +819,25 @@ function resolveSessionJsonlPath(sessionId: string): { jsonlPath: string; jsonlD
|
|
|
818
819
|
}
|
|
819
820
|
|
|
820
821
|
/** GET /chat/sessions/:id/debug — session debug info (IDs, JSONL path) */
|
|
821
|
-
chatRoutes.get("/sessions/:id/debug", (c) => {
|
|
822
|
+
chatRoutes.get("/sessions/:id/debug", async (c) => {
|
|
822
823
|
const sessionId = c.req.param("id");
|
|
823
824
|
const { jsonlPath, jsonlDir, projectPath, exists } = resolveSessionJsonlPath(sessionId);
|
|
824
825
|
// Transcript weight: file size + record count (1 JSONL line = 1 event record).
|
|
825
826
|
// Line count skipped above 64MB so the debug button stays snappy on huge files.
|
|
827
|
+
//
|
|
828
|
+
// `countLines` rather than `readFileSync(path, "utf8")` plus a `charCodeAt`
|
|
829
|
+
// walk. Not for speed — both are 53 ms on a 35 MB transcript — but because
|
|
830
|
+
// the old one held the loop for every one of those milliseconds, and this one
|
|
831
|
+
// hands it back nine times. The yields inside it are explicit `setTimeout`s
|
|
832
|
+
// and have to be: awaiting the stream alone resolves as microtasks and blocks
|
|
833
|
+
// just as hard. See `file-lines.ts`.
|
|
826
834
|
let jsonlSizeBytes: number | null = null;
|
|
827
835
|
let jsonlLines: number | null = null;
|
|
828
836
|
if (exists && jsonlPath) {
|
|
829
837
|
try {
|
|
830
838
|
const st = statSync(jsonlPath);
|
|
831
839
|
jsonlSizeBytes = st.size;
|
|
832
|
-
if (st.size <= 64 * 1024 * 1024)
|
|
833
|
-
const text = readFileSync(jsonlPath, "utf8");
|
|
834
|
-
let n = 0;
|
|
835
|
-
for (let i = 0; i < text.length; i++) if (text.charCodeAt(i) === 10) n++;
|
|
836
|
-
if (text.length > 0 && text.charCodeAt(text.length - 1) !== 10) n++;
|
|
837
|
-
jsonlLines = n;
|
|
838
|
-
}
|
|
840
|
+
if (st.size <= 64 * 1024 * 1024) jsonlLines = await countLines(jsonlPath);
|
|
839
841
|
} catch { /* stat/read failure — omit weight fields */ }
|
|
840
842
|
}
|
|
841
843
|
// PPM session ID == SDK session ID (canonical — see claude-agent-sdk.ts:728).
|
|
@@ -1000,11 +1002,13 @@ chatRoutes.get("/pre-compact-messages", async (c) => {
|
|
|
1000
1002
|
// Codex rollouts live under ~/.codex/sessions (different format + jail than Claude JSONL).
|
|
1001
1003
|
const { isCodexRolloutPath, getCodexPreCompactMessages } = await import("../../providers/codex-app-server/codex-history.ts");
|
|
1002
1004
|
if (isCodexRolloutPath(jsonlPath)) {
|
|
1003
|
-
const messages = getCodexPreCompactMessages(jsonlPath, c.get("projectPath"));
|
|
1005
|
+
const messages = getCodexPreCompactMessages(jsonlPath, c.get("projectPath"), beforeUuid);
|
|
1004
1006
|
return c.json(ok(messages));
|
|
1005
1007
|
}
|
|
1006
1008
|
const validated = validateJsonlPath(jsonlPath);
|
|
1007
|
-
|
|
1009
|
+
// One compaction segment per request: the client walks further back by
|
|
1010
|
+
// expanding the summary that arrives at the head of each one.
|
|
1011
|
+
const messages = await parseJsonlTranscript(validated, beforeUuid, { oneSegment: true });
|
|
1008
1012
|
return c.json(ok(messages));
|
|
1009
1013
|
} catch (e) {
|
|
1010
1014
|
const message = e instanceof Error ? e.message : "Unknown error";
|
package/src/server/routes/git.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { gitService } from "../../services/git.service.ts";
|
|
4
4
|
import { gitHunksService, type HunkRequest, type HunkScope } from "../../services/git-hunks/git-hunks.service.ts";
|
|
5
5
|
import { assertSafeRev, gitBlameService } from "../../services/git-blame/git-blame.service.ts";
|
|
6
|
+
import { branchDiff } from "../../services/git-branch-diff/branch-diff.service.ts";
|
|
6
7
|
import { discoverGitRepos, isGitRepo } from "../../services/git-repos/git-repo-discovery.ts";
|
|
7
8
|
import { isInsideDir, realPathOrSelfSync } from "../../services/fs-ops/fs-real-path.ts";
|
|
8
9
|
import { ok, err } from "../../types/api.ts";
|
|
@@ -128,6 +129,32 @@ gitRoutes.get("/diff-stat", async (c) => {
|
|
|
128
129
|
}
|
|
129
130
|
});
|
|
130
131
|
|
|
132
|
+
/**
|
|
133
|
+
* GET /git/branch-diff?base=&head=&mode=three-dot|two-dot
|
|
134
|
+
*
|
|
135
|
+
* Every file a branch changed, in one answer, plus the commit those changes
|
|
136
|
+
* were measured against. The Branch Review tab opens each file's diff at
|
|
137
|
+
* `mergeBase`, so the list and the viewer can never disagree about the base.
|
|
138
|
+
*
|
|
139
|
+
* A bad ref is a 400, not a 500: `base` and `head` come straight from a picker,
|
|
140
|
+
* and a branch deleted since it was rendered is an ordinary thing to ask about.
|
|
141
|
+
*/
|
|
142
|
+
gitRoutes.get("/branch-diff", async (c) => {
|
|
143
|
+
const projectPath = c.get("projectPath");
|
|
144
|
+
const mode = c.req.query("mode") === "two-dot" ? "two-dot" : "three-dot";
|
|
145
|
+
try {
|
|
146
|
+
const result = await branchDiff(
|
|
147
|
+
projectPath,
|
|
148
|
+
c.req.query("base"),
|
|
149
|
+
c.req.query("head"),
|
|
150
|
+
mode,
|
|
151
|
+
);
|
|
152
|
+
return c.json(ok(result));
|
|
153
|
+
} catch (e) {
|
|
154
|
+
return c.json(err((e as Error).message), 400);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
131
158
|
/** GET /git/file-diff?file=&ref= */
|
|
132
159
|
gitRoutes.get("/file-diff", async (c) => {
|
|
133
160
|
try {
|
|
@@ -144,9 +171,11 @@ gitRoutes.get("/file-diff", async (c) => {
|
|
|
144
171
|
}
|
|
145
172
|
});
|
|
146
173
|
|
|
147
|
-
/** GET /git/file-full-diff?file=&ref=
|
|
174
|
+
/** GET /git/file-full-diff?file=&ref=&ref2=&text=1
|
|
148
175
|
* Returns full file contents (VSCode-style) for both sides:
|
|
149
|
-
* { original: <ref version>, modified: <working tree> }
|
|
176
|
+
* { original: <ref version>, modified: <working tree> }
|
|
177
|
+
* A binary file answers `binary: true` with both sides empty; `text=1` is the
|
|
178
|
+
* viewer's "Open Anyway" and asks for the decoded bytes regardless. */
|
|
150
179
|
gitRoutes.get("/file-full-diff", async (c) => {
|
|
151
180
|
try {
|
|
152
181
|
const projectPath = c.get("projectPath");
|
|
@@ -156,13 +185,64 @@ gitRoutes.get("/file-full-diff", async (c) => {
|
|
|
156
185
|
const ref2 = c.req.query("ref2") || undefined;
|
|
157
186
|
const bad = invalidRev(ref, ref2);
|
|
158
187
|
if (bad) return c.json(err(bad), 400);
|
|
159
|
-
const result = await gitService.fileFullDiff(projectPath, file, ref, ref2
|
|
188
|
+
const result = await gitService.fileFullDiff(projectPath, file, ref, ref2, {
|
|
189
|
+
text: c.req.query("text") === "1",
|
|
190
|
+
});
|
|
160
191
|
return c.json(ok(result));
|
|
161
192
|
} catch (e) {
|
|
162
193
|
return c.json(err((e as Error).message), 500);
|
|
163
194
|
}
|
|
164
195
|
});
|
|
165
196
|
|
|
197
|
+
/**
|
|
198
|
+
* The content types `/git/file-blob` will name. Everything outside this list is
|
|
199
|
+
* served as `application/octet-stream`: a blob URL inherits *this* origin, so
|
|
200
|
+
* answering with the repository's own `text/html` — or `image/svg+xml`, which
|
|
201
|
+
* carries script — would let a committed file run code inside the app.
|
|
202
|
+
*/
|
|
203
|
+
const BLOB_IMAGE_TYPES: Record<string, string> = {
|
|
204
|
+
png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif",
|
|
205
|
+
webp: "image/webp", bmp: "image/bmp", ico: "image/x-icon", avif: "image/avif",
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* GET /git/file-blob?file=&ref=HEAD — the file's bytes at a revision.
|
|
210
|
+
*
|
|
211
|
+
* What the binary diff view draws its left-hand pane from: `/files/raw` serves
|
|
212
|
+
* the working tree, and nothing else reaches the version a commit holds. The
|
|
213
|
+
* path needs no traversal check of its own — git resolves `ref:path` inside the
|
|
214
|
+
* repository and refuses anything above it ("is outside repository").
|
|
215
|
+
*/
|
|
216
|
+
gitRoutes.get("/file-blob", async (c) => {
|
|
217
|
+
try {
|
|
218
|
+
const projectPath = c.get("projectPath");
|
|
219
|
+
const file = c.req.query("file");
|
|
220
|
+
if (!file) return c.json(err("Missing query: file"), 400);
|
|
221
|
+
const ref = c.req.query("ref") || "HEAD";
|
|
222
|
+
// The same guard as every sibling route, and it is not decoration here: `ref` reaches
|
|
223
|
+
// `git show` as its own argv word, so `?ref=--output=<path>` is an option rather than a
|
|
224
|
+
// revision. Measured on a scratch repository, `git show --output=<victim> HEAD:a.txt`
|
|
225
|
+
// exits 0 and leaves the victim at zero bytes — arbitrary file destruction, on a server
|
|
226
|
+
// that is routinely reachable through a public tunnel URL.
|
|
227
|
+
const bad = invalidRev(ref);
|
|
228
|
+
if (bad) return c.json(err(bad), 400);
|
|
229
|
+
const bytes = await gitService.fileBlob(projectPath, file, ref);
|
|
230
|
+
if (!bytes) return c.json(err("File does not exist at that revision"), 404);
|
|
231
|
+
const ext = file.split(".").pop()?.toLowerCase() ?? "";
|
|
232
|
+
// Copied into a plain Uint8Array because a Buffer is typed over
|
|
233
|
+
// ArrayBufferLike, which BodyInit does not accept.
|
|
234
|
+
return new Response(new Uint8Array(bytes), {
|
|
235
|
+
headers: {
|
|
236
|
+
"Content-Type": BLOB_IMAGE_TYPES[ext] ?? "application/octet-stream",
|
|
237
|
+
"Content-Length": String(bytes.length),
|
|
238
|
+
"X-Content-Type-Options": "nosniff",
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
} catch (e) {
|
|
242
|
+
return c.json(err((e as Error).message), 500);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
166
246
|
/** GET /git/graph?max=200&skip=0 */
|
|
167
247
|
gitRoutes.get("/graph", async (c) => {
|
|
168
248
|
try {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { Hono, type Context } from "hono";
|
|
11
11
|
import { ok, err } from "../../types/api.ts";
|
|
12
12
|
import { configService } from "../../services/config.service.ts";
|
|
13
|
-
import { getFfmpegCapabilities } from "../../services/media-transcode/ffmpeg-capabilities.ts";
|
|
13
|
+
import { getFfmpegCapabilities, workingEncoders } from "../../services/media-transcode/ffmpeg-capabilities.ts";
|
|
14
14
|
import { isRemoteDesktopEnabled } from "../../services/remote-desktop/remote-desktop-flag.ts";
|
|
15
15
|
import { mintRemoteDesktopNonce } from "../../services/remote-desktop/remote-desktop-nonce.ts";
|
|
16
16
|
import { listDisplays } from "../../services/remote-desktop/remote-desktop-displays.ts";
|
|
@@ -51,9 +51,14 @@ function assertSessionAllowed(c: Context): Response | null {
|
|
|
51
51
|
|
|
52
52
|
remoteDesktopRoutes.get("/capabilities", async (c) => {
|
|
53
53
|
if (!isRemoteDesktopEnabled()) return c.json(err("remote desktop is disabled"), 404);
|
|
54
|
-
const [caps, readiness, displays] = await Promise.all([
|
|
54
|
+
const [caps, readiness, displays, encoders] = await Promise.all([
|
|
55
|
+
getFfmpegCapabilities(), remoteDesktopReadiness(), listDisplays(), workingEncoders(),
|
|
56
|
+
]);
|
|
55
57
|
return c.json(ok({
|
|
56
58
|
displays,
|
|
59
|
+
// Every encoder that really encodes here, preference order, for the codec picker. The
|
|
60
|
+
// first is what a session uses unless the client names another.
|
|
61
|
+
encoders,
|
|
57
62
|
// Flat booleans kept for existing clients/tests; `readiness` is the source of truth.
|
|
58
63
|
ffmpegAvailable: !!caps.ffmpeg,
|
|
59
64
|
videoAvailable: readiness.videoReady,
|
package/src/server/ws/global.ts
CHANGED
|
@@ -58,7 +58,7 @@ function setWatch(ws: GlobalWsSocket, projectName: string): void {
|
|
|
58
58
|
const project = configService.get("projects").find((p) => p.name === projectName);
|
|
59
59
|
if (!project) return;
|
|
60
60
|
|
|
61
|
-
startWatching(projectName, project.path);
|
|
61
|
+
void startWatching(projectName, project.path);
|
|
62
62
|
watchedProject.set(ws, projectName);
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
* token already travels as `?token=` on every WS URL (`isWsUpgradeAuthorized`,
|
|
7
7
|
* `src/server/index.ts`) and proves nothing beyond "holds the one reusable app token", so a
|
|
8
8
|
* single-use short-TTL nonce (`POST /api/remote-desktop/session`) must be presented as the
|
|
9
|
-
* client's *first* message — `{type:"auth", nonce, displayId?}` — before
|
|
10
|
-
* starts. `displayId` picks one of `/capabilities`' `displays`; absent/unknown = primary.
|
|
11
|
-
*
|
|
9
|
+
* client's *first* message — `{type:"auth", nonce, displayId?, cursor?, codec?}` — before
|
|
10
|
+
* capture or input starts. `displayId` picks one of `/capabilities`' `displays`; absent/unknown = primary.
|
|
11
|
+
* `cursor` and `codec` carry the client's saved cursor and encoder prefs, because ffmpeg takes
|
|
12
|
+
* both at startup and applying them afterwards would respawn it on every connect. Kept out of
|
|
13
|
+
* the query string (unlike the coarse token) so they never land in proxy/tunnel access logs.
|
|
12
14
|
*
|
|
13
15
|
* Both the feature flag and `auth.enabled` are re-checked here even though
|
|
14
16
|
* `src/server/index.ts` already gated the upgrade on them — this handler must never depend on
|
|
@@ -58,8 +60,13 @@ async function authenticateFirstMessage(ws: RemoteDesktopWs, text: string): Prom
|
|
|
58
60
|
getBufferedAmount: ws.getBufferedAmount ? () => ws.getBufferedAmount!() : undefined,
|
|
59
61
|
close: (code, reason) => ws.close(code, reason),
|
|
60
62
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
ws.data.session = await createRemoteDesktopSession(socket, {
|
|
64
|
+
displayId: typeof parsed.displayId === "string" ? parsed.displayId : undefined,
|
|
65
|
+
// Only an explicit `false` hides it: an older client sends no flag at all and must keep
|
|
66
|
+
// getting the pointer it has always had.
|
|
67
|
+
showCursor: parsed.cursor !== false,
|
|
68
|
+
encoder: typeof parsed.codec === "string" ? parsed.codec : undefined,
|
|
69
|
+
});
|
|
63
70
|
} catch (e) {
|
|
64
71
|
console.error(`[remote-desktop] failed to start capture: ${(e as Error).message}`);
|
|
65
72
|
ws.send(JSON.stringify({ type: "error", message: (e as Error).message }));
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is this file content binary?
|
|
3
|
+
*
|
|
4
|
+
* The answer has to be taken from *bytes*. `git show` hands simple-git a string
|
|
5
|
+
* decoded as UTF-8, and a PNG survives that as megabytes of U+FFFD — which is
|
|
6
|
+
* exactly what the diff editor used to render, line-numbered and highlighted,
|
|
7
|
+
* for every image in a commit.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** git's own window (`buffer_is_binary`, FIRST_FEW_BYTES in xdiff-interface.c). */
|
|
11
|
+
export const BINARY_SNIFF_BYTES = 8000;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* True when `bytes` looks binary — a NUL inside the first 8000 bytes.
|
|
15
|
+
*
|
|
16
|
+
* The same rule git applies when it prints "Binary files differ" instead of a
|
|
17
|
+
* diff, and the same shape as `file.service`'s check for whether to answer
|
|
18
|
+
* base64, so the diff view and the editor agree about any given file. A side
|
|
19
|
+
* that does not exist (`null`) is not binary: an added file has no old version,
|
|
20
|
+
* and that alone must not send the pair down the binary path.
|
|
21
|
+
*/
|
|
22
|
+
export function isBinaryContent(bytes: Uint8Array | null | undefined): boolean {
|
|
23
|
+
if (!bytes) return false;
|
|
24
|
+
return bytes.subarray(0, BINARY_SNIFF_BYTES).includes(0);
|
|
25
|
+
}
|
|
@@ -188,7 +188,7 @@ export async function indexSession(
|
|
|
188
188
|
projectPath: string,
|
|
189
189
|
jsonlMtime = 0,
|
|
190
190
|
): Promise<void> {
|
|
191
|
-
const messages = await chatService.
|
|
191
|
+
const messages = await chatService.getFullMessages(providerId, sessionId);
|
|
192
192
|
indexMessages(sessionId, projectPath, messages, jsonlMtime);
|
|
193
193
|
}
|
|
194
194
|
|
|
@@ -201,6 +201,15 @@ class ChatService {
|
|
|
201
201
|
? { ...message, content: stripSharedContext(message.content) }
|
|
202
202
|
: message);
|
|
203
203
|
}
|
|
204
|
+
|
|
205
|
+
/** Whole transcript rather than the resumable conversation, for the search index.
|
|
206
|
+
* Falls back to the conversation for a provider that draws no distinction. */
|
|
207
|
+
async getFullMessages(providerId: string, sessionId: string): Promise<ChatMessage[]> {
|
|
208
|
+
const provider = providerRegistry.get(providerId);
|
|
209
|
+
if (!provider) return [];
|
|
210
|
+
if (provider.getFullMessages) return await provider.getFullMessages(sessionId);
|
|
211
|
+
return await provider.getMessages?.(sessionId) ?? [];
|
|
212
|
+
}
|
|
204
213
|
}
|
|
205
214
|
|
|
206
215
|
export const chatService = new ChatService();
|