@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.
- package/CHANGELOG.md +29 -1
- package/CLAUDE.md +1 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/cli-reference.md +4 -4
- package/assets/skills/ppm/references/http-api.md +3 -2
- package/dist/web/assets/accounts-settings-section-LtSVxNNp.js +1 -0
- package/dist/web/assets/ai-resource-editor-BqFT1EUB.js +1 -0
- package/dist/web/assets/ai-settings-section-CY6spbEx.js +1 -0
- package/dist/web/assets/ai-settings-section-Po3K8j-g.js +1 -0
- package/dist/web/assets/{angular-html-VzxSzTCy.js → angular-html-BuHNQIbQ.js} +1 -1
- package/dist/web/assets/{angular-ts-CmJ4bmzM.js → angular-ts-DHRWoLc-.js} +1 -1
- package/dist/web/assets/api-client-BogqsFna.js +1 -0
- package/dist/web/assets/{api-client-qRWu6_s5.js → api-client-oY1tC3tM.js} +1 -1
- package/dist/web/assets/api-settings-CTMdz_VF.js +1 -0
- package/dist/web/assets/{api-themes-DsAp8m7m.js → api-themes-Cknp3Q-P.js} +1 -1
- package/dist/web/assets/{apl-do17bnVx.js → apl-C1VSWVei.js} +1 -1
- package/dist/web/assets/appearance-settings-section-L9Z-nej_.js +1 -0
- package/dist/web/assets/architecture-PBZL5I3N-Ca5IpdzP.js +1 -0
- package/dist/web/assets/arrow-left-CyFcsyzD.js +1 -0
- package/dist/web/assets/{astro-ClY0e0OA.js → astro-RX-rChsZ.js} +1 -1
- package/dist/web/assets/{audio-preview-Fw935Mok.js → audio-preview-CCd1jdje.js} +1 -1
- package/dist/web/assets/{blade-BXSdXDGq.js → blade-CZ6bxMbK.js} +1 -1
- package/dist/web/assets/{bot-CWZAuloY.js → bot-D6sx11y0.js} +1 -1
- package/dist/web/assets/{pencil-CHKLOgLs.js → brain-KDa9jFRq.js} +1 -1
- package/dist/web/assets/{bsl-MxsMKDUZ.js → bsl-BhgCXLWk.js} +1 -1
- package/dist/web/assets/{button-CrwgGd1y.js → button-BZtEJcUv.js} +1 -1
- package/dist/web/assets/{cairo-4CIrzX_p.js → cairo-BrwygPkn.js} +1 -1
- package/dist/web/assets/calendar-clock-C1asT-gk.js +1 -0
- package/dist/web/assets/chat-tab-BIO-VhWT.js +15 -0
- package/dist/web/assets/check-DN75Ucvz.js +1 -0
- package/dist/web/assets/{chevron-down-D7CbB5f6.js → chevron-down-Dp0rpHb8.js} +1 -1
- package/dist/web/assets/chevron-right-De-Wz5Lz.js +1 -0
- package/dist/web/assets/chevron-up-8PbsNiJp.js +1 -0
- package/dist/web/assets/circle-BCy0AUCo.js +1 -0
- package/dist/web/assets/{circle-question-mark-BBzFZrNh.js → circle-question-mark-CzR7YRc-.js} +1 -1
- package/dist/web/assets/clock-C1CQUQPZ.js +1 -0
- package/dist/web/assets/{cobol-phPOE89d.js → cobol-C5TGW1Ir.js} +1 -1
- package/dist/web/assets/code-DJgYrFzm.js +1 -0
- package/dist/web/assets/code-editor-B9lVjElM.js +7 -0
- package/dist/web/assets/{coffee-Cjuukfz3.js → coffee-CFgKlmgI.js} +1 -1
- package/dist/web/assets/{conflict-editor-Ni0PPsKu.js → conflict-editor-BoRPwL3L.js} +1 -1
- package/dist/web/assets/context-menu-C13b1uT3.js +1 -0
- package/dist/web/assets/{copy-CbPdNp7E.js → copy-BewHj66E.js} +1 -1
- package/dist/web/assets/{cpp-CT7tGBC3.js → cpp-BhG5q3-d.js} +1 -1
- package/dist/web/assets/{createLucideIcon-ConhwJnb.js → createLucideIcon-D8pyde1A.js} +1 -1
- package/dist/web/assets/{crystal-D8U1Qnqh.js → crystal-DvuI7_tY.js} +1 -1
- package/dist/web/assets/{csv-preview-Coc2oed1.js → csv-preview-BpoJqZQi.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-B8WQkZDB.js → data-grid-overlay-editor-DP1xkog4.js} +1 -1
- package/dist/web/assets/{data-grid-types-KpjaKAci.js → data-grid-types-CdAg-4W1.js} +1 -1
- package/dist/web/assets/{database-BqyeGogo.js → database-Y3ESDK4-.js} +1 -1
- package/dist/web/assets/database-viewer-I-9cER1L.js +1 -0
- package/dist/web/assets/diff-viewer-_xHZsxvr.js +4 -0
- package/dist/web/assets/{dist-C2_dKAa_.js → dist-DonmFWG8.js} +1 -1
- package/dist/web/assets/{docx-preview-76KDw1eE.js → docx-preview-CWIDVNZn.js} +1 -1
- package/dist/web/assets/{download-DAF7SXcf.js → download-DG3_9xXy.js} +1 -1
- package/dist/web/assets/{edge-Db4ze6bw.js → edge-BSIEsmlI.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-DyC_-4My.js → edit-diff-preview-DeHb85DN.js} +1 -1
- package/dist/web/assets/{elixir-RZPZ0472.js → elixir-BF-lrGXM.js} +1 -1
- package/dist/web/assets/{elm-DNZcQt_7.js → elm-8mCsJOeN.js} +1 -1
- package/dist/web/assets/{erb-DRONsZXs.js → erb-Dw5PUVKa.js} +1 -1
- package/dist/web/assets/{erlang-DeG0-QwJ.js → erlang-BvjBIA6O.js} +1 -1
- package/dist/web/assets/{esm-CZy-sSoK.js → esm-Bt9OwSnU.js} +1 -1
- package/dist/web/assets/explorer-body-DapwsOnp.js +2 -0
- package/dist/web/assets/explorer-window-content-DD6Z15pm.js +1 -0
- package/dist/web/assets/extension-manager-section-Cu9D9Udt.js +1 -0
- package/dist/web/assets/{extension-webview-BW5GuBnH.js → extension-webview-Diz2OzVq.js} +1 -1
- package/dist/web/assets/eye-gWwaF4Lg.js +1 -0
- package/dist/web/assets/eye-off-GjXVuAOn.js +1 -0
- package/dist/web/assets/{file-exclamation-point-Bpg-zDPP.js → file-exclamation-point-JzhTU2TX.js} +1 -1
- package/dist/web/assets/files-settings-section-B1e7BgKQ.js +1 -0
- package/dist/web/assets/flask-conical-tp8gyZGh.js +1 -0
- package/dist/web/assets/{folder-symlink-LD-mr54V.js → folder-symlink-fQRoG_rV.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-CK5VRJNd.js → fortran-fixed-form-CRjUJe9K.js} +1 -1
- package/dist/web/assets/{fsharp-TVVgzHWp.js → fsharp-CjUIHIHp.js} +1 -1
- package/dist/web/assets/{gdresource-DiUcyewI.js → gdresource-x_NUQFrZ.js} +1 -1
- package/dist/web/assets/general-settings-section-DWdHHRDy.js +1 -0
- package/dist/web/assets/{git-commit-CfOruFrg.js → git-commit-BOpxSbns.js} +1 -1
- package/dist/web/assets/git-log-panel-CMMsvU64.js +1 -0
- package/dist/web/assets/{git-rebase-Bxyr24oc.js → git-rebase-BW4xlK7k.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-BloZfB0Q.js +1 -0
- package/dist/web/assets/glide-data-grid-pKQuyYFj.js +141 -0
- package/dist/web/assets/{glimmer-js-CT5aJxz_.js → glimmer-js-CT9jrmFj.js} +1 -1
- package/dist/web/assets/{glimmer-ts-CRjP9Yey.js → glimmer-ts-CjQT1lxK.js} +1 -1
- package/dist/web/assets/{glsl-BX2--oI_.js → glsl-DsFJvtg5.js} +1 -1
- package/dist/web/assets/{graphql-BIQsvBxQ.js → graphql-Dhlnmkn_.js} +1 -1
- package/dist/web/assets/group-chat-tab-U5u0Loii.js +1 -0
- package/dist/web/assets/{hack-CTWk0k1m.js → hack-BKHbsJ7u.js} +1 -1
- package/dist/web/assets/{haml-DGh27hXc.js → haml-C1Ih25Y2.js} +1 -1
- package/dist/web/assets/{hand-DKSxDa5c.js → hand-x0ejq3fS.js} +1 -1
- package/dist/web/assets/{handlebars-BJL1jNrG.js → handlebars-CaFitvmC.js} +1 -1
- package/dist/web/assets/{html-DALyDNqU.js → html-BwpKIE6V.js} +1 -1
- package/dist/web/assets/{html-derivative-B4XcU57e.js → html-derivative-uuTEquZU.js} +1 -1
- package/dist/web/assets/{http-DfMo7nNu.js → http-Cv4Pg5cR.js} +1 -1
- package/dist/web/assets/{hurl-CGc_n2Qf.js → hurl-DubkI6TG.js} +1 -1
- package/dist/web/assets/{hxml-40zZ1t4x.js → hxml-B9OVZFAB.js} +1 -1
- package/dist/web/assets/{image-preview-Cp3XB8UO.js → image-preview-DRbZ0FA2.js} +1 -1
- package/dist/web/assets/index--KWVW-Ga.js +52 -0
- package/dist/web/assets/index-CvGx7wBQ.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-c0W8wp5o.js +1 -0
- package/dist/web/assets/{jinja-D5zuIn9u.js → jinja-BHb1jyl6.js} +1 -1
- package/dist/web/assets/jira-watcher-section-uA5oyB8s.js +1 -0
- package/dist/web/assets/{jison-Dxut9QwA.js → jison-CtpfXDzp.js} +1 -1
- package/dist/web/assets/{julia-Cu85tTPo.js → julia-TYjgpazU.js} +1 -1
- package/dist/web/assets/{just-CctM4uLa.js → just-D27bzZuW.js} +1 -1
- package/dist/web/assets/key-round-CQpDrDxg.js +1 -0
- package/dist/web/assets/keybindings-store-Ccf0etMJ.js +1 -0
- package/dist/web/assets/{keyboard-AdwmYDSu.js → keyboard-DiOYrs6C.js} +1 -1
- package/dist/web/assets/keyboard-shortcuts-section-B7IxJHiI.js +1 -0
- package/dist/web/assets/{latex-kpwS_CC7.js → latex-D_MzopTn.js} +1 -1
- package/dist/web/assets/{lib-Ca7ai8xa.js → lib-xrSJ4EZX.js} +1 -1
- package/dist/web/assets/{liquid-B8Rr0xue.js → liquid-BG_FG0Pe.js} +1 -1
- package/dist/web/assets/loader-circle-DPMrnLQ2.js +1 -0
- package/dist/web/assets/{lua-C_3gixwM.js → lua-Bl29oC2L.js} +1 -1
- package/dist/web/assets/markdown-renderer-CY1epGRc.js +1 -0
- package/dist/web/assets/markdown-renderer-DDcIGQEd.js +3 -0
- package/dist/web/assets/{marko-B28rvNL-.js → marko-D-HI7dbm.js} +1 -1
- package/dist/web/assets/{mdc-DQ6-STFO.js → mdc-y444JQTQ.js} +1 -1
- package/dist/web/assets/{media-url-BjvbfTTl.js → media-url-VzecHk9g.js} +1 -1
- package/dist/web/assets/{mobile-bottom-sheet-DXn5g2LB.js → mobile-bottom-sheet-BD5b7KJF.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-CqkwbCRl.js +1 -0
- package/dist/web/assets/{monitor-CXI_XpWJ.js → monitor-C835ucWH.js} +1 -1
- package/dist/web/assets/{nextflow-B1xq2O8L.js → nextflow-C5sKcldC.js} +1 -1
- package/dist/web/assets/{nginx-xsLxRGxO.js → nginx-bXnEC3wm.js} +1 -1
- package/dist/web/assets/{nim-wqcek3WO.js → nim-pevhf4WD.js} +1 -1
- package/dist/web/assets/notification-store-BNSYk3E7.js +1 -0
- package/dist/web/assets/notifications-settings-section-v_628h4B.js +1 -0
- package/dist/web/assets/{number-overlay-editor-1c4sgZJt.js → number-overlay-editor-B7WINIW4.js} +1 -1
- package/dist/web/assets/{overview-panel-Cr2xsHc6.js → overview-panel-mhN0qw6Q.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-D_BoHr_M.js +1 -0
- package/dist/web/assets/panel-store-BRrnDO_i.js +1 -0
- package/dist/web/assets/pdf-preview-CI3q1MnH.js +1 -0
- package/dist/web/assets/{perl-BH54gN9q.js → perl-B1KUtMnW.js} +1 -1
- package/dist/web/assets/{php-Br5ZrEZm.js → php-BGmVZBAJ.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-BaLfuEf8.js +1 -0
- package/dist/web/assets/portal-container-context-a399Hd9F.js +1 -0
- package/dist/web/assets/{postgres-viewer-BJPr_FYL.js → postgres-viewer-jaR0FYJ_.js} +3 -3
- package/dist/web/assets/power-CriQkeK5.js +1 -0
- package/dist/web/assets/ppmbot-settings-section-BET4VmKb.js +1 -0
- package/dist/web/assets/{process-table-BqTcAJ-h.js → process-table-D1r-Gupi.js} +1 -1
- package/dist/web/assets/{project-store-KHjFTCxf.js → project-store-BuY7rxn_.js} +1 -1
- package/dist/web/assets/proxy-settings-section-0DvNh1B-.js +3 -0
- package/dist/web/assets/{pug-8VCubZ3H.js → pug-BfN8CpEH.js} +1 -1
- package/dist/web/assets/{qml-D8AF1RZ6.js → qml-D6fRdLZt.js} +1 -1
- package/dist/web/assets/query-audit-section-BA5rT6Sh.js +1 -0
- package/dist/web/assets/radar-KQ55EAFF-DBIZEaIU.js +1 -0
- package/dist/web/assets/{razor-6v042IJW.js → razor-fg-9AScR.js} +1 -1
- package/dist/web/assets/{react-DHs6efqI.js → react-X4jUntEf.js} +1 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-CPa3XElX.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-view-De_a4skd.js +1 -0
- package/dist/web/assets/remote-desktop-readiness-gate-CPrDIv2d.js +1 -0
- package/dist/web/assets/remote-desktop-window-content-CG1StfCE.js +1 -0
- package/dist/web/assets/{rst-mTGFo4AQ.js → rst-BZgx076b.js} +1 -1
- package/dist/web/assets/{ruby-BBaOP636.js → ruby-iUkWDcLo.js} +1 -1
- package/dist/web/assets/{run-in-terminal-Zl5iIx-B.js → run-in-terminal-bKRwuO2z.js} +1 -1
- package/dist/web/assets/{sas-D5QEkIH2.js → sas-CLXO0lZ6.js} +1 -1
- package/dist/web/assets/{save-Dxq-Zepc.js → save-CipGrG1k.js} +1 -1
- package/dist/web/assets/schedules-settings-section-B2f7V7Xr.js +1 -0
- package/dist/web/assets/{scss-C5Mu6y2u.js → scss-DQqteGaG.js} +1 -1
- package/dist/web/assets/send-Bb0jtcR6.js +1 -0
- package/dist/web/assets/{send-to-chat-Cv7ikFUC.js → send-to-chat-S8LCNX7V.js} +1 -1
- package/dist/web/assets/separator-DqGg3f9c.js +1 -0
- package/dist/web/assets/settings-body-omv04jbb.js +2 -0
- package/dist/web/assets/settings-store-Brjd0QnO.js +2 -0
- package/dist/web/assets/settings-tab-NdnBTqyj.js +1 -0
- package/dist/web/assets/settings-window-content-Chou1JGa.js +1 -0
- package/dist/web/assets/{shaderlab-mLnpMpN3.js → shaderlab-CeMTqagR.js} +1 -1
- package/dist/web/assets/{shellsession-qdcDfUuk.js → shellsession-BAkquZAV.js} +1 -1
- package/dist/web/assets/{shield-check-CH7F1P7m.js → shield-check-BsG3LV8i.js} +1 -1
- package/dist/web/assets/{shield-off-C9HiF0-6.js → shield-off-7jKWpJBD.js} +1 -1
- package/dist/web/assets/{soy-CGGf1G43.js → soy-Bxm9Ez55.js} +1 -1
- package/dist/web/assets/{sparkles-BnuLH6Ck.js → sparkles-Dh-_J2Rd.js} +1 -1
- package/dist/web/assets/{sparql-tQHB50VD.js → sparql-C1phwdLc.js} +1 -1
- package/dist/web/assets/sql-query-editor-Dk9y3jqy.js +1 -0
- package/dist/web/assets/sqlite-viewer-4xFhkLYy.js +1 -0
- package/dist/web/assets/{stata-DVQNLMcR.js → stata-DGNMQdEb.js} +1 -1
- package/dist/web/assets/{surrealql-lu576tBi.js → surrealql-ByVrV320.js} +1 -1
- package/dist/web/assets/{svelte-CSCrOBjT.js → svelte-CNWjIMhl.js} +1 -1
- package/dist/web/assets/system-monitor-body-B_1iMVOI.js +2 -0
- package/dist/web/assets/system-monitor-tab-B2j9mWBW.js +1 -0
- package/dist/web/assets/system-monitor-window-content-BOjq53uU.js +1 -0
- package/dist/web/assets/{tab-host-window-content-C5kYP0-z.js → tab-host-window-content-CW_dlYlW.js} +1 -1
- package/dist/web/assets/{tab-store-DS3YlUwR.js → tab-store-PZsP5ZwG.js} +1 -1
- package/dist/web/assets/{table-BXDyImqk.js → table-CRJsmfeg.js} +1 -1
- package/dist/web/assets/team-member-sheet-CkR4qO7V.js +2 -0
- package/dist/web/assets/team-member-window-content-CmF5ia3n.js +1 -0
- package/dist/web/assets/{templ-CPt8AS4Q.js → templ-Cf0gQL4I.js} +1 -1
- package/dist/web/assets/{terminal-tab-8bKQ3CWR.js → terminal-tab-HXZkicIc.js} +3 -3
- package/dist/web/assets/{tex-BrTByT7i.js → tex-BTIqPw_R.js} +1 -1
- package/dist/web/assets/{text-wrap-CzMjB6GD.js → text-wrap-oiekm5SB.js} +1 -1
- package/dist/web/assets/tool-cards-jA1BTLZj.js +9 -0
- package/dist/web/assets/treemap-KZPCXAKY-DFiG9UYW.js +1 -0
- package/dist/web/assets/{ts-tags-DSqEZ7A8.js → ts-tags-B8dN_Yf6.js} +1 -1
- package/dist/web/assets/{twig-DW9qc1bf.js → twig-BgemonbJ.js} +1 -1
- package/dist/web/assets/use-accounts-data-BI_5qNvr.js +1 -0
- package/dist/web/assets/{use-blob-url-DKbV7PM_.js → use-blob-url-Gfi69PtP.js} +1 -1
- package/dist/web/assets/{use-coarse-long-press-DGyC48NF.js → use-coarse-long-press-BmI_GVW5.js} +1 -1
- package/dist/web/assets/{use-is-mobile-ClE-UcPF.js → use-is-mobile-CYW3FJFE.js} +1 -1
- package/dist/web/assets/use-monaco-theme-BcQqKVbE.js +1 -0
- package/dist/web/assets/use-open-team-member-CruUTqzD.js +1 -0
- package/dist/web/assets/use-remote-desktop-display-choice-DY5BWRTo.js +1 -0
- package/dist/web/assets/{use-visual-viewport-CCX21c9w.js → use-visual-viewport-rnvuooQZ.js} +1 -1
- package/dist/web/assets/{use-websocket-CWpG7PWN.js → use-websocket-DetaY8OI.js} +1 -1
- package/dist/web/assets/vendor-markdown-Ls5AcU91.js +291 -0
- package/dist/web/assets/{vendor-mermaid-0Nt-ew76.js → vendor-mermaid-BKWscKt0.js} +1 -1
- package/dist/web/assets/{vendor-ui-CXJtBULr.js → vendor-ui-CaeeFO0B.js} +1 -1
- package/dist/web/assets/video-preview-C0uH2228.js +1 -0
- package/dist/web/assets/{vue-CJNDL1g-.js → vue-D3D4lbXz.js} +1 -1
- package/dist/web/assets/{vue-html-CG5BKtq1.js → vue-html-DYEQFAiK.js} +1 -1
- package/dist/web/assets/{vue-vine-Dd5HZ7T5.js → vue-vine-D0OPNscN.js} +1 -1
- package/dist/web/assets/{window-pip-registry-9H6Y-HqR.js → window-pip-registry-BEN0QNSW.js} +1 -1
- package/dist/web/assets/window-store-DGKf9rPG.js +1 -0
- package/dist/web/assets/ws-auth-Dawx17PM.js +1 -0
- package/dist/web/assets/x-YDu4m2Iq.js +1 -0
- package/dist/web/assets/{xml-DTOm5niz.js → xml-DHaWgpya.js} +1 -1
- package/dist/web/assets/{xsl-BD_kBC04.js → xsl-DVRN8JsA.js} +1 -1
- package/dist/web/index.html +42 -65
- package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
- package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
- package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
- package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
- package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
- package/dist/web/sw.js +1 -1
- package/docs/codebase-summary.md +9 -5
- package/docs/lessons-learned.md +11 -0
- package/package.json +2 -2
- package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
- package/scripts/check-ppm-dir-usage.sh +0 -0
- package/scripts/install.sh +0 -0
- package/scripts/release.sh +0 -0
- package/src/providers/claude-agent-sdk.ts +51 -43
- package/src/providers/codex-app-server/codex-event-mapper.ts +70 -5
- package/src/providers/codex-app-server/codex-history.ts +24 -0
- package/src/providers/codex-app-server/codex-jsonrpc-client.ts +39 -2
- package/src/providers/codex-app-server/codex-protocol.ts +24 -0
- package/src/providers/codex-app-server/codex-provider.ts +166 -18
- package/src/providers/codex-app-server/codex-rollout-items.ts +149 -0
- package/src/providers/codex-app-server/codex-skill-parser.ts +63 -0
- package/src/providers/codex-app-server/codex-usage-fetch.ts +22 -20
- package/src/providers/codex-app-server/codex-usage-source.ts +37 -0
- package/src/server/routes/chat.ts +95 -12
- package/src/server/routes/codex-accounts.ts +9 -5
- package/src/server/ws/chat.ts +55 -5
- package/src/services/claude-usage-source.ts +40 -0
- package/src/services/claude-usage.service.ts +77 -146
- package/src/services/codex-account-login.ts +123 -55
- package/src/services/codex-account.service.ts +27 -4
- package/src/services/db.service.ts +161 -20
- package/src/services/edge-forwarder.ts +191 -191
- package/src/services/fs-credential-path-guard.ts +35 -8
- package/src/services/fs-path-guard.service.ts +1 -0
- package/src/services/named-tunnel/named-tunnel-retry.ts +26 -0
- package/src/services/named-tunnel/network-ready.ts +63 -0
- package/src/services/postgres.service.ts +144 -72
- package/src/services/provider-usage/index.ts +63 -0
- package/src/services/provider-usage/register-usage-sources.ts +18 -0
- package/src/services/provider-usage/usage-memory-cache.ts +71 -0
- package/src/services/provider-usage/usage-registry.ts +157 -0
- package/src/services/provider-usage/usage-scheduler.ts +117 -0
- package/src/services/provider-usage/usage-snapshot-store.ts +126 -0
- package/src/services/provider-usage/usage-source.ts +82 -0
- package/src/services/slash-discovery/builtin-commands.ts +13 -0
- package/src/services/slash-discovery/codex-skill-items.ts +57 -0
- package/src/services/slash-discovery/provider-skill-sigil.ts +19 -0
- package/src/services/slash-discovery/types.ts +19 -0
- package/src/services/supervisor.ts +98 -0
- package/src/services/transcript-images.ts +14 -7
- package/src/services/upgrade.service.ts +7 -2
- package/src/services/windows-process-tree.ts +351 -351
- package/src/shared/image-limits.ts +37 -0
- package/src/types/api.ts +3 -1
- package/src/types/chat.ts +13 -1
- package/src/types/extension-messages.ts +2 -0
- package/src/web/app.tsx +1 -7
- package/src/web/components/chat/attachment-chips.tsx +10 -0
- package/src/web/components/chat/chat-history-bar.tsx +10 -1
- package/src/web/components/chat/chat-tab.tsx +54 -14
- package/src/web/components/chat/message-input.tsx +113 -15
- package/src/web/components/chat/message-list.tsx +14 -0
- package/src/web/components/chat/session-debug-dialog.tsx +2 -1
- package/src/web/components/chat/slash-command-picker.tsx +22 -3
- package/src/web/components/chat/tool-cards.tsx +47 -4
- package/src/web/components/chat/usage-badge.tsx +43 -447
- package/src/web/components/chat/user-message-parse.ts +33 -11
- package/src/web/components/database/database-viewer.tsx +34 -11
- package/src/web/components/database/extract-query-table.ts +24 -0
- package/src/web/components/database/glide-data-grid.tsx +11 -4
- package/src/web/components/database/glide-grid-types.ts +4 -0
- package/src/web/components/database/split-sql-statements.ts +116 -0
- package/src/web/components/database/sql-completion-provider.ts +1 -26
- package/src/web/components/database/sql-query-editor.tsx +2 -1
- package/src/web/components/database/use-database.ts +16 -1
- package/src/web/components/database/use-glide-cell-content.ts +11 -6
- package/src/web/components/editor/code-editor.tsx +30 -56
- package/src/web/components/floating-window/window-content-registry.ts +2 -0
- package/src/web/components/floating-window/window-store-types.ts +1 -1
- package/src/web/components/layout/command-palette.tsx +2 -2
- package/src/web/components/layout/mobile-drawer.tsx +5 -4
- package/src/web/components/layout/mobile-nav.tsx +5 -1
- package/src/web/components/layout/nav-section-rail.tsx +2 -1
- package/src/web/components/layout/project-bottom-sheet.tsx +3 -9
- package/src/web/components/layout/sidebar.tsx +0 -2
- package/src/web/components/layout/tab-pop-out-menu-item.tsx +3 -2
- package/src/web/components/settings/accounts/account-add-dialog.tsx +155 -0
- package/src/web/components/settings/accounts/account-backup-format.ts +19 -0
- package/src/web/components/settings/accounts/account-bucket-row.tsx +55 -0
- package/src/web/components/settings/accounts/account-card.tsx +127 -0
- package/src/web/components/settings/accounts/account-delete-confirm.tsx +62 -0
- package/src/web/components/settings/accounts/account-export-dialog.tsx +154 -0
- package/src/web/components/settings/accounts/account-export-simulation.tsx +196 -0
- package/src/web/components/settings/accounts/account-import-dialog.tsx +72 -0
- package/src/web/components/settings/accounts/account-profile-panel.tsx +52 -0
- package/src/web/components/{chat → settings/accounts}/account-rotation-settings.tsx +1 -1
- package/src/web/components/settings/accounts/account-token-test-dialog.tsx +147 -0
- package/src/web/components/settings/accounts/account-usage-format.ts +92 -0
- package/src/web/components/{chat/usage-pattern-chart.tsx → settings/accounts/account-usage-pattern-chart.tsx} +1 -1
- package/src/web/components/settings/accounts/accounts-pane-header.tsx +93 -0
- package/src/web/components/settings/accounts/accounts-settings-section.tsx +80 -0
- package/src/web/components/settings/accounts/claude-accounts-section.tsx +168 -0
- package/src/web/components/settings/accounts/codex-accounts-section.tsx +137 -0
- package/src/web/components/settings/accounts/codex-add-account-dialog.tsx +130 -0
- package/src/web/components/settings/accounts/codex-backup-dialog.tsx +101 -0
- package/src/web/components/settings/accounts/codex-rotation-dialog.tsx +65 -0
- package/src/web/components/settings/accounts/use-accounts-data.ts +81 -0
- package/src/web/components/settings/accounts/use-codex-accounts.ts +151 -0
- package/src/web/components/settings/ai-settings-section.tsx +14 -4
- package/src/web/components/settings/appearance-settings-section.tsx +125 -0
- package/src/web/components/settings/general-settings-section.tsx +87 -0
- package/src/web/components/settings/jira-watcher-section.tsx +34 -0
- package/src/web/components/settings/notifications-settings-section.tsx +37 -0
- package/src/web/components/settings/open-settings.ts +42 -0
- package/src/web/components/settings/resolve-open-settings-action.ts +45 -0
- package/src/web/components/settings/settings-body.tsx +110 -0
- package/src/web/components/settings/settings-categories.ts +102 -0
- package/src/web/components/settings/settings-category-list.tsx +56 -0
- package/src/web/components/settings/settings-category-rail.tsx +62 -0
- package/src/web/components/settings/settings-section-content.tsx +43 -0
- package/src/web/components/settings/settings-tab.tsx +15 -329
- package/src/web/components/settings/settings-window-content.tsx +24 -0
- package/src/web/hooks/use-chat.ts +44 -4
- package/src/web/hooks/use-global-keybindings.ts +3 -4
- package/src/web/hooks/use-url-sync.ts +30 -0
- package/src/web/hooks/use-usage.ts +23 -11
- package/src/web/lib/attachment-marker-split.ts +44 -0
- package/src/web/lib/image-animation.ts +51 -0
- package/src/web/lib/image-resize-limits.ts +47 -0
- package/src/web/lib/image-resize.ts +133 -0
- package/src/web/lib/sidebar-tabs/tab-registry.ts +9 -9
- package/src/web/lib/slash-items-cache.ts +27 -7
- package/src/web/stores/panel-utils.ts +11 -0
- package/src/web/stores/settings-store.ts +4 -2
- package/src/web/stores/window-panel-actions.ts +1 -3
- package/src/web/stores/window-panel-persistence.ts +9 -4
- package/dist/web/assets/adaptive-context-menu-JfuUSqbe.js +0 -1
- package/dist/web/assets/ai-resource-editor-MfdJKVM5.js +0 -1
- package/dist/web/assets/ai-settings-section-CJE7jFsj.js +0 -1
- package/dist/web/assets/api-client-DLWwuUkJ.js +0 -1
- package/dist/web/assets/api-settings-BFuaZrNC.js +0 -1
- package/dist/web/assets/api-settings-CPg4yGvS.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-6konx-J1.js +0 -1
- package/dist/web/assets/chat-tab-bSOVoC5N.js +0 -15
- package/dist/web/assets/chevron-right-D7Pn5agw.js +0 -1
- package/dist/web/assets/circle-4g2coECH.js +0 -1
- package/dist/web/assets/clipboard-B-gwJ8DF.js +0 -1
- package/dist/web/assets/clock-DbiNiwCG.js +0 -1
- package/dist/web/assets/code-DdKXqtf_.js +0 -1
- package/dist/web/assets/code-editor-COTa37oe.js +0 -10
- package/dist/web/assets/context-menu-BEAa1qhd.js +0 -1
- package/dist/web/assets/database-viewer-Dta7lSSk.js +0 -1
- package/dist/web/assets/dialog-DrL3WEXd.js +0 -1
- package/dist/web/assets/diff-viewer-CT0lmDc9.js +0 -4
- package/dist/web/assets/explorer-body-BGjR2Gbt.js +0 -2
- package/dist/web/assets/explorer-window-content-DAhHh5EQ.js +0 -1
- package/dist/web/assets/extension-store-ClQpnxQr.js +0 -1
- package/dist/web/assets/external-link-ojDHcQxQ.js +0 -1
- package/dist/web/assets/eye-off-Rq1n2-oe.js +0 -1
- package/dist/web/assets/file-store-28e44KP3.js +0 -1
- package/dist/web/assets/git-log-panel-D1-YXZ0O.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-ikUwsekd.js +0 -1
- package/dist/web/assets/glide-data-grid-BSsqxe2v.js +0 -138
- package/dist/web/assets/group-chat-tab-CMtzoaFc.js +0 -1
- package/dist/web/assets/index-BkHlMfPD.js +0 -52
- package/dist/web/assets/index-SFFv2-VM.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-BAgmboBp.js +0 -1
- package/dist/web/assets/input-DTmI80l2.js +0 -1
- package/dist/web/assets/keybindings-store-Bi0q9x4Q.js +0 -1
- package/dist/web/assets/keybindings-store-_KfovSz7.js +0 -1
- package/dist/web/assets/loader-circle-DD65e9-7.js +0 -1
- package/dist/web/assets/lock-BEtt_eTO.js +0 -1
- package/dist/web/assets/markdown-renderer-DroTmKbJ.js +0 -1
- package/dist/web/assets/markdown-renderer-ntDi3Tx-.js +0 -3
- package/dist/web/assets/mobile-explorer-sheet-BP6iU4ma.js +0 -1
- package/dist/web/assets/monitor-smartphone-C1wUPOSg.js +0 -1
- package/dist/web/assets/notification-store-II5Kw9rD.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-RVlbbDRS.js +0 -1
- package/dist/web/assets/panel-store-Dv28vz3L.js +0 -1
- package/dist/web/assets/pdf-preview-DKeIIJTv.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-ZWb4izpk.js +0 -1
- package/dist/web/assets/play-CPbVzGgq.js +0 -1
- package/dist/web/assets/plus-BxqD2FIZ.js +0 -1
- package/dist/web/assets/portal-container-context-BmG4--I-.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-bsP5x1Bx.js +0 -1
- package/dist/web/assets/refresh-cw-BALzw7L3.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-sheet-CYZUjYTO.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-view-DaoB49k4.js +0 -1
- package/dist/web/assets/remote-desktop-readiness-gate--LJgsuoj.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-Cihx4PNZ.js +0 -1
- package/dist/web/assets/rotate-ccw-CuvNBM80.js +0 -1
- package/dist/web/assets/scroll-area-B5yhPpZo.js +0 -1
- package/dist/web/assets/send-Cits8q0l.js +0 -1
- package/dist/web/assets/settings-store-Dp4i8zeA.js +0 -2
- package/dist/web/assets/settings-tab-BtMvvKAF.js +0 -3
- package/dist/web/assets/settings-tab-UPPycGRF.js +0 -1
- package/dist/web/assets/shallow-BBJ_qoEF.js +0 -1
- package/dist/web/assets/sql-query-editor-DZqnYIB7.js +0 -1
- package/dist/web/assets/sqlite-viewer-DXutos0e.js +0 -1
- package/dist/web/assets/switch-BPol3BFC.js +0 -1
- package/dist/web/assets/system-monitor-body-Ct8_GX_f.js +0 -2
- package/dist/web/assets/system-monitor-tab-DC_EerdI.js +0 -1
- package/dist/web/assets/system-monitor-window-content-D25DMNpw.js +0 -1
- package/dist/web/assets/team-member-sheet-DvmioeQL.js +0 -2
- package/dist/web/assets/team-member-window-content-BJ_XFw8-.js +0 -1
- package/dist/web/assets/tool-cards-pIu_cmEL.js +0 -8
- package/dist/web/assets/trash-2-BjShZ7if.js +0 -1
- package/dist/web/assets/treemap-KZPCXAKY-Dh1oxNdQ.js +0 -1
- package/dist/web/assets/triangle-alert-qQ6Plydp.js +0 -1
- package/dist/web/assets/upload-7Zs6oaK5.js +0 -1
- package/dist/web/assets/use-monaco-theme-DaxAk3gB.js +0 -1
- package/dist/web/assets/use-open-team-member-CopUuDHG.js +0 -1
- package/dist/web/assets/use-remote-desktop-display-choice-BUA_1eaH.js +0 -1
- package/dist/web/assets/vendor-markdown-CKTlmXbz.js +0 -291
- package/dist/web/assets/video-preview-C2_fdunE.js +0 -1
- package/dist/web/assets/window-store-CNzkeUyO.js +0 -1
- package/dist/web/assets/x-Dzu9ecti.js +0 -1
- package/src/web/components/chat/account-dialogs.tsx +0 -384
- package/src/web/components/settings/accounts-settings-section.tsx +0 -1090
- package/src/web/components/settings/codex-accounts-section.tsx +0 -198
- package/src/web/components/settings/telegram-settings-section.tsx +0 -113
- /package/dist/web/assets/{abap-Cjj2olMr.js → abap-h3QyH8MW.js} +0 -0
- /package/dist/web/assets/{actionscript-3-DCkW7kWC.js → actionscript-3-mkxkq-FN.js} +0 -0
- /package/dist/web/assets/{ada-AEWvZXuX.js → ada-DhISK_EH.js} +0 -0
- /package/dist/web/assets/{andromeeda-D7cSpME6.js → andromeeda-DnlWWesX.js} +0 -0
- /package/dist/web/assets/{apache-DjvX0RSe.js → apache-P5AK3sfA.js} +0 -0
- /package/dist/web/assets/{apex-70xIZ0pH.js → apex-CJUPehg2.js} +0 -0
- /package/dist/web/assets/{applescript-C3qKqxmA.js → applescript-Dx6GGbay.js} +0 -0
- /package/dist/web/assets/{apply-theme-DJ794zSQ.js → apply-theme-DxTXOAcV.js} +0 -0
- /package/dist/web/assets/{ara-iGt2zixk.js → ara-95Kx3qKb.js} +0 -0
- /package/dist/web/assets/{asciidoc-Bs2t-06W.js → asciidoc-Dp0RbIej.js} +0 -0
- /package/dist/web/assets/{asm-B-T0eqhE.js → asm-C82coUjU.js} +0 -0
- /package/dist/web/assets/{aurora-x-D3h_9M3E.js → aurora-x-BFc3zeop.js} +0 -0
- /package/dist/web/assets/{awk-CZwfwpPi.js → awk-ChjC0icr.js} +0 -0
- /package/dist/web/assets/{ayu-dark-DulkglEA.js → ayu-dark-DYfUf_PB.js} +0 -0
- /package/dist/web/assets/{ayu-light-DyNQiRpn.js → ayu-light-Bh6O0plO.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-CcEt4xkI.js → ayu-mirage-DlNhHTGq.js} +0 -0
- /package/dist/web/assets/{ballerina-OhQPjyHS.js → ballerina-Ceoll3Sk.js} +0 -0
- /package/dist/web/assets/{bat-DqKcJTUn.js → bat-C7QS6Lfq.js} +0 -0
- /package/dist/web/assets/{beancount-BdXlMi8T.js → beancount-5NMEAZgs.js} +0 -0
- /package/dist/web/assets/{berry-DvgY6BkY.js → berry-BBL83Vbk.js} +0 -0
- /package/dist/web/assets/{bibtex-BSRJq0yM.js → bibtex-L7rPkPSG.js} +0 -0
- /package/dist/web/assets/{bicep-DpAdumKt.js → bicep-BOwHIINh.js} +0 -0
- /package/dist/web/assets/{bird2-rlbtp8di.js → bird2-CWLsMom2.js} +0 -0
- /package/dist/web/assets/{c-AjOLBXIs.js → c-CD4Ku5Cr.js} +0 -0
- /package/dist/web/assets/{c3-DqIXz4Ia.js → c3-CpMl-w3G.js} +0 -0
- /package/dist/web/assets/{cadence-DAJtUpOE.js → cadence-CvgF6ehN.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-CdCK2c2a.js → can-open-in-ppm-j3jh7fUo.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-DUPoeZfb.js → catppuccin-frappe-eo_dTm3F.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-CK3U68Ql.js → catppuccin-latte-DoiMCU_7.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-BzteLnIX.js → catppuccin-macchiato-UQxsK2Vh.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-ozLd0aBR.js → catppuccin-mocha-J0i-8RNy.js} +0 -0
- /package/dist/web/assets/{clarity-6KqEoBfz.js → clarity-DXYM8-SA.js} +0 -0
- /package/dist/web/assets/{clojure-B_uBL5C7.js → clojure-DzamsL69.js} +0 -0
- /package/dist/web/assets/{cmake-B3nxBNmj.js → cmake-CR6RP_T_.js} +0 -0
- /package/dist/web/assets/{codeowners-DkS-51at.js → codeowners-D1s6-IyY.js} +0 -0
- /package/dist/web/assets/{codeql-rpr1z_mR.js → codeql-DAqNWMkS.js} +0 -0
- /package/dist/web/assets/{common-lisp-BfNbv_W6.js → common-lisp-8e-KtitB.js} +0 -0
- /package/dist/web/assets/{coq-EAW-1nw0.js → coq-UEq0WUcO.js} +0 -0
- /package/dist/web/assets/{csharp-BE3Gn5bq.js → csharp-wxJ0TuCm.js} +0 -0
- /package/dist/web/assets/{css-Cr27CfPY.js → css-Cerub-yB.js} +0 -0
- /package/dist/web/assets/{csv-B5VtaPtH.js → csv-Bh9_WD_h.js} +0 -0
- /package/dist/web/assets/{csv-parser-B2v8MoUy.js → csv-parser-BPIDt6ni.js} +0 -0
- /package/dist/web/assets/{cue-jm6lYNrx.js → cue-rzM1ztqU.js} +0 -0
- /package/dist/web/assets/{cypher-DXkvK-pn.js → cypher-DGaTQ8m0.js} +0 -0
- /package/dist/web/assets/{d-CgG1_yzd.js → d-DbkZGuhB.js} +0 -0
- /package/dist/web/assets/{dark-plus-CALzcIVB.js → dark-plus-BO2bzZcA.js} +0 -0
- /package/dist/web/assets/{dart-BEVoLlWg.js → dart-BDhCoqVg.js} +0 -0
- /package/dist/web/assets/{dax-H0BW3Q_P.js → dax-Bh03stWh.js} +0 -0
- /package/dist/web/assets/{desktop-BtYq1l_0.js → desktop-BOdr_fwq.js} +0 -0
- /package/dist/web/assets/{diff-CnBYdfTg.js → diff-Bb_gTsS2.js} +0 -0
- /package/dist/web/assets/{dist-CJJKpgR7.js → dist-BUMDu0av.js} +0 -0
- /package/dist/web/assets/{docker-COBitpld.js → docker-BDsTF5ce.js} +0 -0
- /package/dist/web/assets/{dotenv-CG0x1m7c.js → dotenv-CbCWp9Ti.js} +0 -0
- /package/dist/web/assets/{dracula-CLCi47X-.js → dracula-Cd1fxvoX.js} +0 -0
- /package/dist/web/assets/{dracula-soft-BMv5cCAV.js → dracula-soft-B7eXkfXG.js} +0 -0
- /package/dist/web/assets/{dream-maker-1Bd4OSww.js → dream-maker-XlzuS21R.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-B-2zKLn-.js → emacs-lisp-CXV1Ki_7.js} +0 -0
- /package/dist/web/assets/{everforest-dark-BGVynCbV.js → everforest-dark-DuqR60Dg.js} +0 -0
- /package/dist/web/assets/{everforest-light--rn3R7Cm.js → everforest-light-q4BPnenD.js} +0 -0
- /package/dist/web/assets/{fennel-SWg9Us_u.js → fennel-B-e0TX2Q.js} +0 -0
- /package/dist/web/assets/{fish-B04oqgKk.js → fish-Bzb6-TtF.js} +0 -0
- /package/dist/web/assets/{fluent-BefzAGMN.js → fluent-B-szH2-f.js} +0 -0
- /package/dist/web/assets/{format-bytes-gQw5wzqa.js → format-bytes-CKXk9k2d.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-alw7jMxb.js → fortran-free-form-OS6SGYC0.js} +0 -0
- /package/dist/web/assets/{gdscript-BAyhvjCx.js → gdscript-FYq4OP2d.js} +0 -0
- /package/dist/web/assets/{gdshader-Crl09X-B.js → gdshader-BpG6P8rw.js} +0 -0
- /package/dist/web/assets/{genie-BSIGRHu7.js → genie-BS9RLAna.js} +0 -0
- /package/dist/web/assets/{gherkin-DJhSnSWH.js → gherkin-B_9FdtGs.js} +0 -0
- /package/dist/web/assets/{github-dark-r8C6qwug.js → github-dark-cfaIlnBV.js} +0 -0
- /package/dist/web/assets/{github-dark-default-FVtXjcm8.js → github-dark-default-B2Wv65He.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-CzrUPgSb.js → github-dark-dimmed-CtM9X76s.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-1s9rF5EB.js → github-dark-high-contrast-DLHyk0tG.js} +0 -0
- /package/dist/web/assets/{github-light-BvIWCkl7.js → github-light-DWEI10iG.js} +0 -0
- /package/dist/web/assets/{github-light-default-CVdhzrxV.js → github-light-default-DcJkK1YN.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-CBAf6hjF.js → github-light-high-contrast-Bcx9iKDt.js} +0 -0
- /package/dist/web/assets/{gleam-DvL1q7aa.js → gleam-DVpiM6b2.js} +0 -0
- /package/dist/web/assets/{gn-BG5NvwAf.js → gn-B80DNc0i.js} +0 -0
- /package/dist/web/assets/{gnuplot-BARnM-zG.js → gnuplot-D9i3MaT-.js} +0 -0
- /package/dist/web/assets/{go-DXvjjq4q.js → go-CwCBf_Pt.js} +0 -0
- /package/dist/web/assets/{groovy-CRylSKhr.js → groovy-D7mEfwmW.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-BVPjnqm7.js → gruvbox-dark-hard-C4R4MOoH.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-U_PDgq0a.js → gruvbox-dark-medium-CZnFC0s8.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-CkzNtpbr.js → gruvbox-dark-soft-DFcJlEBl.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-DfMmR2G8.js → gruvbox-light-hard-B6IF_1L-.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-BGCak-dd.js → gruvbox-light-medium-C19AZJv2.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-DnpM3fkN.js → gruvbox-light-soft-BAnpDm9B.js} +0 -0
- /package/dist/web/assets/{haskell-DXgGIDD_.js → haskell-CsXSYXRG.js} +0 -0
- /package/dist/web/assets/{haxe-B7qlTpRz.js → haxe-B1uyiI9t.js} +0 -0
- /package/dist/web/assets/{hcl-ADWfVe_Q.js → hcl-DlYKn5q9.js} +0 -0
- /package/dist/web/assets/{hjson-BaM9gLbq.js → hjson-C_0VMGJJ.js} +0 -0
- /package/dist/web/assets/{hlsl-iqaRA4MG.js → hlsl-zt2PEzbg.js} +0 -0
- /package/dist/web/assets/{horizon-BPpTnhB9.js → horizon-6wQfyOK0.js} +0 -0
- /package/dist/web/assets/{horizon-bright-yBLg3QbE.js → horizon-bright-CqI2qTim.js} +0 -0
- /package/dist/web/assets/{houston-eFOp0ySL.js → houston-BljpHDoG.js} +0 -0
- /package/dist/web/assets/{hy-BALeMn3i.js → hy-XzuL55dw.js} +0 -0
- /package/dist/web/assets/{image-gallery-Bqb-wjNw.js → image-gallery-Cj8nd4xW.js} +0 -0
- /package/dist/web/assets/{imba-CdGUm_GD.js → imba-CqhhjBrP.js} +0 -0
- /package/dist/web/assets/{ini-DG7imsqe.js → ini-CiFDi6cY.js} +0 -0
- /package/dist/web/assets/{java-D5GTT474.js → java-OsQgTL5-.js} +0 -0
- /package/dist/web/assets/{javascript-V7WwJvRm.js → javascript-qWAOScxl.js} +0 -0
- /package/dist/web/assets/{json-B9FhwAgR.js → json-RiAKsaD1.js} +0 -0
- /package/dist/web/assets/{json5--Abk7MUy.js → json5-BeCnS3H2.js} +0 -0
- /package/dist/web/assets/{jsonc-C8jb-Z6x.js → jsonc-CbTW1Mka.js} +0 -0
- /package/dist/web/assets/{jsonl-CaJNxfkm.js → jsonl-BLSi_669.js} +0 -0
- /package/dist/web/assets/{jsonnet-CPrKMSCU.js → jsonnet-C3xAggou.js} +0 -0
- /package/dist/web/assets/{jssm-DEu-9_uo.js → jssm-CLdiagC9.js} +0 -0
- /package/dist/web/assets/{jsx-MfAsCWDW.js → jsx-lYE1lb50.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-DnbcAduF.js → kanagawa-dragon-Cd1vcrpD.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-yxsY3ew8.js → kanagawa-lotus-Dw3QKtk9.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-C2RyvZaM.js → kanagawa-wave-DIq5Zv5G.js} +0 -0
- /package/dist/web/assets/{kdl-CvssmvcU.js → kdl-BfDV0825.js} +0 -0
- /package/dist/web/assets/{kotlin-jufO49mU.js → kotlin-DONxR4wD.js} +0 -0
- /package/dist/web/assets/{kusto-BAFnylR7.js → kusto-BDP63Fh5.js} +0 -0
- /package/dist/web/assets/{laserwave-BgxFC4Ge.js → laserwave-BeBrWApL.js} +0 -0
- /package/dist/web/assets/{lean-Bgt24V1D.js → lean-DOZc0N1A.js} +0 -0
- /package/dist/web/assets/{less-Bk_UaqsM.js → less-C3H9NlDv.js} +0 -0
- /package/dist/web/assets/{libesm-dcbw4_18.js → libesm-DLBE59ul.js} +0 -0
- /package/dist/web/assets/{light-plus-nMxFfInN.js → light-plus-C_kTL608.js} +0 -0
- /package/dist/web/assets/{llvm-3_PASDOO.js → llvm-Dsp27U3P.js} +0 -0
- /package/dist/web/assets/{log-CKm0CxB1.js → log-BywQaTCy.js} +0 -0
- /package/dist/web/assets/{logo-DFP1_5U6.js → logo-BirJgciO.js} +0 -0
- /package/dist/web/assets/{luau-BcZnoyh3.js → luau-Btrz5kJB.js} +0 -0
- /package/dist/web/assets/{make-CpNSwgwT.js → make-Kl7nFQrS.js} +0 -0
- /package/dist/web/assets/{markdown-DhU3Djpv.js → markdown-qsC4P8S-.js} +0 -0
- /package/dist/web/assets/{material-theme-DD5L3OqF.js → material-theme-CC__ui3p.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-CG2_tD3R.js → material-theme-darker-D4FxG2FZ.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-CzNkUc9Y.js → material-theme-lighter-zGqiWcxn.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-BtLiAaF_.js → material-theme-ocean-CZIdTTB1.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-Brvjz035.js → material-theme-palenight-CEIs0GcK.js} +0 -0
- /package/dist/web/assets/{matlab-CCY0KtMD.js → matlab-Cf0aUjN9.js} +0 -0
- /package/dist/web/assets/{mdx-CH6YY0aS.js → mdx-CIsXjH86.js} +0 -0
- /package/dist/web/assets/{mermaid-Bqh3gFI0.js → mermaid-B06WO9Qc.js} +0 -0
- /package/dist/web/assets/{min-dark-BUwfTzI6.js → min-dark-z8fyp0FT.js} +0 -0
- /package/dist/web/assets/{min-light-oLanXCOk.js → min-light-Dr-qesbW.js} +0 -0
- /package/dist/web/assets/{mipsasm-CTh3hPWK.js → mipsasm-CWHzYLcE.js} +0 -0
- /package/dist/web/assets/{mojo-DXdszZKa.js → mojo-D9zXrKmv.js} +0 -0
- /package/dist/web/assets/{monokai-TNE_tRxc.js → monokai-4b-AKOpX.js} +0 -0
- /package/dist/web/assets/{moonbit-jrk-Gx1X.js → moonbit-Dl4dhOZJ.js} +0 -0
- /package/dist/web/assets/{move-DItn61bH.js → move-DTYKJmXy.js} +0 -0
- /package/dist/web/assets/{narrat-3kB4dkh0.js → narrat-DnvXOS-o.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-CSKtwETr.js → nextflow-groovy-Di8QnEXg.js} +0 -0
- /package/dist/web/assets/{night-owl-Dm91nSIq.js → night-owl-Czh0yq-H.js} +0 -0
- /package/dist/web/assets/{night-owl-light-riwrcPRg.js → night-owl-light-D_OpZqOR.js} +0 -0
- /package/dist/web/assets/{nix-sLgl9zGF.js → nix-2MvcjpK3.js} +0 -0
- /package/dist/web/assets/{nord-TWkJPI6M.js → nord-CG_r4cyk.js} +0 -0
- /package/dist/web/assets/{nushell-CDZi4-Gf.js → nushell-DINskMcU.js} +0 -0
- /package/dist/web/assets/{objective-c-DW9PboNp.js → objective-c-Dv7BIicR.js} +0 -0
- /package/dist/web/assets/{objective-cpp-2XCCX6q1.js → objective-cpp-CpqsCxPb.js} +0 -0
- /package/dist/web/assets/{ocaml-CxPdnjrh.js → ocaml-DwTsYSmW.js} +0 -0
- /package/dist/web/assets/{odin-BJjlKpkM.js → odin-G-4O4k4t.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-K1KOmBZ7.js → one-dark-pro-13Pv5SjI.js} +0 -0
- /package/dist/web/assets/{one-light-DQB-vUyZ.js → one-light-Bi2e86mS.js} +0 -0
- /package/dist/web/assets/{openscad-DCoLwn4j.js → openscad-D0WsOTij.js} +0 -0
- /package/dist/web/assets/{pascal-z2ktbbWp.js → pascal-DmDStuXr.js} +0 -0
- /package/dist/web/assets/{pkl-Dyq4-v6z.js → pkl-Bl6Qjtuo.js} +0 -0
- /package/dist/web/assets/{plastic-C4Rq5PnD.js → plastic-UeSVSjTI.js} +0 -0
- /package/dist/web/assets/{plsql-BCIjO63y.js → plsql-DreFC689.js} +0 -0
- /package/dist/web/assets/{po-_kXpd_0n.js → po-ZIjccxsX.js} +0 -0
- /package/dist/web/assets/{poimandres-C9XggjB3.js → poimandres-CvQHJPrI.js} +0 -0
- /package/dist/web/assets/{polar-DV_1L04z.js → polar-BrMB0Kgh.js} +0 -0
- /package/dist/web/assets/{postcss-BuqS91mQ.js → postcss-pgUsil1T.js} +0 -0
- /package/dist/web/assets/{powerquery-DK13B2Dc.js → powerquery-CTsZkCg_.js} +0 -0
- /package/dist/web/assets/{powershell-tkuhhtuP.js → powershell-CcwVuBAp.js} +0 -0
- /package/dist/web/assets/{prisma-WNXfBtpr.js → prisma-spjnAI5W.js} +0 -0
- /package/dist/web/assets/{prolog-BzEdNKfO.js → prolog-ChCkZpDO.js} +0 -0
- /package/dist/web/assets/{proto-B5meVsrA.js → proto-wVGqK4gB.js} +0 -0
- /package/dist/web/assets/{puppet-C7h_p4r2.js → puppet-CED7iwPQ.js} +0 -0
- /package/dist/web/assets/{purescript-qH1KxAs6.js → purescript-u9lGiQKV.js} +0 -0
- /package/dist/web/assets/{python-DeTIYd5V.js → python-DeqTx6ef.js} +0 -0
- /package/dist/web/assets/{qmldir-4Rm5AvD3.js → qmldir-BH1uoPrX.js} +0 -0
- /package/dist/web/assets/{qss-YRbeNtr0.js → qss-CUVKA_13.js} +0 -0
- /package/dist/web/assets/{r-DHwHK0Jh.js → r-BqdPzqYH.js} +0 -0
- /package/dist/web/assets/{racket-gKn4SFLQ.js → racket-C2eTgPN3.js} +0 -0
- /package/dist/web/assets/{raku-DWTmAj3A.js → raku-uAay1sWx.js} +0 -0
- /package/dist/web/assets/{red-BFuHRZFS.js → red-DTZP1ZfC.js} +0 -0
- /package/dist/web/assets/{reg-DYoOF5tH.js → reg-Cdk3YsxY.js} +0 -0
- /package/dist/web/assets/{regexp-6rXQHD2-.js → regexp-Bpa2aS7K.js} +0 -0
- /package/dist/web/assets/{rel-Z_tTruCU.js → rel-bNsb1-3L.js} +0 -0
- /package/dist/web/assets/{resolve-theme-4fexHWl6.js → resolve-theme-DQk_YcsT.js} +0 -0
- /package/dist/web/assets/{riscv-DfTfKuvU.js → riscv-DSaubByW.js} +0 -0
- /package/dist/web/assets/{ron-CsUtW8gQ.js → ron-DbQzYM6i.js} +0 -0
- /package/dist/web/assets/{rose-pine-Dng_vCo6.js → rose-pine-C7RsprTu.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-Ds88Nezf.js → rose-pine-dawn-VP976YzL.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-DWpzrqNz.js → rose-pine-moon-XHZNhOqd.js} +0 -0
- /package/dist/web/assets/{rosmsg-h2sO84oD.js → rosmsg-BVQlx0Lj.js} +0 -0
- /package/dist/web/assets/{rust-Dx86yfCi.js → rust-VHaQ3rh3.js} +0 -0
- /package/dist/web/assets/{sass-BwwVhdT9.js → sass-CO-kMkwD.js} +0 -0
- /package/dist/web/assets/{scala-WJqNEN-w.js → scala-cY0GXkiz.js} +0 -0
- /package/dist/web/assets/{scheme-uwj-t1Rp.js → scheme-D4LNZE0X.js} +0 -0
- /package/dist/web/assets/{sdbl-BYy5hgEz.js → sdbl-Bh2Fd2cn.js} +0 -0
- /package/dist/web/assets/{shellscript-DWTW_w2K.js → shellscript-KKclmCnt.js} +0 -0
- /package/dist/web/assets/{slack-dark-BvhGq5vQ.js → slack-dark-D6YQ-fqf.js} +0 -0
- /package/dist/web/assets/{slack-ochin-C9g0ZRFr.js → slack-ochin-CDf66CDj.js} +0 -0
- /package/dist/web/assets/{smalltalk-ZEiZ6i7U.js → smalltalk-D2OH_QIl.js} +0 -0
- /package/dist/web/assets/{snazzy-light-29OA24q5.js → snazzy-light-CQeF8G1K.js} +0 -0
- /package/dist/web/assets/{solarized-dark-Bnq9IOn_.js → solarized-dark-LTPf1iLZ.js} +0 -0
- /package/dist/web/assets/{solarized-light-CYdI7T5g.js → solarized-light-Ose1n88k.js} +0 -0
- /package/dist/web/assets/{solidity-DcuIDB-_.js → solidity-lFuOMHki.js} +0 -0
- /package/dist/web/assets/{splunk-D5ONE91g.js → splunk-Bov-sB5S.js} +0 -0
- /package/dist/web/assets/{sql-DIwJJDU-.js → sql-BKyZ6wmk.js} +0 -0
- /package/dist/web/assets/{ssh-config-D8717tLI.js → ssh-config-CqJoMGMb.js} +0 -0
- /package/dist/web/assets/{stylus-Cm1lO4Y6.js → stylus-Bf5pWRLI.js} +0 -0
- /package/dist/web/assets/{swift-X8oWpSeU.js → swift-D5d1hn_u.js} +0 -0
- /package/dist/web/assets/{synthwave-84-DF-t1UKY.js → synthwave-84-B4c6YAgO.js} +0 -0
- /package/dist/web/assets/{system-verilog-JYyHFpPf.js → system-verilog-D7fXyDsI.js} +0 -0
- /package/dist/web/assets/{systemd-Da-5Ma_c.js → systemd-oNagzlBK.js} +0 -0
- /package/dist/web/assets/{talonscript-CXraVPho.js → talonscript-C6Nqm9Dm.js} +0 -0
- /package/dist/web/assets/{tasl-BCmWezH9.js → tasl-CaWy-cBX.js} +0 -0
- /package/dist/web/assets/{tcl-BTufzfbf.js → tcl-C435P5Mq.js} +0 -0
- /package/dist/web/assets/{terraform-CtUA9c8W.js → terraform-CVDxqdPf.js} +0 -0
- /package/dist/web/assets/{tokyo-night-BCThYXmz.js → tokyo-night-Uhs17tQ3.js} +0 -0
- /package/dist/web/assets/{toml-D5YlDr6Y.js → toml-j6fPOJ6P.js} +0 -0
- /package/dist/web/assets/{tsv-CBB-P_Sl.js → tsv-h78O6xrL.js} +0 -0
- /package/dist/web/assets/{tsx-D5B4juDp.js → tsx-Bol-vCrQ.js} +0 -0
- /package/dist/web/assets/{turtle-D8PCeJ-v.js → turtle-Dw8eFf04.js} +0 -0
- /package/dist/web/assets/{typescript-CMPnTawp.js → typescript-Dtz7DPci.js} +0 -0
- /package/dist/web/assets/{typespec-CHxldxIW.js → typespec-BIOQ7dMP.js} +0 -0
- /package/dist/web/assets/{typst-Q8CqX5OR.js → typst-CoaGMjzu.js} +0 -0
- /package/dist/web/assets/{utils-QHe_Q6UF.js → utils-C9OjCrou.js} +0 -0
- /package/dist/web/assets/{v-Bf2hKAMu.js → v-BoqFdpJ7.js} +0 -0
- /package/dist/web/assets/{vala-Cbete-UE.js → vala-COBpNnCC.js} +0 -0
- /package/dist/web/assets/{vb-CA-xWAcX.js → vb-DxI86znh.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-BHKF_Nsf.js → vendor-xterm-BaMOj65g.js} +0 -0
- /package/dist/web/assets/{verilog-B3wb8CfT.js → verilog-DcTAamuQ.js} +0 -0
- /package/dist/web/assets/{vesper-D6r3VJq_.js → vesper-DO298JSv.js} +0 -0
- /package/dist/web/assets/{vhdl-C42S_797.js → vhdl-Dq-bDVSN.js} +0 -0
- /package/dist/web/assets/{viml-BePrIAs8.js → viml-zHTRWuQh.js} +0 -0
- /package/dist/web/assets/{vitesse-black-DRqn61Ck.js → vitesse-black-jlYMJCBf.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-39_8UE-w.js → vitesse-dark-DS0sUlSZ.js} +0 -0
- /package/dist/web/assets/{vitesse-light-B1jwrxDm.js → vitesse-light-HSeZ2YhG.js} +0 -0
- /package/dist/web/assets/{vyper-tDJv3imF.js → vyper-BQfKVO3M.js} +0 -0
- /package/dist/web/assets/{wasm-ChtccNfv.js → wasm-B1xp9bLp.js} +0 -0
- /package/dist/web/assets/{wasm-DR7eskZH.js → wasm-B6mCuI8h.js} +0 -0
- /package/dist/web/assets/{wenyan-CezVfqN3.js → wenyan-CfpS05sP.js} +0 -0
- /package/dist/web/assets/{wgsl-CwOcIiIG.js → wgsl-14GDsWRD.js} +0 -0
- /package/dist/web/assets/{wikitext-C5coomYc.js → wikitext-Ccbq5EVX.js} +0 -0
- /package/dist/web/assets/{wit-C9yyX6-U.js → wit-C19hb3R5.js} +0 -0
- /package/dist/web/assets/{wolfram-BWi0rMAa.js → wolfram-B-ZSwGwP.js} +0 -0
- /package/dist/web/assets/{yaml-CGXhKUtT.js → yaml-mw_JUeio.js} +0 -0
- /package/dist/web/assets/{zenscript-Do7WLoJh.js → zenscript-WbzDRjvF.js} +0 -0
- /package/dist/web/assets/{zig-Cr80Qrru.js → zig-F0vvImTJ.js} +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { ChatEvent } from "../provider.interface.ts";
|
|
2
|
+
import { redactTruncate } from "./codex-redact.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Rollout `item_completed` records → PPM chat events.
|
|
6
|
+
*
|
|
7
|
+
* Newer codex versions stopped writing `user_message` / `agent_message` events
|
|
8
|
+
* to the rollout and write one `item_completed` per finished item instead. The
|
|
9
|
+
* items are the same family the live app-server streams, but spelled
|
|
10
|
+
* differently: PascalCase types (`AgentMessage`, not `agentMessage`),
|
|
11
|
+
* snake_case fields (`aggregated_output`), a `command` array rather than a
|
|
12
|
+
* string, and image generation arriving as a generic `Extension` carrying a
|
|
13
|
+
* `kind`. Reading a rollout without this mapping yields nothing at all, which
|
|
14
|
+
* is why a codex conversation displayed while it was live and came back empty
|
|
15
|
+
* once it had to be read from disk.
|
|
16
|
+
*
|
|
17
|
+
* Kept separate from the rollout parser so the two spellings of the same item
|
|
18
|
+
* family do not interleave in one function.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
type Item = Record<string, unknown>;
|
|
22
|
+
|
|
23
|
+
/** What a rollout item contributes to the reconstructed transcript. */
|
|
24
|
+
export type RolloutItemMapping =
|
|
25
|
+
| { kind: "user"; text: string }
|
|
26
|
+
| { kind: "assistant"; text: string }
|
|
27
|
+
| { kind: "events"; events: ChatEvent[] }
|
|
28
|
+
| { kind: "ignore" };
|
|
29
|
+
|
|
30
|
+
const IGNORED_ITEM_TYPES = new Set([
|
|
31
|
+
// Reasoning is not persisted as a message anywhere else in PPM; showing it as
|
|
32
|
+
// one would put the model's private notes in the transcript.
|
|
33
|
+
"Reasoning",
|
|
34
|
+
// Injected instructions, not part of the conversation.
|
|
35
|
+
"DeveloperMessage",
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
function textFrom(content: unknown): string {
|
|
39
|
+
if (typeof content === "string") return content;
|
|
40
|
+
if (!Array.isArray(content)) return "";
|
|
41
|
+
return content
|
|
42
|
+
.map((c) => (c && typeof c === "object" && typeof (c as Item).text === "string" ? (c as Item).text as string : ""))
|
|
43
|
+
.filter(Boolean)
|
|
44
|
+
.join("");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** `file:///C:/Users/...` → a path a person recognises. */
|
|
48
|
+
function plainCwd(cwd: unknown): string | undefined {
|
|
49
|
+
if (typeof cwd !== "string" || !cwd) return undefined;
|
|
50
|
+
if (!cwd.startsWith("file://")) return cwd;
|
|
51
|
+
try {
|
|
52
|
+
const path = decodeURIComponent(new URL(cwd).pathname);
|
|
53
|
+
// Windows paths come back as `/C:/…`.
|
|
54
|
+
return /^\/[A-Za-z]:/.test(path) ? path.slice(1) : path;
|
|
55
|
+
} catch {
|
|
56
|
+
return cwd;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The command text to show. `parsed_cmd[].cmd` is the unwrapped script; the raw
|
|
62
|
+
* `command` array is the interpreter plus its arguments, whose path arrives with
|
|
63
|
+
* doubled backslashes on Windows.
|
|
64
|
+
*/
|
|
65
|
+
function commandText(item: Item): string {
|
|
66
|
+
const parsed = Array.isArray(item.parsed_cmd) ? item.parsed_cmd : [];
|
|
67
|
+
const parts: string[] = [];
|
|
68
|
+
for (const entry of parsed) {
|
|
69
|
+
const cmd = (entry as Item)?.cmd;
|
|
70
|
+
if (typeof cmd === "string" && cmd.trim()) parts.push(cmd);
|
|
71
|
+
}
|
|
72
|
+
if (parts.length > 0) return parts.join("\n");
|
|
73
|
+
if (Array.isArray(item.command)) return item.command.filter((c) => typeof c === "string").join(" ");
|
|
74
|
+
return typeof item.command === "string" ? item.command : "";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function commandExecutionEvents(item: Item): ChatEvent[] {
|
|
78
|
+
const raw = Array.isArray(item.command) ? item.command.join(" ") : String(item.command ?? "");
|
|
79
|
+
const tool = /powershell|pwsh/i.test(raw) ? "PowerShell" : "Bash";
|
|
80
|
+
const toolUseId = typeof item.id === "string" ? item.id : undefined;
|
|
81
|
+
const exit = item.exit_code;
|
|
82
|
+
return [
|
|
83
|
+
{ type: "tool_use", tool, input: { command: commandText(item), cwd: plainCwd(item.cwd) }, toolUseId },
|
|
84
|
+
{
|
|
85
|
+
type: "tool_result",
|
|
86
|
+
output: redactTruncate(item.aggregated_output ?? item.stdout ?? ""),
|
|
87
|
+
isError: typeof exit === "number" && exit !== 0,
|
|
88
|
+
toolUseId,
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function imageGenerationEvents(item: Item): ChatEvent[] {
|
|
94
|
+
const toolUseId = typeof item.id === "string" ? item.id : undefined;
|
|
95
|
+
const failure = item.failure;
|
|
96
|
+
return [
|
|
97
|
+
{
|
|
98
|
+
// `file_path` matches what the chat's image preview already looks for, and
|
|
99
|
+
// `result` — the whole PNG as base64 — is deliberately dropped.
|
|
100
|
+
type: "tool_use",
|
|
101
|
+
tool: "ImageGen",
|
|
102
|
+
input: {
|
|
103
|
+
file_path: item.savedPath ?? null,
|
|
104
|
+
prompt: item.revisedPrompt ?? null,
|
|
105
|
+
transparentBackground: item.transparentBackground ?? false,
|
|
106
|
+
},
|
|
107
|
+
toolUseId,
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
type: "tool_result",
|
|
111
|
+
output: failure != null ? redactTruncate(failure) : String(item.savedPath ?? "generated"),
|
|
112
|
+
isError: failure != null,
|
|
113
|
+
toolUseId,
|
|
114
|
+
},
|
|
115
|
+
];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Everything the mapping does not know, shown rather than silently dropped. */
|
|
119
|
+
function genericEvents(item: Item): ChatEvent[] {
|
|
120
|
+
const toolUseId = typeof item.id === "string" ? item.id : undefined;
|
|
121
|
+
// A base64 payload would otherwise land in the transcript whole.
|
|
122
|
+
const { result: _result, ...rest } = item;
|
|
123
|
+
return [{ type: "tool_use", tool: String(item.type ?? "tool"), input: rest, toolUseId }];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function mapRolloutItem(item: unknown): RolloutItemMapping {
|
|
127
|
+
if (!item || typeof item !== "object") return { kind: "ignore" };
|
|
128
|
+
const it = item as Item;
|
|
129
|
+
const type = typeof it.type === "string" ? it.type : "";
|
|
130
|
+
|
|
131
|
+
if (IGNORED_ITEM_TYPES.has(type)) return { kind: "ignore" };
|
|
132
|
+
if (type === "UserMessage") {
|
|
133
|
+
const text = textFrom(it.content);
|
|
134
|
+
return text ? { kind: "user", text } : { kind: "ignore" };
|
|
135
|
+
}
|
|
136
|
+
if (type === "AgentMessage") {
|
|
137
|
+
const text = textFrom(it.content);
|
|
138
|
+
return text ? { kind: "assistant", text } : { kind: "ignore" };
|
|
139
|
+
}
|
|
140
|
+
if (type === "CommandExecution") return { kind: "events", events: commandExecutionEvents(it) };
|
|
141
|
+
if (type === "Extension") {
|
|
142
|
+
// `kind` names the extension; image generation is the one PPM renders specially.
|
|
143
|
+
if (String(it.kind ?? "").startsWith("image_gen")) {
|
|
144
|
+
return { kind: "events", events: imageGenerationEvents(it) };
|
|
145
|
+
}
|
|
146
|
+
return { kind: "events", events: genericEvents(it) };
|
|
147
|
+
}
|
|
148
|
+
return { kind: "events", events: genericEvents(it) };
|
|
149
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { CodexSkill, SkillListResponse } from "./codex-protocol.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pure map of a `skills/list` response → the skills codex would actually run.
|
|
5
|
+
*
|
|
6
|
+
* The response groups skills by the cwd they were resolved for, because one
|
|
7
|
+
* app-server can hold several workspaces at once. PPM asks about a single cwd,
|
|
8
|
+
* so every group is flattened into one list.
|
|
9
|
+
*
|
|
10
|
+
* Disabled skills are dropped: codex will not run them, so offering them in the
|
|
11
|
+
* picker would produce a prompt that silently does nothing. Nameless entries
|
|
12
|
+
* are dropped for the same reason — the name is the only thing that invokes a
|
|
13
|
+
* skill.
|
|
14
|
+
*
|
|
15
|
+
* Duplicates are possible (a project-scope skill shadowing a user-scope one of
|
|
16
|
+
* the same name). First occurrence wins, matching the order codex returns.
|
|
17
|
+
*/
|
|
18
|
+
export function parseSkillList(res: unknown): CodexSkill[] {
|
|
19
|
+
const groups = (res as SkillListResponse | undefined)?.data;
|
|
20
|
+
if (!Array.isArray(groups)) return [];
|
|
21
|
+
const out: CodexSkill[] = [];
|
|
22
|
+
const seen = new Set<string>();
|
|
23
|
+
for (const group of groups) {
|
|
24
|
+
const skills = (group as { skills?: unknown })?.skills;
|
|
25
|
+
if (!Array.isArray(skills)) continue;
|
|
26
|
+
for (const raw of skills) {
|
|
27
|
+
if (!raw || typeof raw !== "object") continue;
|
|
28
|
+
const s = raw as Partial<CodexSkill>;
|
|
29
|
+
if (typeof s.name !== "string" || !s.name) continue;
|
|
30
|
+
if (s.enabled === false) continue;
|
|
31
|
+
if (seen.has(s.name)) continue;
|
|
32
|
+
seen.add(s.name);
|
|
33
|
+
out.push({
|
|
34
|
+
name: s.name,
|
|
35
|
+
description: typeof s.description === "string" ? s.description : undefined,
|
|
36
|
+
scope: typeof s.scope === "string" ? s.scope : undefined,
|
|
37
|
+
path: typeof s.path === "string" ? s.path : undefined,
|
|
38
|
+
// Disabled entries were skipped above, so anything reaching here is enabled.
|
|
39
|
+
enabled: true,
|
|
40
|
+
interface: parseInterface(s.interface),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The presentation block codex ships for its own skills — display name, one-line
|
|
49
|
+
* blurb, and icon files on disk. Every field is optional: a user-authored skill
|
|
50
|
+
* has no `interface` at all, so the caller must be able to fall back to `name`.
|
|
51
|
+
*/
|
|
52
|
+
function parseInterface(raw: unknown): CodexSkill["interface"] {
|
|
53
|
+
if (!raw || typeof raw !== "object") return undefined;
|
|
54
|
+
const i = raw as Record<string, unknown>;
|
|
55
|
+
const str = (v: unknown) => (typeof v === "string" && v ? v : undefined);
|
|
56
|
+
return {
|
|
57
|
+
displayName: str(i.displayName),
|
|
58
|
+
shortDescription: str(i.shortDescription),
|
|
59
|
+
iconSmall: str(i.iconSmall),
|
|
60
|
+
iconLarge: str(i.iconLarge),
|
|
61
|
+
defaultPrompt: str(i.defaultPrompt),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -1,37 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Codex quota reader: runs a short-lived app-server bound to a given CODEX_HOME
|
|
3
|
+
* and reads `account/rateLimits/read`.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately uncached and unguarded — one call, one answer, throws on failure.
|
|
6
|
+
* Caching, the background sweep, the timeout, and persistence belong to the
|
|
7
|
+
* shared provider-usage layer, which reaches this through `codex-usage-source`.
|
|
8
|
+
* It lives here rather than in the provider or the account service to avoid a
|
|
9
|
+
* provider↔account-service import cycle.
|
|
6
10
|
*/
|
|
7
11
|
import type { UsageInfo } from "../provider.interface.ts";
|
|
8
|
-
import { CodexJsonRpcClient } from "./codex-jsonrpc-client.ts";
|
|
12
|
+
import { CodexJsonRpcClient, CONTROL_REQUEST_TIMEOUT_MS } from "./codex-jsonrpc-client.ts";
|
|
9
13
|
import { parseCodexUsage } from "./codex-usage-parser.ts";
|
|
10
14
|
import type { GetAccountRateLimitsResponse } from "./codex-protocol.ts";
|
|
11
15
|
|
|
12
16
|
const CLIENT_INFO = { name: "ppm", title: "PPM", version: "0.0.0" };
|
|
13
17
|
const CAPABILITIES = { experimentalApi: true, requestAttestation: false, optOutNotificationMethods: null };
|
|
14
|
-
const TTL = 60 * 1000;
|
|
15
|
-
|
|
16
|
-
const cache = new Map<string, { usage: UsageInfo; expiry: number }>();
|
|
17
|
-
|
|
18
|
-
export async function fetchCodexUsage(codexHome?: string): Promise<UsageInfo> {
|
|
19
|
-
const key = codexHome ?? "__default__";
|
|
20
|
-
const hit = cache.get(key);
|
|
21
|
-
if (hit && Date.now() < hit.expiry) return hit.usage;
|
|
22
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Read one codex login's quota, live.
|
|
21
|
+
*
|
|
22
|
+
* Throws on any failure — the caller distinguishes a failure from a login with
|
|
23
|
+
* nothing to report, and a swallowed error would be stored as the latter.
|
|
24
|
+
*/
|
|
25
|
+
export async function fetchCodexUsageLive(codexHome?: string): Promise<UsageInfo> {
|
|
23
26
|
const client = new CodexJsonRpcClient();
|
|
24
27
|
try {
|
|
25
28
|
client.start({ cwd: process.cwd(), codexHome });
|
|
26
|
-
await client.request("initialize", { clientInfo: CLIENT_INFO, capabilities: CAPABILITIES });
|
|
29
|
+
await client.request("initialize", { clientInfo: CLIENT_INFO, capabilities: CAPABILITIES }, CONTROL_REQUEST_TIMEOUT_MS);
|
|
27
30
|
client.notify("initialized");
|
|
28
|
-
const res = await client.request<GetAccountRateLimitsResponse>("account/rateLimits/read", {});
|
|
29
|
-
|
|
30
|
-
cache.set(key, { usage, expiry: Date.now() + TTL });
|
|
31
|
-
return usage;
|
|
32
|
-
} catch {
|
|
33
|
-
return {};
|
|
31
|
+
const res = await client.request<GetAccountRateLimitsResponse>("account/rateLimits/read", {}, CONTROL_REQUEST_TIMEOUT_MS);
|
|
32
|
+
return parseCodexUsage(res);
|
|
34
33
|
} finally {
|
|
34
|
+
// Runs even when a request rejects on its own timeout, so a subprocess that
|
|
35
|
+
// accepted the spawn and then went silent is still killed rather than left
|
|
36
|
+
// behind. Before the client had a timeout, that was one orphan per attempt.
|
|
35
37
|
client.close();
|
|
36
38
|
}
|
|
37
39
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { UsageInfo } from "../../types/chat.ts";
|
|
2
|
+
import type { ProviderUsageSource } from "../../services/provider-usage/usage-source.ts";
|
|
3
|
+
import { AMBIENT_ACCOUNT_KEY } from "../../services/provider-usage/usage-source.ts";
|
|
4
|
+
import { listCodexAccounts, getCodexAccount } from "../../services/codex-account.service.ts";
|
|
5
|
+
import { fetchCodexUsageLive } from "./codex-usage-fetch.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Codex's plug into the shared usage layer.
|
|
9
|
+
*
|
|
10
|
+
* All this has to say is which accounts exist and how to read one; the sweep,
|
|
11
|
+
* the timeout, the cache, and the snapshot persistence come from the layer.
|
|
12
|
+
* Before it existed, codex had a lazy 60-second memory cache and no sweep at
|
|
13
|
+
* all, so every read that missed spawned an app-server on the request path.
|
|
14
|
+
*/
|
|
15
|
+
export const codexUsageSource: ProviderUsageSource = {
|
|
16
|
+
providerId: "codex",
|
|
17
|
+
|
|
18
|
+
listAccountIds(): string[] {
|
|
19
|
+
return listCodexAccounts().map((a) => a.id);
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
async fetch(accountId: string): Promise<UsageInfo> {
|
|
23
|
+
// No managed accounts configured — read whatever login `~/.codex` holds.
|
|
24
|
+
if (accountId === AMBIENT_ACCOUNT_KEY) return fetchCodexUsageLive();
|
|
25
|
+
|
|
26
|
+
const account = getCodexAccount(accountId);
|
|
27
|
+
// The account was removed between the sweep listing it and this fetch.
|
|
28
|
+
// Throwing (rather than returning {}) keeps it out of the store: an empty
|
|
29
|
+
// object would be persisted as a real reading for an account that is gone.
|
|
30
|
+
if (!account) throw new Error(`codex account ${accountId} no longer exists`);
|
|
31
|
+
|
|
32
|
+
const usage = await fetchCodexUsageLive(account.home);
|
|
33
|
+
// The rate-limit payload carries the plan name, which the parser puts in
|
|
34
|
+
// activeAccountLabel; the account's own label is the truthful one.
|
|
35
|
+
return { ...usage, activeAccountId: account.id, activeAccountLabel: account.label };
|
|
36
|
+
},
|
|
37
|
+
};
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import { resolve, join, basename } from "node:path";
|
|
3
3
|
import { existsSync, readdirSync, statSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { unlink } from "node:fs/promises";
|
|
4
5
|
import { ensureUploadsDir, resolveUploadPath } from "../../services/chat-upload-storage.service.ts";
|
|
5
6
|
import { chatService } from "../../services/chat.service.ts";
|
|
6
7
|
import { draftService } from "../../services/draft.service.ts";
|
|
7
8
|
import { providerRegistry } from "../../providers/registry.ts";
|
|
8
9
|
import { renameSession as sdkRenameSession } from "@anthropic-ai/claude-agent-sdk";
|
|
9
10
|
import { listSlashItems, searchSlashItems, invalidateCache } from "../../services/slash-items.service.ts";
|
|
11
|
+
import type { SlashItem } from "../../services/slash-discovery/types.ts";
|
|
10
12
|
import { ensureSdkCommands, invalidateSdkCommands } from "../../services/slash-discovery/sdk-commands.ts";
|
|
11
|
-
import { upsertSlashRecent, getSlashRecents, setSessionClearedFrom, listTurnUsage, getSessionAccount } from "../../services/db.service.ts";
|
|
13
|
+
import { upsertSlashRecent, getSlashRecents, setSessionClearedFrom, listTurnUsage, getSessionAccount, getSessionProvider, resolveMigratedSession } from "../../services/db.service.ts";
|
|
12
14
|
import type { TurnUsage } from "../../shared/turn-usage.ts";
|
|
13
15
|
import { getCachedUsage, refreshUsageNow } from "../../services/claude-usage.service.ts";
|
|
14
16
|
import { getSessionLog } from "../../services/session-log.service.ts";
|
|
@@ -36,10 +38,14 @@ chatRoutes.get("/slash-items", async (c) => {
|
|
|
36
38
|
try {
|
|
37
39
|
const projectPath = c.get("projectPath");
|
|
38
40
|
const q = c.req.query("q");
|
|
39
|
-
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
const sessionId = c.req.query("sessionId") || undefined;
|
|
42
|
+
// A brand-new tab has no DB row yet, so the client's own view of which
|
|
43
|
+
// provider it is on wins; the stored value is the fallback for reloads.
|
|
44
|
+
const providerId = c.req.query("providerId")
|
|
45
|
+
|| (sessionId ? getSessionProvider(sessionId) : null)
|
|
46
|
+
|| undefined;
|
|
47
|
+
|
|
48
|
+
let items = await listSlashItemsForProvider(projectPath, providerId, sessionId);
|
|
43
49
|
const recentNames = getSlashRecents(projectPath);
|
|
44
50
|
if (q) items = searchSlashItems(items, q, 20, recentNames);
|
|
45
51
|
return c.json(ok({ items, recentNames }));
|
|
@@ -48,6 +54,41 @@ chatRoutes.get("/slash-items", async (c) => {
|
|
|
48
54
|
}
|
|
49
55
|
});
|
|
50
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Slash items a session can actually run.
|
|
59
|
+
*
|
|
60
|
+
* A provider that owns its own skill runtime answers for itself, and the
|
|
61
|
+
* disk-discovered Claude-side list is not merged in: a codex turn cannot
|
|
62
|
+
* execute a Claude skill, and a Claude turn cannot execute a codex one, so a
|
|
63
|
+
* combined list would offer entries that quietly do nothing when picked.
|
|
64
|
+
*
|
|
65
|
+
* PPM's host-level built-ins ARE kept, because they are intercepted before any
|
|
66
|
+
* provider runs and therefore work in every tab. Dropping them would have cost
|
|
67
|
+
* a codex tab `/clear`, `/skills`, and `/version` — commands that do work —
|
|
68
|
+
* which is a different thing from hiding ones that don't.
|
|
69
|
+
*
|
|
70
|
+
* When such a provider reports no skills — app-server failed to spawn, account
|
|
71
|
+
* not logged in — only the built-ins remain, rather than falling back to the
|
|
72
|
+
* Claude list; showing runnable-looking Claude entries in a codex tab is the
|
|
73
|
+
* very confusion this split exists to remove.
|
|
74
|
+
*/
|
|
75
|
+
async function listSlashItemsForProvider(
|
|
76
|
+
projectPath: string,
|
|
77
|
+
providerId: string | undefined,
|
|
78
|
+
sessionId: string | undefined,
|
|
79
|
+
): Promise<SlashItem[]> {
|
|
80
|
+
const provider = providerId ? providerRegistry.get(providerId) : null;
|
|
81
|
+
if (provider?.listSkills) {
|
|
82
|
+
const { codexSkillsToSlashItems } = await import("../../services/slash-discovery/codex-skill-items.ts");
|
|
83
|
+
const { getHostBuiltinSlashItems } = await import("../../services/slash-discovery/builtin-commands.ts");
|
|
84
|
+
return [...codexSkillsToSlashItems(await provider.listSkills(sessionId)), ...getHostBuiltinSlashItems()];
|
|
85
|
+
}
|
|
86
|
+
// Claude's own built-ins need a live SDK session to enumerate; cached 30 min,
|
|
87
|
+
// so only the first request per project pays for the CLI spawn.
|
|
88
|
+
await ensureSdkCommands(projectPath);
|
|
89
|
+
return listSlashItems(projectPath);
|
|
90
|
+
}
|
|
91
|
+
|
|
51
92
|
/** DELETE /chat/slash-items/cache — invalidate cached slash items for this project */
|
|
52
93
|
chatRoutes.delete("/slash-items/cache", (c) => {
|
|
53
94
|
try {
|
|
@@ -77,9 +118,16 @@ chatRoutes.get("/usage", async (c) => {
|
|
|
77
118
|
// Non-Claude providers expose their own quota via provider.getUsage().
|
|
78
119
|
const providerId = c.req.query("providerId");
|
|
79
120
|
if (providerId && providerId !== "claude") {
|
|
80
|
-
const provider = providerRegistry.get(providerId)
|
|
121
|
+
const provider = providerRegistry.get(providerId);
|
|
81
122
|
if (provider?.getUsage) {
|
|
82
|
-
|
|
123
|
+
// `?refresh=1` is the user pressing refresh, so drop the cached value
|
|
124
|
+
// first — otherwise getUsage answers from cache and the button does
|
|
125
|
+
// nothing visible. Claude's branch below does the same via a sweep.
|
|
126
|
+
if (c.req.query("refresh")) {
|
|
127
|
+
const { invalidateUsage } = await import("../../services/provider-usage/usage-registry.ts");
|
|
128
|
+
invalidateUsage(providerId);
|
|
129
|
+
}
|
|
130
|
+
try { return c.json(ok(await provider.getUsage(c.req.query("session")))); } catch { return c.json(ok({})); }
|
|
83
131
|
}
|
|
84
132
|
return c.json(ok({}));
|
|
85
133
|
}
|
|
@@ -269,7 +317,11 @@ chatRoutes.get("/search", async (c) => {
|
|
|
269
317
|
/** GET /chat/sessions/:id/messages — get message history */
|
|
270
318
|
chatRoutes.get("/sessions/:id/messages", async (c) => {
|
|
271
319
|
try {
|
|
272
|
-
const
|
|
320
|
+
const requestedId = c.req.param("id");
|
|
321
|
+
// A provider that mints its own session id leaves the id PPM created behind,
|
|
322
|
+
// owning no transcript. Follow the recorded move so a tab that still holds
|
|
323
|
+
// the old id reads the conversation instead of an empty list.
|
|
324
|
+
const id = resolveMigratedSession(requestedId);
|
|
273
325
|
const providerId = c.req.query("providerId") ?? "claude";
|
|
274
326
|
const messages = await chatService.getMessages(providerId, id);
|
|
275
327
|
// Forking re-timestamps the copied prefix (both the Claude SDK and codex
|
|
@@ -288,7 +340,14 @@ chatRoutes.get("/sessions/:id/messages", async (c) => {
|
|
|
288
340
|
}
|
|
289
341
|
// versionMap ships with the history so the `‹ n/m ›` switcher needs no
|
|
290
342
|
// per-message request. Ordinals absent from the map have no edited versions.
|
|
291
|
-
|
|
343
|
+
// Hand back the id that actually owns this transcript so the client can
|
|
344
|
+
// adopt it — otherwise every future read, and the next turn, still targets
|
|
345
|
+
// the abandoned one.
|
|
346
|
+
return c.json(ok({
|
|
347
|
+
messages,
|
|
348
|
+
versionMap: resolveVersionMap(id),
|
|
349
|
+
...(id !== requestedId ? { canonicalSessionId: id } : {}),
|
|
350
|
+
}));
|
|
292
351
|
} catch (e) {
|
|
293
352
|
return c.json(err((e as Error).message), 500);
|
|
294
353
|
}
|
|
@@ -791,9 +850,10 @@ chatRoutes.get("/sessions/:id/images", async (c) => {
|
|
|
791
850
|
* would be lost by the rewrite.
|
|
792
851
|
*
|
|
793
852
|
* `includeAttachments` also clears images the user attached to their own messages. It is
|
|
794
|
-
* opt-in because
|
|
795
|
-
* oversized attachment makes every later turn of the session fail,
|
|
796
|
-
* remove it.
|
|
853
|
+
* opt-in because an attachment that arrived without an uploaded copy has no other source, but
|
|
854
|
+
* it has to be reachable: one oversized attachment makes every later turn of the session fail,
|
|
855
|
+
* and nothing else can remove it. Attachments PPM sent itself keep a copy in the uploads
|
|
856
|
+
* directory, with the path still in the message text, so those stay readable afterwards.
|
|
797
857
|
*/
|
|
798
858
|
chatRoutes.post("/sessions/:id/images/strip", async (c) => {
|
|
799
859
|
try {
|
|
@@ -899,6 +959,29 @@ chatRoutes.post("/upload", async (c) => {
|
|
|
899
959
|
}
|
|
900
960
|
});
|
|
901
961
|
|
|
962
|
+
/**
|
|
963
|
+
* DELETE /chat/uploads/:filename — discard an upload that was never sent.
|
|
964
|
+
*
|
|
965
|
+
* Removing an attachment from the composer used to leave its uploaded file behind with
|
|
966
|
+
* nothing referencing it, and uploads are kept indefinitely because chat history points at
|
|
967
|
+
* them. Only a file the user just abandoned is deletable this way; one already named in a
|
|
968
|
+
* message is reachable from the transcript and is not this route's business.
|
|
969
|
+
*/
|
|
970
|
+
chatRoutes.delete("/uploads/:filename", async (c) => {
|
|
971
|
+
try {
|
|
972
|
+
const filename = c.req.param("filename");
|
|
973
|
+
if (!filename || filename.includes("/") || filename.includes("..")) {
|
|
974
|
+
return c.json(err("Invalid filename"), 400);
|
|
975
|
+
}
|
|
976
|
+
const filePath = resolveUploadPath(filename);
|
|
977
|
+
// Already gone is the outcome the caller wanted, so it is not an error.
|
|
978
|
+
if (filePath) await unlink(filePath).catch(() => {});
|
|
979
|
+
return c.json(ok({ deleted: true }));
|
|
980
|
+
} catch (e) {
|
|
981
|
+
return c.json(err((e as Error).message), 500);
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
|
|
902
985
|
/** GET /chat/uploads/:filename — serve uploaded files (images, etc.) for preview */
|
|
903
986
|
chatRoutes.get("/uploads/:filename", async (c) => {
|
|
904
987
|
try {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import { ok, err } from "../../types/api.ts";
|
|
3
3
|
import { listCodexAccounts, removeCodexAccount, getAllCodexUsages, getCodexStrategy, setCodexStrategy, type CodexStrategy } from "../../services/codex-account.service.ts";
|
|
4
|
-
import { addApiKeyAccount, startDeviceLogin,
|
|
4
|
+
import { addApiKeyAccount, startDeviceLogin, getDeviceLoginStatus, cancelDeviceLogin } from "../../services/codex-account-login.ts";
|
|
5
5
|
import { exportCodexEncrypted, importCodexEncrypted } from "../../services/codex-account-portability.ts";
|
|
6
6
|
|
|
7
7
|
/** Codex multi-account management. Mounted under /api/codex-accounts (auth-guarded). */
|
|
@@ -36,10 +36,14 @@ codexAccountsRoutes.post("/device-login", async (c) => {
|
|
|
36
36
|
catch (e) { return c.json(err((e as Error).message), 400); }
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
/** Poll a device login. Answers immediately: holding the connection open for the
|
|
40
|
+
* whole authorization made a dropped socket look like a failed login. */
|
|
41
|
+
codexAccountsRoutes.get("/device-login/:id/status", (c) => c.json(ok(getDeviceLoginStatus(c.req.param("id")))));
|
|
42
|
+
|
|
43
|
+
/** Abandon a device login the user closed out of. */
|
|
44
|
+
codexAccountsRoutes.delete("/device-login/:id", (c) => {
|
|
45
|
+
cancelDeviceLogin(c.req.param("id"));
|
|
46
|
+
return c.json(ok({ cancelled: true }));
|
|
43
47
|
});
|
|
44
48
|
|
|
45
49
|
/** Download a password-encrypted backup of codex accounts (auth.json + apiKey creds). */
|
package/src/server/ws/chat.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { providerRegistry } from "../../providers/registry.ts";
|
|
|
3
3
|
import { resolveProjectPath } from "../helpers/resolve-project.ts";
|
|
4
4
|
import { logSessionEvent } from "../../services/session-log.service.ts";
|
|
5
5
|
import { listSessions as sdkListSessions } from "@anthropic-ai/claude-agent-sdk";
|
|
6
|
-
import { getSessionTitle, incrementSessionUnread, clearSessionUnread, getSessionModel, setSessionModel, getSessionProvider, getSessionEffort, setSessionEffort, getSessionThinking, setSessionThinking } from "../../services/db.service.ts";
|
|
6
|
+
import { getSessionTitle, incrementSessionUnread, clearSessionUnread, getSessionModel, setSessionModel, getSessionProvider, getSessionEffort, setSessionEffort, getSessionThinking, setSessionThinking, setSessionMigratedTo } from "../../services/db.service.ts";
|
|
7
7
|
import { VALID_EFFORT_VALUES, THINKING_ADAPTIVE, isThinkingEnabled } from "../../providers/claude-agent-sdk-query-options.ts";
|
|
8
8
|
import type { ChatWsClientMessage, SessionPhase } from "../../types/api.ts";
|
|
9
9
|
// File watching and app-wide broadcasts are owned by the global WS (`./global.ts`)
|
|
@@ -397,6 +397,40 @@ async function detectImplicitTeam(sessionId: string): Promise<void> {
|
|
|
397
397
|
}
|
|
398
398
|
|
|
399
399
|
/** Transition session phase — guards same-phase, broadcasts phase_changed */
|
|
400
|
+
/**
|
|
401
|
+
* Rewrite a typed `/skill` to the sigil the session's provider recognises, in
|
|
402
|
+
* place on the parsed message.
|
|
403
|
+
*
|
|
404
|
+
* Only providers that own a skill runtime (`listSkills`) are affected, so a
|
|
405
|
+
* Claude session is left alone entirely. PPM's own built-ins are checked first
|
|
406
|
+
* and never rewritten: `/clear` and `/version` are handled by PPM regardless of
|
|
407
|
+
* which provider the tab is on, and must keep their slash to be intercepted.
|
|
408
|
+
*/
|
|
409
|
+
async function rewriteProviderSkillSigil(
|
|
410
|
+
parsed: { content: string },
|
|
411
|
+
providerId: string,
|
|
412
|
+
sessionId: string,
|
|
413
|
+
): Promise<void> {
|
|
414
|
+
const content = parsed.content.trimStart();
|
|
415
|
+
if (!content.startsWith("/")) return;
|
|
416
|
+
const provider = providerRegistry.get(providerId);
|
|
417
|
+
if (!provider?.listSkills) return;
|
|
418
|
+
|
|
419
|
+
const { isPpmHandled } = await import("../../services/slash-discovery/index.ts");
|
|
420
|
+
const name = content.match(/^\/(\S+)/)?.[1];
|
|
421
|
+
if (!name || isPpmHandled(name)) return;
|
|
422
|
+
|
|
423
|
+
try {
|
|
424
|
+
const { applySkillSigil } = await import("../../services/slash-discovery/provider-skill-sigil.ts");
|
|
425
|
+
const skills = await provider.listSkills(sessionId);
|
|
426
|
+
const rewritten = applySkillSigil(content, new Set(skills.map((s) => s.name)), "$");
|
|
427
|
+
if (rewritten !== content) parsed.content = rewritten;
|
|
428
|
+
} catch {
|
|
429
|
+
// Listing failed (app-server down, account not logged in). Send what the
|
|
430
|
+
// user typed rather than dropping their message.
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
400
434
|
function setPhase(sessionId: string, phase: SessionPhase, elapsed?: number): void {
|
|
401
435
|
const entry = activeSessions.get(sessionId);
|
|
402
436
|
if (!entry || entry.phase === phase) return;
|
|
@@ -485,7 +519,7 @@ function startCleanupTimer(sessionId: string): void {
|
|
|
485
519
|
* First message creates the query; follow-ups push into the provider's
|
|
486
520
|
* message channel. Events from ALL turns flow through this single loop.
|
|
487
521
|
*/
|
|
488
|
-
async function startSessionConsumer(sessionId: string, providerId: string, content: string, permissionMode?: string, images?: Array<{ data: string; mediaType: string }>, model?: string): Promise<void> {
|
|
522
|
+
async function startSessionConsumer(sessionId: string, providerId: string, content: string, permissionMode?: string, images?: Array<{ data: string; mediaType: string }>, model?: string, imagePaths?: string[]): Promise<void> {
|
|
489
523
|
const entry = activeSessions.get(sessionId);
|
|
490
524
|
if (!entry) {
|
|
491
525
|
console.error(`[chat] session=${sessionId} startSessionConsumer: no entry — aborting`);
|
|
@@ -549,7 +583,7 @@ async function startSessionConsumer(sessionId: string, providerId: string, conte
|
|
|
549
583
|
// provider config: omit so the provider falls back. thinking 0 = explicit OFF (overrides config).
|
|
550
584
|
const effortOverride = getSessionEffort(sessionId) ?? undefined;
|
|
551
585
|
const thinkingBudget = getSessionThinking(sessionId);
|
|
552
|
-
for await (const event of chatService.sendMessage(providerId, sessionId, content, { permissionMode, images, ...(model && { model }), ...(effortOverride && { effort: effortOverride }), ...(thinkingBudget != null && { thinkingBudget }) })) {
|
|
586
|
+
for await (const event of chatService.sendMessage(providerId, sessionId, content, { permissionMode, images, ...(imagePaths?.length && { imagePaths }), ...(model && { model }), ...(effortOverride && { effort: effortOverride }), ...(thinkingBudget != null && { thinkingBudget }) })) {
|
|
553
587
|
eventCount++;
|
|
554
588
|
const ev = event as any;
|
|
555
589
|
const evType = ev.type ?? "unknown";
|
|
@@ -802,6 +836,10 @@ async function startSessionConsumer(sessionId: string, providerId: string, conte
|
|
|
802
836
|
const newId = ev.newSessionId as string;
|
|
803
837
|
if (newId && newId !== sessionId) {
|
|
804
838
|
console.log(`[chat] session_migrated: ${sessionId} → ${newId}`);
|
|
839
|
+
// Persist the link before re-keying. A tab that opened under the old
|
|
840
|
+
// id keeps it in its own storage, so without this the conversation
|
|
841
|
+
// becomes unreachable from that tab the moment the turn ends.
|
|
842
|
+
setSessionMigratedTo(sessionId, newId);
|
|
805
843
|
// Stop spies tagged with old session ID before re-keying
|
|
806
844
|
bashOutputSpy.stopAllForSession(sessionId);
|
|
807
845
|
nestedSubagentSpy.stopAllForSession(sessionId);
|
|
@@ -1083,7 +1121,10 @@ export const chatWebSocket = {
|
|
|
1083
1121
|
}
|
|
1084
1122
|
|
|
1085
1123
|
if (parsed.type === "message") {
|
|
1086
|
-
|
|
1124
|
+
// Images count as content: a message may carry only a picture, with nothing typed.
|
|
1125
|
+
const hasInlineImages = Array.isArray((parsed as { images?: unknown }).images)
|
|
1126
|
+
&& ((parsed as { images: unknown[] }).images.length > 0);
|
|
1127
|
+
if (typeof parsed.content !== "string" || (!parsed.content.trim() && !hasInlineImages)) {
|
|
1087
1128
|
ws.send(JSON.stringify({ type: "error", message: "Message content is required" }));
|
|
1088
1129
|
return;
|
|
1089
1130
|
}
|
|
@@ -1135,6 +1176,13 @@ export const chatWebSocket = {
|
|
|
1135
1176
|
if (canonical !== typedContent) parsed.content = canonical;
|
|
1136
1177
|
}
|
|
1137
1178
|
|
|
1179
|
+
// Providers with their own skill runtime may not use a leading slash.
|
|
1180
|
+
// Codex resolves a skill from a `$name` mention in the prompt; sent as
|
|
1181
|
+
// `/imagegen` it is inert prose, so the picked skill would silently not
|
|
1182
|
+
// run. Rewritten before the echo for the same reason as the alias above:
|
|
1183
|
+
// other devices and the stored transcript must show what actually ran.
|
|
1184
|
+
await rewriteProviderSkillSigil(parsed, providerId, sessionId);
|
|
1185
|
+
|
|
1138
1186
|
// Echo the user message to OTHER connected clients (second device/tab).
|
|
1139
1187
|
// The sender renders it optimistically; without this echo a live-connected
|
|
1140
1188
|
// second device only sees the assistant stream for this turn.
|
|
@@ -1213,9 +1261,10 @@ export const chatWebSocket = {
|
|
|
1213
1261
|
const permMode = entry.permissionMode;
|
|
1214
1262
|
const msgModel = entry.model;
|
|
1215
1263
|
const msgImages = parsed.type === "message" ? parsed.images : undefined;
|
|
1264
|
+
const msgImagePaths = parsed.type === "message" ? parsed.imagePaths : undefined;
|
|
1216
1265
|
entry.streamPromise = new Promise<void>((resolve) => {
|
|
1217
1266
|
setTimeout(() => {
|
|
1218
|
-
startSessionConsumer(sessionId, providerId, parsed.content, permMode, msgImages, msgModel).then(resolve, resolve);
|
|
1267
|
+
startSessionConsumer(sessionId, providerId, parsed.content, permMode, msgImages, msgModel, msgImagePaths).then(resolve, resolve);
|
|
1219
1268
|
}, 0);
|
|
1220
1269
|
});
|
|
1221
1270
|
} else {
|
|
@@ -1224,6 +1273,7 @@ export const chatWebSocket = {
|
|
|
1224
1273
|
(provider as any).pushMessage(sessionId, parsed.content, {
|
|
1225
1274
|
priority: parsed.priority ?? 'next',
|
|
1226
1275
|
images: parsed.images,
|
|
1276
|
+
imagePaths: parsed.imagePaths,
|
|
1227
1277
|
});
|
|
1228
1278
|
}
|
|
1229
1279
|
// Clear turn events for new turn display + transition phase
|