@qwen-code/qwen-code 0.19.3-nightly.20260701.a974594d7 → 0.19.4
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/bundled/loop/loop-tick-resolver.test.ts +42 -0
- package/bundled/loop/loop-tick-resolver.ts +1 -1
- package/bundled/qc-helper/docs/configuration/auth.md +1 -1
- package/bundled/qc-helper/docs/configuration/settings.md +23 -11
- package/bundled/qc-helper/docs/features/approval-mode.md +3 -0
- package/bundled/qc-helper/docs/features/auto-mode.md +40 -0
- package/bundled/qc-helper/docs/features/channels/overview.md +81 -18
- package/bundled/qc-helper/docs/features/code-review.md +17 -61
- package/bundled/qc-helper/docs/features/commands.md +6 -6
- package/bundled/qc-helper/docs/features/mcp.md +3 -1
- package/bundled/qc-helper/docs/features/scheduled-tasks.md +10 -0
- package/bundled/qc-helper/docs/qwen-serve.md +19 -0
- package/bundled/review/DESIGN.md +21 -26
- package/bundled/review/SKILL.md +45 -129
- package/chunks/{MaxSizedBox-UZHJ73JB.js → MaxSizedBox-ERTNFI22.js} +20 -18
- package/chunks/{StandaloneSessionPicker-GGUIZWCE.js → StandaloneSessionPicker-Y5NGRLRY.js} +32 -29
- package/chunks/{acpAgent-QYZFICEZ.js → acpAgent-UMN6OYTR.js} +473 -187
- package/chunks/{agent-2EN7YNSW.js → agent-56DZU4ZU.js} +17 -15
- package/chunks/{agent-headless-SLTEZ5LV.js → agent-headless-SR7N2IJG.js} +17 -15
- package/chunks/{anthropicContentGenerator-CA3JTAMO.js → anthropicContentGenerator-5OZFO6WQ.js} +1 -1
- package/chunks/{artifact-tool-RM7WINJO.js → artifact-tool-JVZMHA4B.js} +1 -1
- package/chunks/{askUserQuestion-J3P6QAJ7.js → askUserQuestion-CMJZ2F3D.js} +1 -1
- package/chunks/{bridge-HZVAUTMX.js → bridge-AUHNFOUS.js} +23 -21
- package/chunks/channel-worker-supervisor-A74FV522.js +308 -0
- package/chunks/{chunk-N4V4VBP6.js → chunk-2X2JUE47.js} +2 -2
- package/chunks/{chunk-JV7A66DR.js → chunk-4VZH35PN.js} +528 -358
- package/chunks/{chunk-M2U5NM24.js → chunk-54YZOP7P.js} +309 -72
- package/chunks/{chunk-WGY3GIPP.js → chunk-6AQR7732.js} +1 -1
- package/chunks/{chunk-FR3II765.js → chunk-6CIIP6OE.js} +4 -4
- package/chunks/{chunk-IKO44NAT.js → chunk-6TLY5W3B.js} +2 -2
- package/chunks/{chunk-VN4CKYUW.js → chunk-73H73ASU.js} +1 -1
- package/chunks/{chunk-IBEM64M4.js → chunk-75X2VLSH.js} +36 -25
- package/chunks/{chunk-PLQTLKMR.js → chunk-7KPI4LAI.js} +3 -3
- package/chunks/{chunk-NVTRORSK.js → chunk-7VPC7D7O.js} +2 -2
- package/chunks/chunk-AHNXYU4O.js +24 -0
- package/chunks/{chunk-MTJVHQDP.js → chunk-B7KGXIPZ.js} +302 -257
- package/chunks/{chunk-35FH347N.js → chunk-BFG6OZN7.js} +16097 -16236
- package/chunks/{chunk-T44AF4I6.js → chunk-BNJLLAQ7.js} +1 -1
- package/chunks/{chunk-OEY3YAL2.js → chunk-BQNJBEUA.js} +2 -2
- package/chunks/{chunk-Q7TDHJ2G.js → chunk-C2IHHGBZ.js} +10 -10
- package/chunks/chunk-CG7SVARK.js +367 -0
- package/chunks/{chunk-7SP275SH.js → chunk-CS5XYDBO.js} +5 -5
- package/chunks/{chunk-V5YJAUHW.js → chunk-CUGEBAKG.js} +1 -1
- package/chunks/{chunk-FQDMX5IL.js → chunk-D5KISTLW.js} +309 -72
- package/chunks/{chunk-I4MXURND.js → chunk-DQ74W65K.js} +356 -468
- package/chunks/chunk-E5A7LHNN.js +51 -0
- package/chunks/{chunk-3PJXIDKI.js → chunk-E6US47KI.js} +3 -27
- package/chunks/chunk-EDAGQADF.js +21 -0
- package/chunks/{chunk-HUGAEYZR.js → chunk-FNLXLI4I.js} +2 -2
- package/chunks/{chunk-WJAY4WXR.js → chunk-FYKX6XOT.js} +1 -1
- package/chunks/{chunk-CA4ML3VH.js → chunk-G7OQ3RFJ.js} +1 -1
- package/chunks/{chunk-VHRREANT.js → chunk-GICCHL2Q.js} +3 -1
- package/chunks/{chunk-QHHNFG22.js → chunk-GLPV75ES.js} +1 -1
- package/chunks/{chunk-LD2XBG6Z.js → chunk-GO6LNQXT.js} +1 -39
- package/chunks/chunk-HI55Z6PR.js +3523 -0
- package/chunks/{chunk-B5MPZI4K.js → chunk-HJDF3ZVU.js} +2670 -1233
- package/chunks/{chunk-GDIG7TMG.js → chunk-HRZB5CSC.js} +1 -1
- package/chunks/{chunk-LDWTYE33.js → chunk-J3F7X3LW.js} +1 -1
- package/chunks/{chunk-FV4SQ54H.js → chunk-KVPFPC4J.js} +227 -85
- package/chunks/{chunk-Q3JOGB4U.js → chunk-L7QPM2YD.js} +1 -1
- package/chunks/{chunk-4722VFFU.js → chunk-L7VH7HQA.js} +6 -6
- package/chunks/{chunk-HWG4XLD7.js → chunk-LKWAVAB5.js} +1 -1
- package/chunks/{chunk-YCRTJYSQ.js → chunk-MEQWZGK5.js} +1 -1
- package/chunks/{chunk-6FIWYVN4.js → chunk-MKHC4BYZ.js} +30 -5
- package/chunks/{chunk-YCEHOJZJ.js → chunk-MNDTD5E5.js} +65 -38
- package/chunks/{chunk-ZGSV5ORL.js → chunk-MVZXZWGT.js} +809 -759
- package/chunks/chunk-MX2YXRER.js +44 -0
- package/chunks/{chunk-TMILTLP3.js → chunk-ND25GXEU.js} +21 -4
- package/chunks/{chunk-AIBPG5MB.js → chunk-OAFUMTWW.js} +2 -2
- package/chunks/chunk-OJCECIJA.js +238 -0
- package/chunks/{chunk-EUURTJY7.js → chunk-PIYV6COW.js} +1 -1
- package/chunks/{chunk-LXEW6BHF.js → chunk-Q7VHIRDD.js} +1 -1
- package/chunks/{chunk-DBR7PF4B.js → chunk-QAFQLTXG.js} +1 -1
- package/chunks/{chunk-3TK7FSCP.js → chunk-QMIND2AS.js} +3 -3
- package/chunks/{chunk-ESVSWG6Z.js → chunk-QOMAHFYI.js} +1 -1
- package/chunks/{chunk-J37FGIOA.js → chunk-QXPI7FEC.js} +38 -4
- package/chunks/chunk-RM3PGSGL.js +66 -0
- package/chunks/chunk-RNBYOUGV.js +42 -0
- package/chunks/chunk-S5EQPDTM.js +91 -0
- package/chunks/{chunk-GZKQ36JS.js → chunk-SFN4TEHY.js} +4 -4
- package/chunks/{chunk-DHXWNSJA.js → chunk-T22H4V4R.js} +42 -0
- package/chunks/{chunk-LXGWLOCM.js → chunk-TENOUWY2.js} +1 -1
- package/chunks/{chunk-BLAE2TCO.js → chunk-TTFNVEO5.js} +3 -3
- package/chunks/{chunk-FZY7NH37.js → chunk-UANCYOOQ.js} +1 -1
- package/chunks/{chunk-E5I6YSNO.js → chunk-UQFP2FEF.js} +1 -1
- package/chunks/{chunk-3O2BJMFW.js → chunk-UQYOMM6J.js} +4 -4
- package/chunks/{chunk-JERW36RG.js → chunk-UT5JU5DJ.js} +15 -15
- package/chunks/{chunk-DJ25MIU2.js → chunk-VKGG724L.js} +8 -8
- package/chunks/{chunk-G525INVW.js → chunk-W2ZOAX7F.js} +3 -3
- package/chunks/{chunk-HIYUAXBG.js → chunk-W6X4FGWX.js} +1 -1
- package/chunks/{chunk-OE5J6UA7.js → chunk-XCUFHOAD.js} +1 -1
- package/chunks/{chunk-KXVVXAAC.js → chunk-YBDZ63KI.js} +3 -3
- package/chunks/{chunk-Z34RYLRO.js → chunk-ZJP5T7IA.js} +1 -1
- package/chunks/{chunk-NOYV2NXI.js → chunk-ZXCVPMIK.js} +411 -43
- package/chunks/{computer-use-FCNWHVPW.js → computer-use-VWN5HH6A.js} +20 -18
- package/chunks/{contextCommand-FVFMZZEU.js → contextCommand-DFFG5KDJ.js} +19 -17
- package/chunks/{cron-create-IY5NMIHQ.js → cron-create-CSSWJ67I.js} +1 -1
- package/chunks/{cron-delete-OLNEQWKO.js → cron-delete-E56CFQ7A.js} +1 -1
- package/chunks/{cron-list-YAUAUV24.js → cron-list-BKSINY6Y.js} +1 -1
- package/chunks/daemon-5T6IHWJV.js +8430 -0
- package/chunks/{daemon-status-provider-42JEXENZ.js → daemon-status-provider-V5UY7O7J.js} +26 -24
- package/chunks/{dist-4FCNM6KJ.js → dist-2HDL5SHQ.js} +2 -2
- package/chunks/{dist-SU6L7ZNJ.js → dist-B7SSDVWT.js} +3 -3
- package/chunks/{dist-VD7HXTML.js → dist-BL3SL665.js} +1 -1
- package/chunks/{dist-UKPWQJCL.js → dist-TAVAGZU6.js} +37 -13
- package/chunks/{dist-K4XZILIO.js → dist-XBOVZ3R5.js} +26 -5
- package/chunks/{earlyInputCapture-FFFEC6P4.js → earlyInputCapture-VVYU4FDO.js} +17 -15
- package/chunks/{edit-32ILSGZY.js → edit-F35PTMBA.js} +17 -15
- package/chunks/{en-U3LWGSFX.js → en-FZREZFPO.js} +18 -0
- package/chunks/{enter-worktree-XWZKIN6A.js → enter-worktree-3DIW7EGJ.js} +17 -15
- package/chunks/{enterPlanMode-LPFFY7BJ.js → enterPlanMode-QNXT77KP.js} +27 -15
- package/chunks/{errors-UXBPYPI2.js → errors-M53YG76R.js} +19 -17
- package/chunks/{exit-worktree-IBS4UIU7.js → exit-worktree-BSDWNWHZ.js} +17 -15
- package/chunks/{exitPlanMode-JA72YHII.js → exitPlanMode-EL7AY327.js} +41 -23
- package/chunks/{fast-path-7UU67Y2W.js → fast-path-DUCSMUFW.js} +2 -2
- package/chunks/{gemini-GMVKJNMX.js → gemini-F3IERB37.js} +59 -51
- package/chunks/{geminiContentGenerator-7NIFF2L4.js → geminiContentGenerator-6OULAKFQ.js} +1 -1
- package/chunks/{glob-4NJCW5HI.js → glob-ZMLFTD4T.js} +17 -15
- package/chunks/{grep-PAIMLQEL.js → grep-H5X66EI7.js} +17 -15
- package/chunks/{initializer-R6KU4VYM.js → initializer-LEOT2OLH.js} +21 -19
- package/chunks/{list-H7LXAO64.js → list-M3DKXML6.js} +23 -21
- package/chunks/{loadedSettingsAdapter-H27JMTLU.js → loadedSettingsAdapter-OKMQFGGG.js} +21 -19
- package/chunks/{loop-wakeup-Y3OIRQFM.js → loop-wakeup-FPBAVVPR.js} +2 -2
- package/chunks/{ls-TVJEYS7O.js → ls-UGWQXPTY.js} +1 -1
- package/chunks/{lsp-JZSDVR3F.js → lsp-24WV7NXB.js} +1 -1
- package/chunks/{monitor-JSIHDPXP.js → monitor-ARL5P45R.js} +19 -16
- package/chunks/{nonInteractiveCli-Y4CHOETG.js → nonInteractiveCli-NUVLF3YR.js} +53 -45
- package/chunks/{notebook-edit-SHRNL2UM.js → notebook-edit-ANTZ2DDY.js} +17 -15
- package/chunks/{openaiContentGenerator-ORZZ7VFO.js → openaiContentGenerator-TFGMKGHN.js} +6 -6
- package/chunks/pidfile-AWTL4C63.js +72 -0
- package/chunks/{qwenContentGenerator-H643PL2G.js → qwenContentGenerator-6QWAYTWS.js} +18 -16
- package/chunks/{qwenOAuth2-OI2B7PES.js → qwenOAuth2-LWLA6MQG.js} +3 -3
- package/chunks/{read-file-5PRNPTTY.js → read-file-EJY73XQ3.js} +4 -4
- package/chunks/{read-mcp-resource-QTXGKTEV.js → read-mcp-resource-NCBTPP7X.js} +1 -1
- package/chunks/{ripGrep-JIKZMABJ.js → ripGrep-HC4SA6CQ.js} +17 -15
- package/chunks/{run-qwen-serve-PQV7QOHZ.js → run-qwen-serve-AGLZ642J.js} +291 -55
- package/chunks/{scheduler-OS3R5MGU.js → scheduler-TURO7NGY.js} +17 -15
- package/chunks/{send-message-DKGHWSTF.js → send-message-3S2NYS3O.js} +5 -3
- package/chunks/{serve-LVYZDKCZ.js → serve-5DAMOIDW.js} +22 -19
- package/chunks/{server-P4YIH35I.js → server-4AGEK2HC.js} +2268 -1222
- package/chunks/{session-DE4TZBKH.js → session-7ZOQUC4Y.js} +54 -46
- package/chunks/{settings-DFTYMJYF.js → settings-DR4V6L5Y.js} +20 -18
- package/chunks/{shell-3GNQLNV7.js → shell-H5WFKUXH.js} +17 -15
- package/chunks/{skill-NCNG4KKZ.js → skill-ANHU5ZCR.js} +4 -4
- package/chunks/{spawnChannel-DHRIMDVL.js → spawnChannel-VN5LJV7Z.js} +19 -17
- package/chunks/{src-PNZYGM3R.js → src-OVZLTVVM.js} +49 -17
- package/chunks/{startInteractiveUI-P6GZBFQW.js → startInteractiveUI-OA22V32P.js} +11110 -10670
- package/chunks/{syntheticOutput-X7L5HWLN.js → syntheticOutput-SV7GBHZ5.js} +2 -2
- package/chunks/{task-create-AWEUI4GI.js → task-create-W2H2UJCH.js} +6 -5
- package/chunks/{task-list-KZ3VJLDU.js → task-list-THBOPV4G.js} +5 -4
- package/chunks/{task-stop-GUNOZKJ5.js → task-stop-33FVCNCJ.js} +1 -1
- package/chunks/{task-update-Y56GP6DU.js → task-update-LVFQLXEI.js} +6 -5
- package/chunks/{team-create-6JMW3KQK.js → team-create-OYL5CAQO.js} +17 -15
- package/chunks/{team-delete-6AFRGDJY.js → team-delete-NTUQ25UY.js} +5 -4
- package/chunks/{theme-manager-FEHQO22G.js → theme-manager-XCMPF2SI.js} +17 -15
- package/chunks/{todoWrite-JRS6NOEU.js → todoWrite-SEPO2RI3.js} +1 -1
- package/chunks/{tool-search-IXAEKS7J.js → tool-search-YXW55X6Z.js} +29 -5
- package/chunks/{trustedFolders-SJRXNHZE.js → trustedFolders-TEPGM5YL.js} +18 -16
- package/chunks/{validateNonInterActiveAuth-HCH7OBDH.js → validateNonInterActiveAuth-OQIS7WUE.js} +50 -42
- package/chunks/{web-fetch-RELNL37A.js → web-fetch-OE7LHURA.js} +4 -4
- package/chunks/{workflow-M2COBI7R.js → workflow-RTUWORAP.js} +48 -27
- package/chunks/{workspace-providers-status-GOCWYDMK.js → workspace-providers-status-DM2XANVB.js} +23 -21
- package/chunks/{workspace-service-34C7SUBU.js → workspace-service-VKBI2KZD.js} +27 -25
- package/chunks/{write-file-E52WRXZL.js → write-file-WGALQ53L.js} +17 -15
- package/chunks/{zh-TW-LKLBTZHE.js → zh-TW-6GIKXEAN.js} +18 -0
- package/chunks/{zh-F5T4WWZO.js → zh-V3B4TFWL.js} +18 -0
- package/cli.js +6 -6
- package/locales/en.js +30 -0
- package/locales/fr.js +1 -2
- package/locales/ja.js +1 -2
- package/locales/zh-TW.js +29 -0
- package/locales/zh.js +29 -0
- package/node_modules/@qwen-code/audio-capture/package.json +1 -1
- package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-arm64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-x64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/win32-x64/@qwen-code+audio-capture.node +0 -0
- package/package.json +3 -3
- package/web-shell/assets/{arc-C2DILvUl.js → arc-Det5Ccum.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-b8gZ2cNw.js → architectureDiagram-3BPJPVTR-DqNWVFpl.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-BuRpghMj.js → blockDiagram-GPEHLZMM-CYl7tYBi.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-2ItdhFmE.js → c4Diagram-AAUBKEIU-DaAEi0NQ.js} +1 -1
- package/web-shell/assets/channel-Bm0bFauD.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-B1QXa0q3.js → chunk-2J33WTMH-Df2CDEE2.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-DXfhKlNq.js → chunk-4BX2VUAB-DNBFcyL2.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-ClSNjxLo.js → chunk-55IACEB6-BKjF-0HI.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-Dsh2wisB.js → chunk-727SXJPM-DYwAwfIM.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-C1TkY5AR.js → chunk-AQP2D5EJ-C6FswLqy.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-CGtN9XR6.js → chunk-FMBD7UC4-DRWAcZSY.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-BHiPi1SS.js → chunk-ND2GUHAM-AoL16mNR.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-CzG3I5o1.js → chunk-QZHKN3VN-a8PtLMck.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK--NgUfb55.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2--NgUfb55.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-EgrIoH_K.js → cose-bilkent-S5V4N54A-C4Aeu5qf.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-BUnpdX8S.js → dagre-BM42HDAG-BOulamjo.js} +1 -1
- package/web-shell/assets/{diagram-2AECGRRQ-BlLUCVYu.js → diagram-2AECGRRQ-D1wt2ykO.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-FG-enmjQ.js → diagram-5GNKFQAL-BRseus-O.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-BlzmTONk.js → diagram-KO2AKTUF-XSMkNzpZ.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-CoBQ8mIx.js → diagram-LMA3HP47-21KNLqoi.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-DvxV6sX8.js → diagram-OG6HWLK6-DnUI8F5G.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-Jq1tpAoj.js → erDiagram-TEJ5UH35-BVSOMuM2.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-CbtYOry5.js → flowDiagram-I6XJVG4X-fYtDPJsW.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-ZEn2VEKR.js → ganttDiagram-6RSMTGT7-CmRXosW_.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-BZh-LkhJ.js → gitGraphDiagram-PVQCEYII-DPrmySxO.js} +1 -1
- package/web-shell/assets/index-BpKnmyFV.js +717 -0
- package/web-shell/assets/index-fLrtxpOG.css +5 -0
- package/web-shell/assets/{infoDiagram-5YYISTIA-B9HuZygK.js → infoDiagram-5YYISTIA-x3VbEuCV.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-D76cNpYG.js → ishikawaDiagram-YF4QCWOH-SmitHRMC.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-6bGo7P1t.js → journeyDiagram-JHISSGLW-BxvA551S.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-DuYyRBvB.js → kanban-definition-UN3LZRKU-DNHC16jY.js} +1 -1
- package/web-shell/assets/{linear-CpdSW1BC.js → linear-CQ6irBL9.js} +1 -1
- package/web-shell/assets/{mermaid.core-s5xbZMMj.js → mermaid.core-DtB6NO1e.js} +72 -72
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-go01fZ2y.js → mindmap-definition-RKZ34NQL-QKMAgV2v.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-BfuZiEHO.js → pieDiagram-4H26LBE5-bvb6dGn6.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-DsoINqV6.js → quadrantDiagram-W4KKPZXB-DLgdlG4D.js} +1 -1
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-B7FU4LiU.js → requirementDiagram-4Y6WPE33-2M9YH4GF.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-BEDjFnGB.js → sankeyDiagram-5OEKKPKP-DzwfclTH.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-gzmmL5tK.js → sequenceDiagram-3UESZ5HK-BvPmTuy2.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-BtHD4IN_.js → stateDiagram-AJRCARHV-BjwUmrJF.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-C4L8zXeL.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-T_8gmMCf.js → timeline-definition-PNZ67QCA-CXzS6qqz.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-DdjuqnjO.js → vennDiagram-CIIHVFJN-yRSolF5B.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-Cgtrvb37.js → wardley-L42UT6IY-ClLJjHPG.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-BW3f3-yW.js → wardleyDiagram-YWT4CUSO-VIa6t2p2.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-BUS9IKkc.js → xychartDiagram-2RQKCTM6-CtmrkezW.js} +1 -1
- package/web-shell/index.html +18 -2
- package/chunks/chunk-2JKEQWDZ.js +0 -1596
- package/chunks/chunk-32MCHZQC.js +0 -73
- package/chunks/chunk-UAKXW3DN.js +0 -30
- package/web-shell/assets/channel-eRvn2UA3.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-YZNwy1rx.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-YZNwy1rx.js +0 -1
- package/web-shell/assets/index-5FhE0ORO.css +0 -5
- package/web-shell/assets/index-CDTGmqVm.js +0 -720
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-DFy6gLDd.js +0 -1
|
@@ -1,34 +1,93 @@
|
|
|
1
1
|
// Force strict mode and setup for ESM
|
|
2
2
|
"use strict";
|
|
3
3
|
import {
|
|
4
|
+
MAX_REMEMBER_CONTENT_BYTES,
|
|
5
|
+
extractRememberErrorCode,
|
|
4
6
|
isValidServerName
|
|
5
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-RM3PGSGL.js";
|
|
6
8
|
import {
|
|
7
9
|
openQwenAsrRealtimeStream,
|
|
8
10
|
openVoiceStream,
|
|
9
11
|
openVoiceStreamWithRetry
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-CUGEBAKG.js";
|
|
11
13
|
import {
|
|
12
14
|
SetupGithubError,
|
|
13
15
|
setupGithub
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-L7QPM2YD.js";
|
|
17
|
+
import "./chunk-54YZOP7P.js";
|
|
18
|
+
import "./chunk-HRZB5CSC.js";
|
|
19
|
+
import {
|
|
20
|
+
CAPABILITIES_SCHEMA_VERSION,
|
|
21
|
+
CLIENT_ID_HEADER,
|
|
22
|
+
CLIENT_ID_RE,
|
|
23
|
+
ClientMcpSenderRegistry,
|
|
24
|
+
DeviceFlowRegistry,
|
|
25
|
+
MAX_CLIENT_ID_LENGTH,
|
|
26
|
+
MAX_SERVER_NAME_LENGTH,
|
|
27
|
+
MAX_TOOL_NAME_LENGTH,
|
|
28
|
+
TooManyActiveDeviceFlowsError,
|
|
29
|
+
UnsupportedDeviceFlowProviderError,
|
|
30
|
+
UpstreamDeviceFlowError,
|
|
31
|
+
allowOriginCors,
|
|
32
|
+
bearerAuth,
|
|
33
|
+
brandSecret,
|
|
34
|
+
buildDaemonStatusResponse,
|
|
35
|
+
createBridgeFileSystemAdapter,
|
|
36
|
+
createBuildWorkspaceCtx,
|
|
37
|
+
createClientMcpServerProvider,
|
|
38
|
+
createMutationGate,
|
|
39
|
+
createRateLimiter,
|
|
40
|
+
denyBrowserOriginCors,
|
|
41
|
+
detectFromLoopback,
|
|
42
|
+
getAdvertisedServeFeatures,
|
|
43
|
+
getServeProtocolVersions,
|
|
44
|
+
hostAllowlist,
|
|
45
|
+
parseAllowOriginPatterns,
|
|
46
|
+
parseAndValidateWorkspaceClientId,
|
|
47
|
+
parseClientIdHeader,
|
|
48
|
+
parseDaemonStatusDetail,
|
|
49
|
+
parseLastEventId,
|
|
50
|
+
parseMaxQueuedQuery,
|
|
51
|
+
parseOptionalWorkspaceCwd,
|
|
52
|
+
parsePermissionVoteBody,
|
|
53
|
+
requireSessionId,
|
|
54
|
+
require_express,
|
|
55
|
+
safeBody,
|
|
56
|
+
safeLogValue,
|
|
57
|
+
sanitizeForStderr,
|
|
58
|
+
sendJsonBodyParserError,
|
|
59
|
+
setDeviceFlowRegistry,
|
|
60
|
+
setRateLimiter,
|
|
61
|
+
unsafeRevealSecret,
|
|
62
|
+
validateMcpRuntimeServerName
|
|
63
|
+
} from "./chunk-B7KGXIPZ.js";
|
|
64
|
+
import {
|
|
65
|
+
isLoopbackBind
|
|
66
|
+
} from "./chunk-RNBYOUGV.js";
|
|
67
|
+
import "./chunk-3HX5LZ6R.js";
|
|
68
|
+
import "./chunk-7RYW5LQV.js";
|
|
17
69
|
import {
|
|
18
70
|
createDaemonStatusProvider
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import "./chunk-
|
|
71
|
+
} from "./chunk-6CIIP6OE.js";
|
|
72
|
+
import "./chunk-W2ZOAX7F.js";
|
|
21
73
|
import "./chunk-KHDZHZMH.js";
|
|
74
|
+
import "./chunk-W6X4FGWX.js";
|
|
22
75
|
import {
|
|
23
76
|
createAcpSessionBridge
|
|
24
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-KVPFPC4J.js";
|
|
78
|
+
import "./chunk-EWALXUAD.js";
|
|
79
|
+
import {
|
|
80
|
+
EVENT_SCHEMA_VERSION,
|
|
81
|
+
SubscriberLimitExceededError
|
|
82
|
+
} from "./chunk-ALX6BUYW.js";
|
|
83
|
+
import "./chunk-YBDZ63KI.js";
|
|
25
84
|
import {
|
|
26
85
|
createWorkspaceProvidersStatusProvider
|
|
27
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-L7VH7HQA.js";
|
|
28
87
|
import {
|
|
29
88
|
getAuthTypeFromEnv,
|
|
30
89
|
resolveCliGenerationConfig
|
|
31
|
-
} from "./chunk-
|
|
90
|
+
} from "./chunk-BQNJBEUA.js";
|
|
32
91
|
import {
|
|
33
92
|
EMPTY_WORKSPACE_VOICE_UPDATE_ERROR,
|
|
34
93
|
WorkspaceVoiceError,
|
|
@@ -39,14 +98,14 @@ import {
|
|
|
39
98
|
transcribeWorkspaceVoiceAudio,
|
|
40
99
|
validateWorkspaceVoiceState,
|
|
41
100
|
voiceSettingsScopeToWire
|
|
42
|
-
} from "./chunk-
|
|
101
|
+
} from "./chunk-UT5JU5DJ.js";
|
|
43
102
|
import {
|
|
44
103
|
PermissionRulesValidationError,
|
|
45
104
|
buildPermissionSettings,
|
|
46
105
|
isPermissionRuleType,
|
|
47
106
|
normalizePermissionRules,
|
|
48
107
|
readPermissionRuleSet
|
|
49
|
-
} from "./chunk-
|
|
108
|
+
} from "./chunk-QAFQLTXG.js";
|
|
50
109
|
import {
|
|
51
110
|
assertVoiceBaseUrlNetworkAllowed,
|
|
52
111
|
isStreamingVoiceModel,
|
|
@@ -54,12 +113,12 @@ import {
|
|
|
54
113
|
resolveVoiceTranscriptionConfig,
|
|
55
114
|
sanitizeVoiceErrorMessage,
|
|
56
115
|
transcribeVoiceAudio
|
|
57
|
-
} from "./chunk-
|
|
116
|
+
} from "./chunk-BNJLLAQ7.js";
|
|
58
117
|
import {
|
|
59
118
|
isVoiceEnabled,
|
|
60
119
|
isVoiceMode,
|
|
61
120
|
readVoiceModel
|
|
62
|
-
} from "./chunk-
|
|
121
|
+
} from "./chunk-Q7VHIRDD.js";
|
|
63
122
|
import "./chunk-NIOPRZNN.js";
|
|
64
123
|
import "./chunk-P7M4GA57.js";
|
|
65
124
|
import {
|
|
@@ -77,7 +136,10 @@ import {
|
|
|
77
136
|
PermissionPolicyNotImplementedError,
|
|
78
137
|
PromptQueueFullError,
|
|
79
138
|
RestoreInProgressError,
|
|
139
|
+
SessionArchivedError,
|
|
140
|
+
SessionArchivingError,
|
|
80
141
|
SessionBusyError,
|
|
142
|
+
SessionConflictError,
|
|
81
143
|
SessionLimitExceededError,
|
|
82
144
|
SessionNotFoundError,
|
|
83
145
|
SessionShellClientRequiredError,
|
|
@@ -87,91 +149,36 @@ import {
|
|
|
87
149
|
WorkspaceInitRaceError,
|
|
88
150
|
WorkspaceInitSymlinkError,
|
|
89
151
|
WorkspaceMismatchError
|
|
90
|
-
} from "./chunk-
|
|
91
|
-
import {
|
|
92
|
-
WorkspacePermissionRulesSessionRequiredError,
|
|
93
|
-
WorkspaceSettingsPartialPersistError
|
|
94
|
-
} from "./chunk-BMFMJINR.js";
|
|
95
|
-
import {
|
|
96
|
-
CAPABILITIES_SCHEMA_VERSION,
|
|
97
|
-
CLIENT_ID_HEADER,
|
|
98
|
-
CLIENT_ID_RE,
|
|
99
|
-
ClientMcpSenderRegistry,
|
|
100
|
-
DeviceFlowRegistry,
|
|
101
|
-
MAX_CLIENT_ID_LENGTH,
|
|
102
|
-
MAX_SERVER_NAME_LENGTH,
|
|
103
|
-
MAX_TOOL_NAME_LENGTH,
|
|
104
|
-
TooManyActiveDeviceFlowsError,
|
|
105
|
-
UnsupportedDeviceFlowProviderError,
|
|
106
|
-
UpstreamDeviceFlowError,
|
|
107
|
-
allowOriginCors,
|
|
108
|
-
bearerAuth,
|
|
109
|
-
brandSecret,
|
|
110
|
-
buildDaemonStatusResponse,
|
|
111
|
-
createBridgeFileSystemAdapter,
|
|
112
|
-
createBuildWorkspaceCtx,
|
|
113
|
-
createClientMcpServerProvider,
|
|
114
|
-
createMutationGate,
|
|
115
|
-
createRateLimiter,
|
|
116
|
-
denyBrowserOriginCors,
|
|
117
|
-
detectFromLoopback,
|
|
118
|
-
getAdvertisedServeFeatures,
|
|
119
|
-
getServeProtocolVersions,
|
|
120
|
-
hostAllowlist,
|
|
121
|
-
isLoopbackBind,
|
|
122
|
-
parseAllowOriginPatterns,
|
|
123
|
-
parseAndValidateWorkspaceClientId,
|
|
124
|
-
parseClientIdHeader,
|
|
125
|
-
parseDaemonStatusDetail,
|
|
126
|
-
parseLastEventId,
|
|
127
|
-
parseMaxQueuedQuery,
|
|
128
|
-
parseOptionalWorkspaceCwd,
|
|
129
|
-
parsePermissionVoteBody,
|
|
130
|
-
requireSessionId,
|
|
131
|
-
require_express,
|
|
132
|
-
safeBody,
|
|
133
|
-
safeLogValue,
|
|
134
|
-
sanitizeForStderr,
|
|
135
|
-
sendJsonBodyParserError,
|
|
136
|
-
setDeviceFlowRegistry,
|
|
137
|
-
setRateLimiter,
|
|
138
|
-
unsafeRevealSecret,
|
|
139
|
-
validateMcpRuntimeServerName
|
|
140
|
-
} from "./chunk-MTJVHQDP.js";
|
|
141
|
-
import "./chunk-3HX5LZ6R.js";
|
|
142
|
-
import "./chunk-7RYW5LQV.js";
|
|
143
|
-
import "./chunk-HIYUAXBG.js";
|
|
144
|
-
import "./chunk-EWALXUAD.js";
|
|
152
|
+
} from "./chunk-T22H4V4R.js";
|
|
145
153
|
import {
|
|
146
154
|
MAX_WORKSPACE_PATH_LENGTH,
|
|
147
155
|
canonicalizeWorkspace
|
|
148
156
|
} from "./chunk-SEZC2725.js";
|
|
149
157
|
import {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
158
|
+
STATUS_SCHEMA_VERSION,
|
|
159
|
+
createIdleWorkspaceMemoryStatus,
|
|
160
|
+
mapDomainErrorToErrorKind
|
|
161
|
+
} from "./chunk-GICCHL2Q.js";
|
|
162
|
+
import {
|
|
163
|
+
WorkspacePermissionRulesSessionRequiredError,
|
|
164
|
+
WorkspaceSettingsPartialPersistError
|
|
165
|
+
} from "./chunk-BMFMJINR.js";
|
|
153
166
|
import {
|
|
154
167
|
getDialogSettingKeys,
|
|
155
168
|
getNestedProperty,
|
|
156
169
|
getSettingDefinition,
|
|
157
170
|
loadSettings,
|
|
158
171
|
validateSettingValue
|
|
159
|
-
} from "./chunk-
|
|
172
|
+
} from "./chunk-MKHC4BYZ.js";
|
|
160
173
|
import {
|
|
161
174
|
SUPPORTED_LANGUAGES
|
|
162
|
-
} from "./chunk-
|
|
175
|
+
} from "./chunk-UQYOMM6J.js";
|
|
163
176
|
import "./chunk-TLRYABYP.js";
|
|
164
|
-
import "./chunk-KXVVXAAC.js";
|
|
165
177
|
import "./chunk-UFC57OYT.js";
|
|
166
|
-
import {
|
|
167
|
-
STATUS_SCHEMA_VERSION,
|
|
168
|
-
createIdleWorkspaceMemoryStatus,
|
|
169
|
-
mapDomainErrorToErrorKind
|
|
170
|
-
} from "./chunk-VHRREANT.js";
|
|
171
178
|
import "./chunk-PPFWEV7G.js";
|
|
172
179
|
import {
|
|
173
180
|
getWorkspaceTrustStatus
|
|
174
|
-
} from "./chunk-
|
|
181
|
+
} from "./chunk-ZJP5T7IA.js";
|
|
175
182
|
import {
|
|
176
183
|
writeStderrLine
|
|
177
184
|
} from "./chunk-Y6Z2O3WR.js";
|
|
@@ -185,6 +192,7 @@ import {
|
|
|
185
192
|
ClientMcpRegistrar,
|
|
186
193
|
ExtensionManager,
|
|
187
194
|
ModelsConfig,
|
|
195
|
+
RUNTIME_MCP_IF_ABSENT_CONFIG_FLAG,
|
|
188
196
|
SessionService,
|
|
189
197
|
StandardFileSystemService,
|
|
190
198
|
StdioClientTransport,
|
|
@@ -204,21 +212,23 @@ import {
|
|
|
204
212
|
redactUrlCredentials,
|
|
205
213
|
shouldShowStep,
|
|
206
214
|
writeWorkspaceContextFile
|
|
207
|
-
} from "./chunk-
|
|
208
|
-
import "./chunk-K5PGHDBN.js";
|
|
215
|
+
} from "./chunk-BFG6OZN7.js";
|
|
209
216
|
import "./chunk-5P5XGNYH.js";
|
|
217
|
+
import "./chunk-K5PGHDBN.js";
|
|
210
218
|
import "./chunk-SKAZWEV5.js";
|
|
211
|
-
import "./chunk-
|
|
212
|
-
import "./chunk-
|
|
213
|
-
import "./chunk-
|
|
214
|
-
import "./chunk-
|
|
215
|
-
import "./chunk-
|
|
219
|
+
import "./chunk-QOMAHFYI.js";
|
|
220
|
+
import "./chunk-G7OQ3RFJ.js";
|
|
221
|
+
import "./chunk-LKWAVAB5.js";
|
|
222
|
+
import "./chunk-MEQWZGK5.js";
|
|
223
|
+
import "./chunk-UANCYOOQ.js";
|
|
216
224
|
import "./chunk-2MPVVENX.js";
|
|
217
225
|
import "./chunk-ZTQ26VBE.js";
|
|
218
226
|
import "./chunk-SJBIFVD2.js";
|
|
219
227
|
import "./chunk-MLZQVCF3.js";
|
|
228
|
+
import "./chunk-S5EQPDTM.js";
|
|
220
229
|
import "./chunk-LQ7TMOCE.js";
|
|
221
|
-
import "./chunk-
|
|
230
|
+
import "./chunk-E5A7LHNN.js";
|
|
231
|
+
import "./chunk-GO6LNQXT.js";
|
|
222
232
|
import "./chunk-B236XPGF.js";
|
|
223
233
|
import "./chunk-YXK4GPYO.js";
|
|
224
234
|
import "./chunk-TOAUDIS7.js";
|
|
@@ -236,15 +246,15 @@ import {
|
|
|
236
246
|
recordDaemonHttpRequest,
|
|
237
247
|
recordDaemonHttpResponse,
|
|
238
248
|
withDaemonRequestSpan
|
|
239
|
-
} from "./chunk-
|
|
240
|
-
import "./chunk-
|
|
249
|
+
} from "./chunk-HJDF3ZVU.js";
|
|
250
|
+
import "./chunk-E6US47KI.js";
|
|
241
251
|
import "./chunk-UWCTAVOD.js";
|
|
242
252
|
import "./chunk-OFEVLU4C.js";
|
|
243
253
|
import "./chunk-OMX7CUOE.js";
|
|
244
254
|
import "./chunk-MOUXUCXQ.js";
|
|
245
255
|
import "./chunk-BZHLWC6T.js";
|
|
246
|
-
import "./chunk-
|
|
247
|
-
import "./chunk-
|
|
256
|
+
import "./chunk-UQFP2FEF.js";
|
|
257
|
+
import "./chunk-4VZH35PN.js";
|
|
248
258
|
import {
|
|
249
259
|
QwenOAuth2Client,
|
|
250
260
|
QwenOAuthPollError,
|
|
@@ -253,8 +263,8 @@ import {
|
|
|
253
263
|
isDeviceAuthorizationSuccess,
|
|
254
264
|
isDeviceTokenPending,
|
|
255
265
|
isDeviceTokenSuccess
|
|
256
|
-
} from "./chunk-
|
|
257
|
-
import "./chunk-
|
|
266
|
+
} from "./chunk-TENOUWY2.js";
|
|
267
|
+
import "./chunk-GLPV75ES.js";
|
|
258
268
|
import "./chunk-22IFUCVR.js";
|
|
259
269
|
import "./chunk-T42ACHFI.js";
|
|
260
270
|
import "./chunk-YTFBURQD.js";
|
|
@@ -267,7 +277,7 @@ import {
|
|
|
267
277
|
} from "./chunk-H6BD2ELD.js";
|
|
268
278
|
import "./chunk-5IFG2VC4.js";
|
|
269
279
|
import "./chunk-64WXLC72.js";
|
|
270
|
-
import "./chunk-
|
|
280
|
+
import "./chunk-D5KISTLW.js";
|
|
271
281
|
import {
|
|
272
282
|
FatalConfigError,
|
|
273
283
|
Storage,
|
|
@@ -292,6 +302,8 @@ var import_express4 = __toESM(require_express(), 1);
|
|
|
292
302
|
// packages/cli/src/serve/acp-http/index.ts
|
|
293
303
|
init_esbuild_shims();
|
|
294
304
|
import { createHash as createHash3, timingSafeEqual } from "node:crypto";
|
|
305
|
+
import { createRequire } from "node:module";
|
|
306
|
+
import * as path9 from "node:path";
|
|
295
307
|
|
|
296
308
|
// packages/cli/src/serve/acp-http/dispatch.ts
|
|
297
309
|
init_esbuild_shims();
|
|
@@ -1500,8 +1512,8 @@ var PathMutexRegistry = class {
|
|
|
1500
1512
|
async runExclusive(key, fn) {
|
|
1501
1513
|
const previous = this.tails.get(key) ?? Promise.resolve();
|
|
1502
1514
|
let release;
|
|
1503
|
-
const current = new Promise((
|
|
1504
|
-
release =
|
|
1515
|
+
const current = new Promise((resolve5) => {
|
|
1516
|
+
release = resolve5;
|
|
1505
1517
|
});
|
|
1506
1518
|
const tail = previous.catch(() => void 0).then(() => current);
|
|
1507
1519
|
this.tails.set(key, tail);
|
|
@@ -1988,7 +2000,7 @@ async function renameWithRetryLocal(src, dest, retries, delayMs) {
|
|
|
1988
2000
|
const retryable = code === "EPERM" || code === "EACCES";
|
|
1989
2001
|
if (!retryable || attempt === retries) throw err;
|
|
1990
2002
|
await new Promise(
|
|
1991
|
-
(
|
|
2003
|
+
(resolve5) => setTimeout(resolve5, delayMs * 2 ** attempt)
|
|
1992
2004
|
);
|
|
1993
2005
|
}
|
|
1994
2006
|
}
|
|
@@ -4157,6 +4169,316 @@ function createDaemonSubagentManager(boundWorkspace, safeMode = false) {
|
|
|
4157
4169
|
}
|
|
4158
4170
|
__name(createDaemonSubagentManager, "createDaemonSubagentManager");
|
|
4159
4171
|
|
|
4172
|
+
// packages/cli/src/serve/server/session-archive.ts
|
|
4173
|
+
init_esbuild_shims();
|
|
4174
|
+
var SessionArchiveCoordinator = class {
|
|
4175
|
+
static {
|
|
4176
|
+
__name(this, "SessionArchiveCoordinator");
|
|
4177
|
+
}
|
|
4178
|
+
exclusive = /* @__PURE__ */ new Set();
|
|
4179
|
+
shared = /* @__PURE__ */ new Map();
|
|
4180
|
+
assertNotTransitioning(sessionId) {
|
|
4181
|
+
if (this.exclusive.has(sessionId)) {
|
|
4182
|
+
throw new SessionArchivingError(sessionId);
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
async runExclusiveMany(sessionIds, fn) {
|
|
4186
|
+
const uniqueSessionIds = [...new Set(sessionIds)];
|
|
4187
|
+
for (const sessionId of uniqueSessionIds) {
|
|
4188
|
+
this.assertNotTransitioning(sessionId);
|
|
4189
|
+
if ((this.shared.get(sessionId) ?? 0) > 0) {
|
|
4190
|
+
throw new SessionArchivingError(sessionId, "shared");
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
4193
|
+
for (const sessionId of uniqueSessionIds) {
|
|
4194
|
+
this.exclusive.add(sessionId);
|
|
4195
|
+
}
|
|
4196
|
+
try {
|
|
4197
|
+
return await fn();
|
|
4198
|
+
} finally {
|
|
4199
|
+
for (const sessionId of uniqueSessionIds) {
|
|
4200
|
+
this.exclusive.delete(sessionId);
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
async runSharedMany(sessionIds, fn) {
|
|
4205
|
+
const uniqueSessionIds = [...new Set(sessionIds)];
|
|
4206
|
+
for (const sessionId of uniqueSessionIds) {
|
|
4207
|
+
this.assertNotTransitioning(sessionId);
|
|
4208
|
+
}
|
|
4209
|
+
for (const sessionId of uniqueSessionIds) {
|
|
4210
|
+
this.shared.set(sessionId, (this.shared.get(sessionId) ?? 0) + 1);
|
|
4211
|
+
}
|
|
4212
|
+
try {
|
|
4213
|
+
return await fn();
|
|
4214
|
+
} finally {
|
|
4215
|
+
for (const sessionId of uniqueSessionIds) {
|
|
4216
|
+
const count = (this.shared.get(sessionId) ?? 1) - 1;
|
|
4217
|
+
if (count <= 0) {
|
|
4218
|
+
this.shared.delete(sessionId);
|
|
4219
|
+
} else {
|
|
4220
|
+
this.shared.set(sessionId, count);
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
};
|
|
4226
|
+
async function deleteDaemonSessions(params) {
|
|
4227
|
+
const { sessionIds, service, bridge, coordinator, onError } = params;
|
|
4228
|
+
const uniqueSessionIds = [...new Set(sessionIds)];
|
|
4229
|
+
const closeErrors = [];
|
|
4230
|
+
const removed = [];
|
|
4231
|
+
const notFound = [];
|
|
4232
|
+
const removeErrors = [];
|
|
4233
|
+
for (const sessionId of uniqueSessionIds) {
|
|
4234
|
+
coordinator.assertNotTransitioning(sessionId);
|
|
4235
|
+
}
|
|
4236
|
+
await Promise.all(
|
|
4237
|
+
uniqueSessionIds.map(async (sessionId) => {
|
|
4238
|
+
try {
|
|
4239
|
+
await coordinator.runExclusiveMany([sessionId], async () => {
|
|
4240
|
+
let shouldRemove = false;
|
|
4241
|
+
try {
|
|
4242
|
+
await bridge.closeSession(sessionId);
|
|
4243
|
+
shouldRemove = true;
|
|
4244
|
+
} catch (closeErr) {
|
|
4245
|
+
if (closeErr instanceof SessionNotFoundError || closeErr instanceof Error && closeErr.name === "SessionNotFoundError") {
|
|
4246
|
+
shouldRemove = true;
|
|
4247
|
+
} else {
|
|
4248
|
+
const message = closeErr instanceof Error ? closeErr.message : String(closeErr);
|
|
4249
|
+
onError?.({ phase: "close", sessionId, error: message });
|
|
4250
|
+
closeErrors.push({ sessionId, error: message });
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
if (!shouldRemove) return;
|
|
4254
|
+
try {
|
|
4255
|
+
if (await service.removeSession(sessionId)) {
|
|
4256
|
+
removed.push(sessionId);
|
|
4257
|
+
} else {
|
|
4258
|
+
notFound.push(sessionId);
|
|
4259
|
+
}
|
|
4260
|
+
} catch (removeErr) {
|
|
4261
|
+
const message = removeErr instanceof Error ? removeErr.message : String(removeErr);
|
|
4262
|
+
onError?.({ phase: "remove", sessionId, error: message });
|
|
4263
|
+
removeErrors.push({ sessionId, error: message });
|
|
4264
|
+
}
|
|
4265
|
+
});
|
|
4266
|
+
} catch (err) {
|
|
4267
|
+
if (err instanceof SessionArchivingError && err.lockKind === "exclusive") {
|
|
4268
|
+
throw err;
|
|
4269
|
+
}
|
|
4270
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4271
|
+
onError?.({ phase: "delete", sessionId, error: message });
|
|
4272
|
+
closeErrors.push({ sessionId, error: message });
|
|
4273
|
+
}
|
|
4274
|
+
})
|
|
4275
|
+
);
|
|
4276
|
+
return { removed, notFound, errors: [...closeErrors, ...removeErrors] };
|
|
4277
|
+
}
|
|
4278
|
+
__name(deleteDaemonSessions, "deleteDaemonSessions");
|
|
4279
|
+
async function assertSessionLoadable(workspaceCwd, sessionId) {
|
|
4280
|
+
const location = await new SessionService(workspaceCwd).getSessionLocation(
|
|
4281
|
+
sessionId
|
|
4282
|
+
);
|
|
4283
|
+
if (location === "archived") {
|
|
4284
|
+
throw new SessionArchivedError(sessionId);
|
|
4285
|
+
}
|
|
4286
|
+
if (location === "conflict") {
|
|
4287
|
+
throw new SessionConflictError(sessionId);
|
|
4288
|
+
}
|
|
4289
|
+
}
|
|
4290
|
+
__name(assertSessionLoadable, "assertSessionLoadable");
|
|
4291
|
+
function isSessionNotFoundError(err) {
|
|
4292
|
+
return err instanceof SessionNotFoundError || err instanceof Error && err.name === "SessionNotFoundError";
|
|
4293
|
+
}
|
|
4294
|
+
__name(isSessionNotFoundError, "isSessionNotFoundError");
|
|
4295
|
+
async function classifySessionLocations(service, sessionIds) {
|
|
4296
|
+
const result = {
|
|
4297
|
+
active: [],
|
|
4298
|
+
archived: [],
|
|
4299
|
+
notFound: [],
|
|
4300
|
+
errors: []
|
|
4301
|
+
};
|
|
4302
|
+
const locationResults = await Promise.allSettled(
|
|
4303
|
+
sessionIds.map(async (sessionId) => ({
|
|
4304
|
+
sessionId,
|
|
4305
|
+
location: await service.getSessionLocation(sessionId)
|
|
4306
|
+
}))
|
|
4307
|
+
);
|
|
4308
|
+
for (let i = 0; i < locationResults.length; i++) {
|
|
4309
|
+
const sessionId = sessionIds[i];
|
|
4310
|
+
const locationResult = locationResults[i];
|
|
4311
|
+
if (locationResult.status === "rejected") {
|
|
4312
|
+
result.errors.push({ sessionId, error: locationResult.reason });
|
|
4313
|
+
continue;
|
|
4314
|
+
}
|
|
4315
|
+
const location = locationResult.value.location;
|
|
4316
|
+
if (location === void 0) {
|
|
4317
|
+
result.notFound.push(sessionId);
|
|
4318
|
+
} else if (location === "archived") {
|
|
4319
|
+
result.archived.push(sessionId);
|
|
4320
|
+
} else if (location === "conflict") {
|
|
4321
|
+
result.errors.push({
|
|
4322
|
+
sessionId,
|
|
4323
|
+
error: new Error(`Session archive conflict: ${sessionId}`)
|
|
4324
|
+
});
|
|
4325
|
+
} else {
|
|
4326
|
+
result.active.push(sessionId);
|
|
4327
|
+
}
|
|
4328
|
+
}
|
|
4329
|
+
return result;
|
|
4330
|
+
}
|
|
4331
|
+
__name(classifySessionLocations, "classifySessionLocations");
|
|
4332
|
+
function logSessionArchiveResult(action, result) {
|
|
4333
|
+
const changedLabel = action === "archive" ? "archived" : "unarchived";
|
|
4334
|
+
const alreadyLabel = action === "archive" ? "alreadyArchived" : "alreadyActive";
|
|
4335
|
+
const details = [
|
|
4336
|
+
`requested=${result.requested.length} requestedIds=${formatSessionIds(result.requested)}`,
|
|
4337
|
+
`${changedLabel}=${result.changed.length} ${changedLabel}Ids=${formatSessionIds(result.changed)}`,
|
|
4338
|
+
`${alreadyLabel}=${result.already.length} ${alreadyLabel}Ids=${formatSessionIds(result.already)}`,
|
|
4339
|
+
`notFound=${result.notFound.length} notFoundIds=${formatSessionIds(result.notFound)}`,
|
|
4340
|
+
`errors=${result.errors.length} errorIds=${formatSessionErrors(result.errors)}`
|
|
4341
|
+
].join(" ");
|
|
4342
|
+
writeStderrLine(`qwen serve: sessions ${action} result ${details}`);
|
|
4343
|
+
}
|
|
4344
|
+
__name(logSessionArchiveResult, "logSessionArchiveResult");
|
|
4345
|
+
function formatSessionIds(sessionIds) {
|
|
4346
|
+
return `[${sessionIds.map((sessionId) => safeLogValue(sessionId)).join(",")}]`;
|
|
4347
|
+
}
|
|
4348
|
+
__name(formatSessionIds, "formatSessionIds");
|
|
4349
|
+
function formatSessionErrors(errors) {
|
|
4350
|
+
return `[${errors.map(
|
|
4351
|
+
({ sessionId, error: error2 }) => `${safeLogValue(sessionId)}:${safeLogValue(errorMessage(error2))}`
|
|
4352
|
+
).join(",")}]`;
|
|
4353
|
+
}
|
|
4354
|
+
__name(formatSessionErrors, "formatSessionErrors");
|
|
4355
|
+
function errorMessage(error2) {
|
|
4356
|
+
return error2 instanceof Error ? error2.message : String(error2);
|
|
4357
|
+
}
|
|
4358
|
+
__name(errorMessage, "errorMessage");
|
|
4359
|
+
function logSessionArchiveWarning(message) {
|
|
4360
|
+
writeStderrLine(`qwen serve: ${sanitizeLogLine(message)}`);
|
|
4361
|
+
}
|
|
4362
|
+
__name(logSessionArchiveWarning, "logSessionArchiveWarning");
|
|
4363
|
+
var LOG_LINE_UNSAFE_RE = /[\x00-\x1f\x7f-\x9f\u200b-\u200f\u2028-\u202e\u2066-\u2069\ufeff]/g;
|
|
4364
|
+
function sanitizeLogLine(message) {
|
|
4365
|
+
return message.replace(LOG_LINE_UNSAFE_RE, " ").slice(0, 4096);
|
|
4366
|
+
}
|
|
4367
|
+
__name(sanitizeLogLine, "sanitizeLogLine");
|
|
4368
|
+
async function archiveDaemonSessions(params) {
|
|
4369
|
+
const { sessionIds, service, bridge, coordinator } = params;
|
|
4370
|
+
const uniqueSessionIds = [...new Set(sessionIds)];
|
|
4371
|
+
const archived = [];
|
|
4372
|
+
const alreadyArchived = [];
|
|
4373
|
+
const notFound = [];
|
|
4374
|
+
const errors = [];
|
|
4375
|
+
const initial = await classifySessionLocations(service, uniqueSessionIds);
|
|
4376
|
+
const activeIds = initial.active;
|
|
4377
|
+
alreadyArchived.push(...initial.archived);
|
|
4378
|
+
notFound.push(...initial.notFound);
|
|
4379
|
+
errors.push(...initial.errors);
|
|
4380
|
+
if (activeIds.length > 0) {
|
|
4381
|
+
await coordinator.runExclusiveMany(activeIds, async () => {
|
|
4382
|
+
const locked = await classifySessionLocations(service, activeIds);
|
|
4383
|
+
const closableIds = locked.active;
|
|
4384
|
+
alreadyArchived.push(...locked.archived);
|
|
4385
|
+
notFound.push(...locked.notFound);
|
|
4386
|
+
errors.push(...locked.errors);
|
|
4387
|
+
const closeResults = await Promise.allSettled(
|
|
4388
|
+
closableIds.map(async (sessionId) => {
|
|
4389
|
+
try {
|
|
4390
|
+
await bridge.closeSession(sessionId, void 0, {
|
|
4391
|
+
requireAgentClose: true
|
|
4392
|
+
});
|
|
4393
|
+
} catch (err) {
|
|
4394
|
+
if (!isSessionNotFoundError(err)) {
|
|
4395
|
+
throw err;
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
4398
|
+
})
|
|
4399
|
+
);
|
|
4400
|
+
const archiveIds = [];
|
|
4401
|
+
for (let i = 0; i < closeResults.length; i++) {
|
|
4402
|
+
const sessionId = closableIds[i];
|
|
4403
|
+
const result = closeResults[i];
|
|
4404
|
+
if (result.status === "fulfilled") {
|
|
4405
|
+
archiveIds.push(sessionId);
|
|
4406
|
+
} else {
|
|
4407
|
+
errors.push({ sessionId, error: result.reason });
|
|
4408
|
+
}
|
|
4409
|
+
}
|
|
4410
|
+
try {
|
|
4411
|
+
const archiveResult = await service.archiveSessions(archiveIds, {
|
|
4412
|
+
knownLocation: "active"
|
|
4413
|
+
});
|
|
4414
|
+
archived.push(...archiveResult.archived);
|
|
4415
|
+
alreadyArchived.push(...archiveResult.alreadyArchived);
|
|
4416
|
+
notFound.push(...archiveResult.notFound);
|
|
4417
|
+
errors.push(...archiveResult.errors);
|
|
4418
|
+
} catch (err) {
|
|
4419
|
+
for (const sessionId of archiveIds) {
|
|
4420
|
+
errors.push({ sessionId, error: err });
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
});
|
|
4424
|
+
}
|
|
4425
|
+
logSessionArchiveResult("archive", {
|
|
4426
|
+
requested: uniqueSessionIds,
|
|
4427
|
+
changed: archived,
|
|
4428
|
+
already: alreadyArchived,
|
|
4429
|
+
notFound,
|
|
4430
|
+
errors
|
|
4431
|
+
});
|
|
4432
|
+
return { archived, alreadyArchived, notFound, errors };
|
|
4433
|
+
}
|
|
4434
|
+
__name(archiveDaemonSessions, "archiveDaemonSessions");
|
|
4435
|
+
async function unarchiveDaemonSessions(params) {
|
|
4436
|
+
const { sessionIds, service, coordinator } = params;
|
|
4437
|
+
const uniqueSessionIds = [...new Set(sessionIds)];
|
|
4438
|
+
const unarchived = [];
|
|
4439
|
+
const alreadyActive = [];
|
|
4440
|
+
const notFound = [];
|
|
4441
|
+
const errors = [];
|
|
4442
|
+
const initial = await classifySessionLocations(service, uniqueSessionIds);
|
|
4443
|
+
const archivedIds = initial.archived;
|
|
4444
|
+
alreadyActive.push(...initial.active);
|
|
4445
|
+
notFound.push(...initial.notFound);
|
|
4446
|
+
errors.push(...initial.errors);
|
|
4447
|
+
if (archivedIds.length > 0) {
|
|
4448
|
+
await coordinator.runExclusiveMany(archivedIds, async () => {
|
|
4449
|
+
const locked = await classifySessionLocations(service, archivedIds);
|
|
4450
|
+
const unarchiveIds = locked.archived;
|
|
4451
|
+
alreadyActive.push(...locked.active);
|
|
4452
|
+
notFound.push(...locked.notFound);
|
|
4453
|
+
errors.push(...locked.errors);
|
|
4454
|
+
if (unarchiveIds.length > 0) {
|
|
4455
|
+
try {
|
|
4456
|
+
const result = await service.unarchiveSessions(unarchiveIds, {
|
|
4457
|
+
knownLocation: "archived"
|
|
4458
|
+
});
|
|
4459
|
+
unarchived.push(...result.unarchived);
|
|
4460
|
+
alreadyActive.push(...result.alreadyActive);
|
|
4461
|
+
notFound.push(...result.notFound);
|
|
4462
|
+
errors.push(...result.errors);
|
|
4463
|
+
} catch (err) {
|
|
4464
|
+
for (const sessionId of unarchiveIds) {
|
|
4465
|
+
errors.push({ sessionId, error: err });
|
|
4466
|
+
}
|
|
4467
|
+
}
|
|
4468
|
+
}
|
|
4469
|
+
});
|
|
4470
|
+
}
|
|
4471
|
+
logSessionArchiveResult("unarchive", {
|
|
4472
|
+
requested: uniqueSessionIds,
|
|
4473
|
+
changed: unarchived,
|
|
4474
|
+
already: alreadyActive,
|
|
4475
|
+
notFound,
|
|
4476
|
+
errors
|
|
4477
|
+
});
|
|
4478
|
+
return { unarchived, alreadyActive, notFound, errors };
|
|
4479
|
+
}
|
|
4480
|
+
__name(unarchiveDaemonSessions, "unarchiveDaemonSessions");
|
|
4481
|
+
|
|
4160
4482
|
// packages/cli/src/serve/acp-http/json-rpc.ts
|
|
4161
4483
|
init_esbuild_shims();
|
|
4162
4484
|
var QWEN_METHOD_NS = "_qwen/";
|
|
@@ -4275,6 +4597,8 @@ var ALL_QWEN_VENDOR_METHODS = [
|
|
|
4275
4597
|
// Wave 1: memory
|
|
4276
4598
|
`${QWEN_METHOD_NS}workspace/memory`,
|
|
4277
4599
|
`${QWEN_METHOD_NS}workspace/memory/write`,
|
|
4600
|
+
`${QWEN_METHOD_NS}workspace/memory/remember`,
|
|
4601
|
+
`${QWEN_METHOD_NS}workspace/memory/remember/get`,
|
|
4278
4602
|
// Wave 1: files
|
|
4279
4603
|
`${QWEN_METHOD_NS}file/read`,
|
|
4280
4604
|
`${QWEN_METHOD_NS}file/read_bytes`,
|
|
@@ -4295,6 +4619,8 @@ var ALL_QWEN_VENDOR_METHODS = [
|
|
|
4295
4619
|
`${QWEN_METHOD_NS}workspace/mcp/servers/add`,
|
|
4296
4620
|
`${QWEN_METHOD_NS}workspace/mcp/servers/remove`,
|
|
4297
4621
|
`${QWEN_METHOD_NS}sessions/delete`,
|
|
4622
|
+
`${QWEN_METHOD_NS}sessions/archive`,
|
|
4623
|
+
`${QWEN_METHOD_NS}sessions/unarchive`,
|
|
4298
4624
|
// Wave 2: agents
|
|
4299
4625
|
`${QWEN_METHOD_NS}workspace/agents/list`,
|
|
4300
4626
|
`${QWEN_METHOD_NS}workspace/agents/get`,
|
|
@@ -4487,6 +4813,33 @@ function toRpcError(err) {
|
|
|
4487
4813
|
}
|
|
4488
4814
|
const name = err instanceof Error ? err.name : "";
|
|
4489
4815
|
switch (name) {
|
|
4816
|
+
case "SessionArchivedError":
|
|
4817
|
+
return {
|
|
4818
|
+
code: RPC.INTERNAL_ERROR,
|
|
4819
|
+
message: errMsg(err),
|
|
4820
|
+
data: {
|
|
4821
|
+
errorKind: "session_archived",
|
|
4822
|
+
sessionId: err.sessionId
|
|
4823
|
+
}
|
|
4824
|
+
};
|
|
4825
|
+
case "SessionConflictError":
|
|
4826
|
+
return {
|
|
4827
|
+
code: RPC.INTERNAL_ERROR,
|
|
4828
|
+
message: errMsg(err),
|
|
4829
|
+
data: {
|
|
4830
|
+
errorKind: "session_conflict",
|
|
4831
|
+
sessionId: err.sessionId
|
|
4832
|
+
}
|
|
4833
|
+
};
|
|
4834
|
+
case "SessionArchivingError":
|
|
4835
|
+
return {
|
|
4836
|
+
code: RPC.INTERNAL_ERROR,
|
|
4837
|
+
message: errMsg(err),
|
|
4838
|
+
data: {
|
|
4839
|
+
errorKind: "session_archiving",
|
|
4840
|
+
sessionId: err.sessionId
|
|
4841
|
+
}
|
|
4842
|
+
};
|
|
4490
4843
|
case "SessionNotFoundError":
|
|
4491
4844
|
case "InvalidSessionScopeError":
|
|
4492
4845
|
case "WorkspaceMismatchError":
|
|
@@ -4523,14 +4876,16 @@ function rpcErrorFrame(id, err) {
|
|
|
4523
4876
|
__name(rpcErrorFrame, "rpcErrorFrame");
|
|
4524
4877
|
var ACP_PROTOCOL_VERSION = 1;
|
|
4525
4878
|
var AcpDispatcher = class {
|
|
4526
|
-
constructor(bridge, boundWorkspace, workspace, fsFactory, deviceFlowRegistry, sessionShellCommandEnabled = false, registry) {
|
|
4879
|
+
constructor(bridge, boundWorkspace, workspace, workspaceRememberLane, fsFactory, deviceFlowRegistry, sessionShellCommandEnabled = false, registry, archiveCoordinator = new SessionArchiveCoordinator()) {
|
|
4527
4880
|
this.bridge = bridge;
|
|
4528
4881
|
this.boundWorkspace = boundWorkspace;
|
|
4529
4882
|
this.workspace = workspace;
|
|
4883
|
+
this.workspaceRememberLane = workspaceRememberLane;
|
|
4530
4884
|
this.fsFactory = fsFactory;
|
|
4531
4885
|
this.deviceFlowRegistry = deviceFlowRegistry;
|
|
4532
4886
|
this.sessionShellCommandEnabled = sessionShellCommandEnabled;
|
|
4533
4887
|
this.registry = registry;
|
|
4888
|
+
this.archiveCoordinator = archiveCoordinator;
|
|
4534
4889
|
this.agentManager = createDaemonSubagentManager(boundWorkspace);
|
|
4535
4890
|
}
|
|
4536
4891
|
static {
|
|
@@ -4556,6 +4911,21 @@ var AcpDispatcher = class {
|
|
|
4556
4911
|
workspaceCwd: this.boundWorkspace
|
|
4557
4912
|
};
|
|
4558
4913
|
}
|
|
4914
|
+
parseSessionIds(params) {
|
|
4915
|
+
const sessionIds = params["sessionIds"];
|
|
4916
|
+
if (!Array.isArray(sessionIds) || sessionIds.length === 0 || sessionIds.length > 100 || !sessionIds.every((s) => typeof s === "string")) {
|
|
4917
|
+
throw new AcpParamError(
|
|
4918
|
+
"`sessionIds` must be non-empty string array (max 100)"
|
|
4919
|
+
);
|
|
4920
|
+
}
|
|
4921
|
+
return [...new Set(sessionIds)];
|
|
4922
|
+
}
|
|
4923
|
+
serializeSessionErrors(errors) {
|
|
4924
|
+
return errors.map((e) => ({
|
|
4925
|
+
sessionId: e.sessionId,
|
|
4926
|
+
error: errMsg(e.error)
|
|
4927
|
+
}));
|
|
4928
|
+
}
|
|
4559
4929
|
/**
|
|
4560
4930
|
* Build the bridge context for a per-session call. Echoes the clientId the
|
|
4561
4931
|
* bridge STAMPED at create/attach (the connection's own id is unregistered
|
|
@@ -4723,6 +5093,12 @@ var AcpDispatcher = class {
|
|
|
4723
5093
|
);
|
|
4724
5094
|
return false;
|
|
4725
5095
|
}
|
|
5096
|
+
async withMutableOwned(conn, sessionId, id, fn) {
|
|
5097
|
+
if (!this.requireOwned(conn, sessionId, id)) return;
|
|
5098
|
+
await this.archiveCoordinator.runSharedMany([sessionId], async () => {
|
|
5099
|
+
await fn();
|
|
5100
|
+
});
|
|
5101
|
+
}
|
|
4726
5102
|
findPendingClientRequest(conn, id) {
|
|
4727
5103
|
const req = conn.pending.get(id);
|
|
4728
5104
|
if (req) return { conn, id, req };
|
|
@@ -4837,17 +5213,23 @@ var AcpDispatcher = class {
|
|
|
4837
5213
|
return;
|
|
4838
5214
|
}
|
|
4839
5215
|
const cwd = parseOptionalWorkspaceCwd2(params, this.boundWorkspace);
|
|
4840
|
-
const restored =
|
|
4841
|
-
sessionId,
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
5216
|
+
const restored = await this.archiveCoordinator.runSharedMany(
|
|
5217
|
+
[sessionId],
|
|
5218
|
+
async () => {
|
|
5219
|
+
await assertSessionLoadable(cwd, sessionId);
|
|
5220
|
+
return method === "session/load" ? await this.bridge.loadSession({
|
|
5221
|
+
sessionId,
|
|
5222
|
+
workspaceCwd: cwd,
|
|
5223
|
+
clientId: conn.clientId
|
|
5224
|
+
}) : await this.bridge.resumeSession({
|
|
5225
|
+
sessionId,
|
|
5226
|
+
workspaceCwd: cwd,
|
|
5227
|
+
clientId: conn.clientId
|
|
5228
|
+
});
|
|
5229
|
+
}
|
|
5230
|
+
);
|
|
5231
|
+
const closeRaced = conn.closingSessions.has(sessionId);
|
|
5232
|
+
if (conn.destroyed || closeRaced) {
|
|
4851
5233
|
const cleanup = restored.attached ? this.bridge.detachClient(sessionId, restored.clientId) : this.bridge.killSession(sessionId, {
|
|
4852
5234
|
requireZeroAttaches: true
|
|
4853
5235
|
});
|
|
@@ -4881,20 +5263,51 @@ var AcpDispatcher = class {
|
|
|
4881
5263
|
return;
|
|
4882
5264
|
}
|
|
4883
5265
|
case "session/list": {
|
|
5266
|
+
const rawWorkspace = typeof params["workspaceCwd"] === "string" ? params["workspaceCwd"] : void 0;
|
|
5267
|
+
let workspaceCwd = rawWorkspace === void 0 ? this.boundWorkspace : parseOptionalWorkspaceCwd2(
|
|
5268
|
+
{ cwd: rawWorkspace },
|
|
5269
|
+
this.boundWorkspace
|
|
5270
|
+
);
|
|
5271
|
+
if (rawWorkspace !== void 0) {
|
|
5272
|
+
const requestedWorkspace = canonicalizeWorkspace(workspaceCwd);
|
|
5273
|
+
if (requestedWorkspace !== this.boundWorkspace) {
|
|
5274
|
+
throw new WorkspaceMismatchError(
|
|
5275
|
+
this.boundWorkspace,
|
|
5276
|
+
requestedWorkspace
|
|
5277
|
+
);
|
|
5278
|
+
}
|
|
5279
|
+
workspaceCwd = requestedWorkspace;
|
|
5280
|
+
}
|
|
4884
5281
|
const cursor = typeof params["cursor"] === "string" ? params["cursor"] : void 0;
|
|
4885
5282
|
const meta = isObject(params["_meta"]) ? params["_meta"] : void 0;
|
|
4886
5283
|
const metaSize = typeof meta?.["size"] === "number" ? meta["size"] : void 0;
|
|
5284
|
+
const rawArchiveState = typeof params["archiveState"] === "string" ? params["archiveState"] : typeof meta?.["archiveState"] === "string" ? meta["archiveState"] : void 0;
|
|
5285
|
+
let archiveState;
|
|
5286
|
+
if (rawArchiveState !== void 0) {
|
|
5287
|
+
if (rawArchiveState !== "active" && rawArchiveState !== "archived") {
|
|
5288
|
+
throw new AcpParamError(
|
|
5289
|
+
'`archiveState` must be "active" or "archived"'
|
|
5290
|
+
);
|
|
5291
|
+
}
|
|
5292
|
+
archiveState = rawArchiveState;
|
|
5293
|
+
}
|
|
4887
5294
|
const result = await listWorkspaceSessionsForResponse(
|
|
4888
5295
|
this.bridge,
|
|
4889
|
-
|
|
4890
|
-
{ cursor, size: metaSize }
|
|
5296
|
+
workspaceCwd,
|
|
5297
|
+
{ cursor, size: metaSize, archiveState }
|
|
4891
5298
|
);
|
|
4892
5299
|
this.replyConn(conn, id, {
|
|
4893
5300
|
sessions: result.sessions.map((s) => ({
|
|
4894
5301
|
sessionId: s.sessionId,
|
|
5302
|
+
workspaceCwd: s.workspaceCwd,
|
|
4895
5303
|
cwd: s.workspaceCwd,
|
|
5304
|
+
createdAt: s.createdAt,
|
|
5305
|
+
updatedAt: s.updatedAt,
|
|
5306
|
+
displayName: s.displayName,
|
|
4896
5307
|
title: s.displayName,
|
|
4897
|
-
|
|
5308
|
+
clientCount: s.clientCount,
|
|
5309
|
+
hasActivePrompt: s.hasActivePrompt,
|
|
5310
|
+
isArchived: s.isArchived === true
|
|
4898
5311
|
})),
|
|
4899
5312
|
...result.nextCursor != null ? { nextCursor: result.nextCursor } : {}
|
|
4900
5313
|
});
|
|
@@ -4905,19 +5318,47 @@ var AcpDispatcher = class {
|
|
|
4905
5318
|
if (!this.requireOwned(conn, sessionId, id)) return;
|
|
4906
5319
|
conn.ownedSessions.delete(sessionId);
|
|
4907
5320
|
conn.closingSessions.add(sessionId);
|
|
5321
|
+
let closeStarted = false;
|
|
5322
|
+
const closeSession = /* @__PURE__ */ __name(async () => {
|
|
5323
|
+
closeStarted = true;
|
|
5324
|
+
try {
|
|
5325
|
+
await this.bridge.closeSession(
|
|
5326
|
+
sessionId,
|
|
5327
|
+
this.sessionCtx(conn, sessionId, loopback)
|
|
5328
|
+
);
|
|
5329
|
+
} finally {
|
|
5330
|
+
try {
|
|
5331
|
+
conn.closeSessionStream(sessionId);
|
|
5332
|
+
} catch (teardownErr) {
|
|
5333
|
+
writeStderrLine(
|
|
5334
|
+
`qwen serve: /acp session/close local teardown failed (${logSafe(sessionId)}): ${logSafe(errMsg(teardownErr))}`
|
|
5335
|
+
);
|
|
5336
|
+
}
|
|
5337
|
+
}
|
|
5338
|
+
}, "closeSession");
|
|
4908
5339
|
try {
|
|
4909
|
-
await this.bridge.closeSession(
|
|
4910
|
-
sessionId,
|
|
4911
|
-
this.sessionCtx(conn, sessionId, loopback)
|
|
4912
|
-
);
|
|
4913
|
-
} finally {
|
|
4914
5340
|
try {
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
`qwen serve: /acp session/close local teardown failed (${logSafe(sessionId)}): ${logSafe(errMsg(teardownErr))}`
|
|
5341
|
+
await this.archiveCoordinator.runExclusiveMany(
|
|
5342
|
+
[sessionId],
|
|
5343
|
+
closeSession
|
|
4919
5344
|
);
|
|
5345
|
+
} catch (err) {
|
|
5346
|
+
const promptAbort = conn.sessions.get(sessionId)?.promptAbort;
|
|
5347
|
+
if (err instanceof SessionArchivingError && err.lockKind === "shared" && promptAbort !== void 0) {
|
|
5348
|
+
await this.archiveCoordinator.runSharedMany(
|
|
5349
|
+
[sessionId],
|
|
5350
|
+
closeSession
|
|
5351
|
+
);
|
|
5352
|
+
} else {
|
|
5353
|
+
throw err;
|
|
5354
|
+
}
|
|
5355
|
+
}
|
|
5356
|
+
} catch (err) {
|
|
5357
|
+
if (!closeStarted) {
|
|
5358
|
+
conn.ownedSessions.add(sessionId);
|
|
4920
5359
|
}
|
|
5360
|
+
throw err;
|
|
5361
|
+
} finally {
|
|
4921
5362
|
conn.closingSessions.delete(sessionId);
|
|
4922
5363
|
}
|
|
4923
5364
|
this.replyConn(conn, id, {});
|
|
@@ -4935,51 +5376,54 @@ var AcpDispatcher = class {
|
|
|
4935
5376
|
}
|
|
4936
5377
|
return;
|
|
4937
5378
|
}
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
5379
|
+
await this.withMutableOwned(conn, sessionId, id, async () => {
|
|
5380
|
+
const ctx = this.sessionCtx(conn, sessionId, loopback);
|
|
5381
|
+
const result = await this.bridge.branchSession(
|
|
5382
|
+
sessionId,
|
|
5383
|
+
{
|
|
5384
|
+
name: typeof params["name"] === "string" ? params["name"] : void 0
|
|
5385
|
+
},
|
|
5386
|
+
ctx
|
|
5387
|
+
);
|
|
5388
|
+
if (conn.destroyed) {
|
|
5389
|
+
this.killOrphanSession(result.sessionId);
|
|
5390
|
+
return;
|
|
5391
|
+
}
|
|
5392
|
+
conn.getOrCreateSession(result.sessionId).clientId = result.clientId;
|
|
5393
|
+
conn.ownSession(result.sessionId);
|
|
5394
|
+
const configOptions = await this.configOptionsFor(result.sessionId);
|
|
5395
|
+
const models = this.extractModelState(configOptions);
|
|
5396
|
+
const modes = this.extractModeState(configOptions);
|
|
5397
|
+
this.replyConn(conn, id, {
|
|
5398
|
+
sessionId: result.sessionId,
|
|
5399
|
+
...configOptions ? { configOptions } : {},
|
|
5400
|
+
...models ? { models } : {},
|
|
5401
|
+
...modes ? { modes } : {}
|
|
5402
|
+
});
|
|
4961
5403
|
});
|
|
4962
5404
|
return;
|
|
4963
5405
|
}
|
|
4964
5406
|
case "session/cancel": {
|
|
4965
5407
|
const sessionId = String(params["sessionId"] ?? "");
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
5408
|
+
await this.withMutableOwned(conn, sessionId, id, async () => {
|
|
5409
|
+
conn.sessions.get(sessionId)?.promptAbort?.abort();
|
|
5410
|
+
await this.bridge.cancelSession(
|
|
5411
|
+
sessionId,
|
|
5412
|
+
// Forward client-supplied cancel fields (reason/context) while
|
|
5413
|
+
// force-stamping sessionId — mirrors the REST surface.
|
|
5414
|
+
{ ...params, sessionId },
|
|
5415
|
+
this.sessionCtx(conn, sessionId, loopback)
|
|
5416
|
+
);
|
|
5417
|
+
if (id !== void 0) this.replySession(conn, sessionId, id, {});
|
|
5418
|
+
});
|
|
4976
5419
|
return;
|
|
4977
5420
|
}
|
|
4978
5421
|
case "session/prompt": {
|
|
4979
5422
|
const sessionId = String(params["sessionId"] ?? "");
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
5423
|
+
await this.withMutableOwned(conn, sessionId, id, async () => {
|
|
5424
|
+
validatePrompt(params);
|
|
5425
|
+
await this.handlePrompt(conn, sessionId, id, params, loopback);
|
|
5426
|
+
});
|
|
4983
5427
|
return;
|
|
4984
5428
|
}
|
|
4985
5429
|
case "session/permission": {
|
|
@@ -5198,34 +5642,11 @@ var AcpDispatcher = class {
|
|
|
5198
5642
|
// setters. Replaces the old vendor `_qwen/session/set_model`.
|
|
5199
5643
|
case "session/set_config_option": {
|
|
5200
5644
|
const sessionId = String(params["sessionId"] ?? "");
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
if (id !== void 0) {
|
|
5207
|
-
this.replySession(
|
|
5208
|
-
conn,
|
|
5209
|
-
sessionId,
|
|
5210
|
-
id,
|
|
5211
|
-
void 0,
|
|
5212
|
-
error(
|
|
5213
|
-
id,
|
|
5214
|
-
RPC.INVALID_PARAMS,
|
|
5215
|
-
"`value` must be a non-empty string"
|
|
5216
|
-
)
|
|
5217
|
-
);
|
|
5218
|
-
}
|
|
5219
|
-
return;
|
|
5220
|
-
}
|
|
5221
|
-
if (configId === "model") {
|
|
5222
|
-
await this.bridge.setSessionModel(
|
|
5223
|
-
sessionId,
|
|
5224
|
-
{ modelId: rawValue },
|
|
5225
|
-
ctx
|
|
5226
|
-
);
|
|
5227
|
-
} else if (configId === "mode") {
|
|
5228
|
-
if (!APPROVAL_MODES.includes(rawValue)) {
|
|
5645
|
+
await this.withMutableOwned(conn, sessionId, id, async () => {
|
|
5646
|
+
const configId = String(params["configId"] ?? "");
|
|
5647
|
+
const rawValue = params["value"];
|
|
5648
|
+
const ctx = this.sessionCtx(conn, sessionId, loopback);
|
|
5649
|
+
if (typeof rawValue !== "string" || rawValue.length === 0) {
|
|
5229
5650
|
if (id !== void 0) {
|
|
5230
5651
|
this.replySession(
|
|
5231
5652
|
conn,
|
|
@@ -5235,32 +5656,60 @@ var AcpDispatcher = class {
|
|
|
5235
5656
|
error(
|
|
5236
5657
|
id,
|
|
5237
5658
|
RPC.INVALID_PARAMS,
|
|
5238
|
-
`
|
|
5659
|
+
"`value` must be a non-empty string"
|
|
5239
5660
|
)
|
|
5240
5661
|
);
|
|
5241
5662
|
}
|
|
5242
5663
|
return;
|
|
5243
5664
|
}
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5665
|
+
if (configId === "model") {
|
|
5666
|
+
await this.bridge.setSessionModel(
|
|
5667
|
+
sessionId,
|
|
5668
|
+
{ modelId: rawValue },
|
|
5669
|
+
ctx
|
|
5670
|
+
);
|
|
5671
|
+
} else if (configId === "mode") {
|
|
5672
|
+
if (!APPROVAL_MODES.includes(rawValue)) {
|
|
5673
|
+
if (id !== void 0) {
|
|
5674
|
+
this.replySession(
|
|
5675
|
+
conn,
|
|
5676
|
+
sessionId,
|
|
5677
|
+
id,
|
|
5678
|
+
void 0,
|
|
5679
|
+
error(
|
|
5680
|
+
id,
|
|
5681
|
+
RPC.INVALID_PARAMS,
|
|
5682
|
+
`invalid mode "${rawValue}" (expected one of: ${APPROVAL_MODES.join(", ")})`
|
|
5683
|
+
)
|
|
5684
|
+
);
|
|
5685
|
+
}
|
|
5686
|
+
return;
|
|
5687
|
+
}
|
|
5688
|
+
await this.bridge.setSessionApprovalMode(
|
|
5254
5689
|
sessionId,
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5690
|
+
rawValue,
|
|
5691
|
+
{ persist: params["persist"] === true },
|
|
5692
|
+
ctx
|
|
5258
5693
|
);
|
|
5694
|
+
} else {
|
|
5695
|
+
if (id !== void 0) {
|
|
5696
|
+
this.replySession(
|
|
5697
|
+
conn,
|
|
5698
|
+
sessionId,
|
|
5699
|
+
id,
|
|
5700
|
+
void 0,
|
|
5701
|
+
error(
|
|
5702
|
+
id,
|
|
5703
|
+
RPC.INVALID_PARAMS,
|
|
5704
|
+
`Unknown configId: ${configId}`
|
|
5705
|
+
)
|
|
5706
|
+
);
|
|
5707
|
+
}
|
|
5708
|
+
return;
|
|
5259
5709
|
}
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
this.replySession(conn, sessionId, id, { configOptions });
|
|
5710
|
+
const configOptions = await this.configOptionsFor(sessionId);
|
|
5711
|
+
this.replySession(conn, sessionId, id, { configOptions });
|
|
5712
|
+
});
|
|
5264
5713
|
return;
|
|
5265
5714
|
}
|
|
5266
5715
|
// ACP standard: session/set_mode — dedicated method for mode changes.
|
|
@@ -5274,32 +5723,33 @@ var AcpDispatcher = class {
|
|
|
5274
5723
|
);
|
|
5275
5724
|
return;
|
|
5276
5725
|
}
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
id,
|
|
5285
|
-
void 0,
|
|
5286
|
-
error(
|
|
5726
|
+
await this.withMutableOwned(conn, sessionId, id, async () => {
|
|
5727
|
+
const modeId = String(params["modeId"] ?? "");
|
|
5728
|
+
if (!modeId || !APPROVAL_MODES.includes(modeId)) {
|
|
5729
|
+
if (id !== void 0) {
|
|
5730
|
+
this.replySession(
|
|
5731
|
+
conn,
|
|
5732
|
+
sessionId,
|
|
5287
5733
|
id,
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5734
|
+
void 0,
|
|
5735
|
+
error(
|
|
5736
|
+
id,
|
|
5737
|
+
RPC.INVALID_PARAMS,
|
|
5738
|
+
`invalid modeId "${modeId}" (expected one of: ${APPROVAL_MODES.join(", ")})`
|
|
5739
|
+
)
|
|
5740
|
+
);
|
|
5741
|
+
}
|
|
5742
|
+
return;
|
|
5292
5743
|
}
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
);
|
|
5302
|
-
this.replySession(conn, sessionId, id, {});
|
|
5744
|
+
const ctx = this.sessionCtx(conn, sessionId, loopback);
|
|
5745
|
+
await this.bridge.setSessionApprovalMode(
|
|
5746
|
+
sessionId,
|
|
5747
|
+
modeId,
|
|
5748
|
+
{ persist: false },
|
|
5749
|
+
ctx
|
|
5750
|
+
);
|
|
5751
|
+
this.replySession(conn, sessionId, id, {});
|
|
5752
|
+
});
|
|
5303
5753
|
return;
|
|
5304
5754
|
}
|
|
5305
5755
|
// ACP standard (unstable): session/set_model — dedicated method for
|
|
@@ -5314,27 +5764,28 @@ var AcpDispatcher = class {
|
|
|
5314
5764
|
);
|
|
5315
5765
|
return;
|
|
5316
5766
|
}
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5767
|
+
await this.withMutableOwned(conn, sessionId, id, async () => {
|
|
5768
|
+
const modelId = String(params["modelId"] ?? "");
|
|
5769
|
+
if (!modelId) {
|
|
5770
|
+
if (id !== void 0) {
|
|
5771
|
+
this.replySession(
|
|
5772
|
+
conn,
|
|
5773
|
+
sessionId,
|
|
5774
|
+
id,
|
|
5775
|
+
void 0,
|
|
5776
|
+
error(id, RPC.INVALID_PARAMS, "`modelId` is required")
|
|
5777
|
+
);
|
|
5778
|
+
}
|
|
5779
|
+
return;
|
|
5328
5780
|
}
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
);
|
|
5337
|
-
this.replySession(conn, sessionId, id, {});
|
|
5781
|
+
const ctx = this.sessionCtx(conn, sessionId, loopback);
|
|
5782
|
+
await this.bridge.setSessionModel(
|
|
5783
|
+
sessionId,
|
|
5784
|
+
{ modelId, sessionId },
|
|
5785
|
+
ctx
|
|
5786
|
+
);
|
|
5787
|
+
this.replySession(conn, sessionId, id, {});
|
|
5788
|
+
});
|
|
5338
5789
|
return;
|
|
5339
5790
|
}
|
|
5340
5791
|
case `${QWEN_METHOD_NS}session/heartbeat`: {
|
|
@@ -5369,14 +5820,15 @@ var AcpDispatcher = class {
|
|
|
5369
5820
|
}
|
|
5370
5821
|
case `${QWEN_METHOD_NS}session/update_metadata`: {
|
|
5371
5822
|
const sessionId = String(params["sessionId"] ?? "");
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5823
|
+
await this.withMutableOwned(conn, sessionId, id, async () => {
|
|
5824
|
+
const metadata = isObject(params["metadata"]) ? params["metadata"] : {};
|
|
5825
|
+
const result = this.bridge.updateSessionMetadata(
|
|
5826
|
+
sessionId,
|
|
5827
|
+
metadata,
|
|
5828
|
+
this.sessionCtx(conn, sessionId, loopback)
|
|
5829
|
+
);
|
|
5830
|
+
this.replyConn(conn, id, result);
|
|
5831
|
+
});
|
|
5380
5832
|
return;
|
|
5381
5833
|
}
|
|
5382
5834
|
case `${QWEN_METHOD_NS}workspace/mcp`:
|
|
@@ -5718,36 +6170,38 @@ var AcpDispatcher = class {
|
|
|
5718
6170
|
// ── Wave 1+2: ACP/REST parity methods ───────────────────────
|
|
5719
6171
|
case `${QWEN_METHOD_NS}session/recap`: {
|
|
5720
6172
|
const sessionId = String(params["sessionId"] ?? "");
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
6173
|
+
await this.withMutableOwned(conn, sessionId, id, async () => {
|
|
6174
|
+
const result = await this.bridge.generateSessionRecap(
|
|
6175
|
+
sessionId,
|
|
6176
|
+
this.sessionCtx(conn, sessionId, loopback)
|
|
6177
|
+
);
|
|
6178
|
+
this.replyConn(conn, id, result);
|
|
6179
|
+
});
|
|
5727
6180
|
return;
|
|
5728
6181
|
}
|
|
5729
6182
|
case `${QWEN_METHOD_NS}session/btw`: {
|
|
5730
6183
|
const sessionId = String(params["sessionId"] ?? "");
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
6184
|
+
await this.withMutableOwned(conn, sessionId, id, async () => {
|
|
6185
|
+
const rawQ = params["question"];
|
|
6186
|
+
if (typeof rawQ !== "string" || rawQ.trim().length === 0 || rawQ.length > BTW_MAX_INPUT_LENGTH) {
|
|
6187
|
+
if (id !== void 0)
|
|
6188
|
+
conn.sendConn(
|
|
6189
|
+
error(
|
|
6190
|
+
id,
|
|
6191
|
+
RPC.INVALID_PARAMS,
|
|
6192
|
+
`\`question\` required, non-empty, max ${BTW_MAX_INPUT_LENGTH} chars`
|
|
6193
|
+
)
|
|
6194
|
+
);
|
|
6195
|
+
return;
|
|
6196
|
+
}
|
|
6197
|
+
const result = await this.bridge.generateSessionBtw(
|
|
6198
|
+
sessionId,
|
|
6199
|
+
rawQ.trim(),
|
|
6200
|
+
void 0,
|
|
6201
|
+
this.sessionCtx(conn, sessionId, loopback)
|
|
6202
|
+
);
|
|
6203
|
+
this.replyConn(conn, id, result);
|
|
6204
|
+
});
|
|
5751
6205
|
return;
|
|
5752
6206
|
}
|
|
5753
6207
|
case `${QWEN_METHOD_NS}session/shell`: {
|
|
@@ -5758,50 +6212,52 @@ var AcpDispatcher = class {
|
|
|
5758
6212
|
}
|
|
5759
6213
|
return;
|
|
5760
6214
|
}
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
6215
|
+
await this.withMutableOwned(conn, sessionId, id, async () => {
|
|
6216
|
+
const binding = conn.sessions.get(sessionId);
|
|
6217
|
+
const clientId = binding?.clientId;
|
|
6218
|
+
if (!clientId) {
|
|
6219
|
+
if (id !== void 0) {
|
|
6220
|
+
conn.sendConn(
|
|
6221
|
+
rpcErrorFrame(id, new SessionShellClientRequiredError())
|
|
6222
|
+
);
|
|
6223
|
+
}
|
|
6224
|
+
return;
|
|
5769
6225
|
}
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
this.
|
|
5795
|
-
);
|
|
5796
|
-
this.replyConn(conn, id, result);
|
|
6226
|
+
const rawCmd = params["command"];
|
|
6227
|
+
if (typeof rawCmd !== "string" || rawCmd.trim().length === 0) {
|
|
6228
|
+
if (id !== void 0)
|
|
6229
|
+
conn.sendConn(
|
|
6230
|
+
error(
|
|
6231
|
+
id,
|
|
6232
|
+
RPC.INVALID_PARAMS,
|
|
6233
|
+
"`command` required and must be non-empty"
|
|
6234
|
+
)
|
|
6235
|
+
);
|
|
6236
|
+
return;
|
|
6237
|
+
}
|
|
6238
|
+
const logSessionId = logSafe(sessionId.slice(0, 8));
|
|
6239
|
+
const logClientId = logSafe(String(conn.clientId?.slice(0, 8)));
|
|
6240
|
+
const logCommand = logSafe(rawCmd.slice(0, 120));
|
|
6241
|
+
writeStderrLine(
|
|
6242
|
+
`qwen serve: /acp session/shell session=${logSessionId} client=${logClientId} cmd=${logCommand}`
|
|
6243
|
+
);
|
|
6244
|
+
const result = await this.bridge.executeShellCommand(
|
|
6245
|
+
sessionId,
|
|
6246
|
+
rawCmd,
|
|
6247
|
+
binding.abort.signal,
|
|
6248
|
+
this.sessionCtx(conn, sessionId, loopback)
|
|
6249
|
+
);
|
|
6250
|
+
this.replyConn(conn, id, result);
|
|
6251
|
+
});
|
|
5797
6252
|
return;
|
|
5798
6253
|
}
|
|
5799
6254
|
case `${QWEN_METHOD_NS}session/detach`: {
|
|
5800
6255
|
const sessionId = String(params["sessionId"] ?? "");
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
6256
|
+
await this.withMutableOwned(conn, sessionId, id, async () => {
|
|
6257
|
+
const ctx = this.sessionCtx(conn, sessionId, loopback);
|
|
6258
|
+
await this.bridge.detachClient(sessionId, ctx.clientId);
|
|
6259
|
+
this.replyConn(conn, id, { ok: true });
|
|
6260
|
+
});
|
|
5805
6261
|
return;
|
|
5806
6262
|
}
|
|
5807
6263
|
case `${QWEN_METHOD_NS}session/context_usage`: {
|
|
@@ -5913,6 +6369,110 @@ var AcpDispatcher = class {
|
|
|
5913
6369
|
}
|
|
5914
6370
|
return;
|
|
5915
6371
|
}
|
|
6372
|
+
case `${QWEN_METHOD_NS}workspace/memory/remember`: {
|
|
6373
|
+
const content = params["content"];
|
|
6374
|
+
if (typeof content !== "string" || !content.trim()) {
|
|
6375
|
+
if (id !== void 0) {
|
|
6376
|
+
conn.sendConn(
|
|
6377
|
+
error(
|
|
6378
|
+
id,
|
|
6379
|
+
RPC.INVALID_PARAMS,
|
|
6380
|
+
"`content` must be a non-empty string"
|
|
6381
|
+
)
|
|
6382
|
+
);
|
|
6383
|
+
}
|
|
6384
|
+
return;
|
|
6385
|
+
}
|
|
6386
|
+
if (Buffer.byteLength(content, "utf8") > MAX_REMEMBER_CONTENT_BYTES) {
|
|
6387
|
+
if (id !== void 0) {
|
|
6388
|
+
conn.sendConn(
|
|
6389
|
+
error(
|
|
6390
|
+
id,
|
|
6391
|
+
RPC.INVALID_PARAMS,
|
|
6392
|
+
`\`content\` exceeds the ${MAX_REMEMBER_CONTENT_BYTES}-byte limit`
|
|
6393
|
+
)
|
|
6394
|
+
);
|
|
6395
|
+
}
|
|
6396
|
+
return;
|
|
6397
|
+
}
|
|
6398
|
+
const rawContextMode = params["contextMode"] ?? "workspace";
|
|
6399
|
+
if (rawContextMode !== "workspace" && rawContextMode !== "clean") {
|
|
6400
|
+
if (id !== void 0) {
|
|
6401
|
+
conn.sendConn(
|
|
6402
|
+
error(
|
|
6403
|
+
id,
|
|
6404
|
+
RPC.INVALID_PARAMS,
|
|
6405
|
+
'`contextMode` must be "workspace", "clean", or omitted'
|
|
6406
|
+
)
|
|
6407
|
+
);
|
|
6408
|
+
}
|
|
6409
|
+
return;
|
|
6410
|
+
}
|
|
6411
|
+
try {
|
|
6412
|
+
const available = await this.bridge.isWorkspaceMemoryRememberAvailable();
|
|
6413
|
+
if (!available) {
|
|
6414
|
+
if (id !== void 0) {
|
|
6415
|
+
conn.sendConn(
|
|
6416
|
+
error(
|
|
6417
|
+
id,
|
|
6418
|
+
-32009,
|
|
6419
|
+
"Managed memory is unavailable for this daemon workspace",
|
|
6420
|
+
{
|
|
6421
|
+
errorKind: "managed_memory_unavailable",
|
|
6422
|
+
httpStatus: 409
|
|
6423
|
+
}
|
|
6424
|
+
)
|
|
6425
|
+
);
|
|
6426
|
+
}
|
|
6427
|
+
return;
|
|
6428
|
+
}
|
|
6429
|
+
const task = this.workspaceRememberLane.enqueue({
|
|
6430
|
+
content: content.trim(),
|
|
6431
|
+
contextMode: rawContextMode,
|
|
6432
|
+
...conn.clientId ? { originatorClientId: conn.clientId } : {}
|
|
6433
|
+
});
|
|
6434
|
+
this.replyConn(conn, id, task);
|
|
6435
|
+
} catch (err) {
|
|
6436
|
+
const code = extractRememberErrorCode(err);
|
|
6437
|
+
if (id !== void 0) {
|
|
6438
|
+
conn.sendConn(
|
|
6439
|
+
error(
|
|
6440
|
+
id,
|
|
6441
|
+
-32099,
|
|
6442
|
+
code === "remember_queue_full" ? "Workspace memory remember queue is full." : code === "managed_memory_unavailable" ? "Managed memory is unavailable for this daemon workspace" : "Workspace memory remember failed.",
|
|
6443
|
+
{
|
|
6444
|
+
errorKind: code,
|
|
6445
|
+
httpStatus: code === "remember_queue_full" ? 429 : code === "managed_memory_unavailable" ? 409 : 500
|
|
6446
|
+
}
|
|
6447
|
+
)
|
|
6448
|
+
);
|
|
6449
|
+
}
|
|
6450
|
+
}
|
|
6451
|
+
return;
|
|
6452
|
+
}
|
|
6453
|
+
case `${QWEN_METHOD_NS}workspace/memory/remember/get`: {
|
|
6454
|
+
const taskId = params["taskId"];
|
|
6455
|
+
if (typeof taskId !== "string" || taskId.length === 0) {
|
|
6456
|
+
if (id !== void 0) {
|
|
6457
|
+
conn.sendConn(error(id, RPC.INVALID_PARAMS, "`taskId` required"));
|
|
6458
|
+
}
|
|
6459
|
+
return;
|
|
6460
|
+
}
|
|
6461
|
+
const task = this.workspaceRememberLane.get(taskId, conn.clientId);
|
|
6462
|
+
if (!task) {
|
|
6463
|
+
if (id !== void 0) {
|
|
6464
|
+
conn.sendConn(
|
|
6465
|
+
error(id, -32004, "Workspace memory remember task not found", {
|
|
6466
|
+
errorKind: "remember_task_not_found",
|
|
6467
|
+
httpStatus: 404
|
|
6468
|
+
})
|
|
6469
|
+
);
|
|
6470
|
+
}
|
|
6471
|
+
return;
|
|
6472
|
+
}
|
|
6473
|
+
this.replyConn(conn, id, task);
|
|
6474
|
+
return;
|
|
6475
|
+
}
|
|
5916
6476
|
case `${QWEN_METHOD_NS}file/read`: {
|
|
5917
6477
|
const p = String(params["path"] ?? "");
|
|
5918
6478
|
if (!p) {
|
|
@@ -6428,60 +6988,58 @@ var AcpDispatcher = class {
|
|
|
6428
6988
|
return;
|
|
6429
6989
|
}
|
|
6430
6990
|
case `${QWEN_METHOD_NS}sessions/delete`: {
|
|
6431
|
-
const
|
|
6432
|
-
if (!Array.isArray(sessionIds) || sessionIds.length === 0 || sessionIds.length > 100 || !sessionIds.every((s) => typeof s === "string")) {
|
|
6433
|
-
if (id !== void 0)
|
|
6434
|
-
conn.sendConn(
|
|
6435
|
-
error(
|
|
6436
|
-
id,
|
|
6437
|
-
RPC.INVALID_PARAMS,
|
|
6438
|
-
"`sessionIds` must be non-empty string array (max 100)"
|
|
6439
|
-
)
|
|
6440
|
-
);
|
|
6441
|
-
return;
|
|
6442
|
-
}
|
|
6443
|
-
const ids = [...new Set(sessionIds)];
|
|
6444
|
-
const closeErrors = [];
|
|
6445
|
-
const closedIds = [];
|
|
6446
|
-
await Promise.allSettled(
|
|
6447
|
-
ids.map(async (sid) => {
|
|
6448
|
-
try {
|
|
6449
|
-
await this.bridge.closeSession(sid);
|
|
6450
|
-
closedIds.push(sid);
|
|
6451
|
-
} catch (err) {
|
|
6452
|
-
const msg2 = err instanceof Error ? err.message : String(err);
|
|
6453
|
-
if (err instanceof Error && err.name === "SessionNotFoundError") {
|
|
6454
|
-
closedIds.push(sid);
|
|
6455
|
-
} else {
|
|
6456
|
-
const safeSessionId = logSafe(sid.slice(0, 8));
|
|
6457
|
-
const safeMessage = logSafe(msg2);
|
|
6458
|
-
writeStderrLine(
|
|
6459
|
-
`qwen serve: /acp sessions/delete closeSession(${safeSessionId}) failed: ${safeMessage}`
|
|
6460
|
-
);
|
|
6461
|
-
closeErrors.push({ sessionId: sid, error: msg2 });
|
|
6462
|
-
}
|
|
6463
|
-
}
|
|
6464
|
-
})
|
|
6465
|
-
);
|
|
6991
|
+
const ids = this.parseSessionIds(params);
|
|
6466
6992
|
const svc = new SessionService(this.boundWorkspace);
|
|
6467
|
-
const
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6993
|
+
const result = await deleteDaemonSessions({
|
|
6994
|
+
sessionIds: ids,
|
|
6995
|
+
service: svc,
|
|
6996
|
+
bridge: this.bridge,
|
|
6997
|
+
coordinator: this.archiveCoordinator,
|
|
6998
|
+
onError: /* @__PURE__ */ __name(({ phase, sessionId, error: error2 }) => {
|
|
6999
|
+
const safeSessionId = logSafe(sessionId.slice(0, 8));
|
|
7000
|
+
const safeMessage = logSafe(error2);
|
|
7001
|
+
writeStderrLine(
|
|
7002
|
+
`qwen serve: /acp sessions/delete ${phase}Session(${safeSessionId}) failed: ${safeMessage}`
|
|
7003
|
+
);
|
|
7004
|
+
}, "onError")
|
|
7005
|
+
});
|
|
7006
|
+
this.replyConn(conn, id, result);
|
|
7007
|
+
return;
|
|
7008
|
+
}
|
|
7009
|
+
case `${QWEN_METHOD_NS}sessions/archive`: {
|
|
7010
|
+
const ids = this.parseSessionIds(params);
|
|
7011
|
+
const svc = new SessionService(this.boundWorkspace, {
|
|
7012
|
+
onWarning: logSessionArchiveWarning
|
|
7013
|
+
});
|
|
7014
|
+
const result = await archiveDaemonSessions({
|
|
7015
|
+
sessionIds: ids,
|
|
7016
|
+
service: svc,
|
|
7017
|
+
bridge: this.bridge,
|
|
7018
|
+
coordinator: this.archiveCoordinator
|
|
7019
|
+
});
|
|
7020
|
+
this.replyConn(conn, id, {
|
|
7021
|
+
archived: result.archived,
|
|
7022
|
+
alreadyArchived: result.alreadyArchived,
|
|
7023
|
+
notFound: result.notFound,
|
|
7024
|
+
errors: this.serializeSessionErrors(result.errors)
|
|
7025
|
+
});
|
|
7026
|
+
return;
|
|
7027
|
+
}
|
|
7028
|
+
case `${QWEN_METHOD_NS}sessions/unarchive`: {
|
|
7029
|
+
const ids = this.parseSessionIds(params);
|
|
7030
|
+
const svc = new SessionService(this.boundWorkspace, {
|
|
7031
|
+
onWarning: logSessionArchiveWarning
|
|
7032
|
+
});
|
|
7033
|
+
const result = await unarchiveDaemonSessions({
|
|
7034
|
+
sessionIds: ids,
|
|
7035
|
+
service: svc,
|
|
7036
|
+
coordinator: this.archiveCoordinator
|
|
7037
|
+
});
|
|
6475
7038
|
this.replyConn(conn, id, {
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
...removeResult.errors.map((e) => ({
|
|
6481
|
-
sessionId: e.sessionId,
|
|
6482
|
-
error: errMsg(e.error)
|
|
6483
|
-
}))
|
|
6484
|
-
]
|
|
7039
|
+
unarchived: result.unarchived,
|
|
7040
|
+
alreadyActive: result.alreadyActive,
|
|
7041
|
+
notFound: result.notFound,
|
|
7042
|
+
errors: this.serializeSessionErrors(result.errors)
|
|
6485
7043
|
});
|
|
6486
7044
|
return;
|
|
6487
7045
|
}
|
|
@@ -7708,9 +8266,9 @@ var SseStream = class {
|
|
|
7708
8266
|
return next;
|
|
7709
8267
|
}
|
|
7710
8268
|
doWrite(chunk) {
|
|
7711
|
-
return new Promise((
|
|
8269
|
+
return new Promise((resolve5, reject) => {
|
|
7712
8270
|
if (this.closed || this.res.writableEnded) {
|
|
7713
|
-
|
|
8271
|
+
resolve5();
|
|
7714
8272
|
return;
|
|
7715
8273
|
}
|
|
7716
8274
|
let ok;
|
|
@@ -7721,7 +8279,7 @@ var SseStream = class {
|
|
|
7721
8279
|
return;
|
|
7722
8280
|
}
|
|
7723
8281
|
if (ok) {
|
|
7724
|
-
|
|
8282
|
+
resolve5();
|
|
7725
8283
|
return;
|
|
7726
8284
|
}
|
|
7727
8285
|
const cleanup = /* @__PURE__ */ __name(() => {
|
|
@@ -7731,11 +8289,11 @@ var SseStream = class {
|
|
|
7731
8289
|
}, "cleanup");
|
|
7732
8290
|
const onDrain = /* @__PURE__ */ __name(() => {
|
|
7733
8291
|
cleanup();
|
|
7734
|
-
|
|
8292
|
+
resolve5();
|
|
7735
8293
|
}, "onDrain");
|
|
7736
8294
|
const onCloseEv = /* @__PURE__ */ __name(() => {
|
|
7737
8295
|
cleanup();
|
|
7738
|
-
|
|
8296
|
+
resolve5();
|
|
7739
8297
|
}, "onCloseEv");
|
|
7740
8298
|
const onErrorEv = /* @__PURE__ */ __name((err) => {
|
|
7741
8299
|
cleanup();
|
|
@@ -7797,14 +8355,14 @@ var WsStream = class {
|
|
|
7797
8355
|
send(message, _id) {
|
|
7798
8356
|
const data = JSON.stringify(message);
|
|
7799
8357
|
const next = this.writeChain.then(
|
|
7800
|
-
() => new Promise((
|
|
8358
|
+
() => new Promise((resolve5, reject) => {
|
|
7801
8359
|
if (this._closed) {
|
|
7802
|
-
|
|
8360
|
+
resolve5();
|
|
7803
8361
|
return;
|
|
7804
8362
|
}
|
|
7805
8363
|
this.ws.send(data, (err) => {
|
|
7806
8364
|
if (err) reject(err);
|
|
7807
|
-
else
|
|
8365
|
+
else resolve5();
|
|
7808
8366
|
});
|
|
7809
8367
|
})
|
|
7810
8368
|
);
|
|
@@ -8088,16 +8646,14 @@ var CdpReverseLink = class {
|
|
|
8088
8646
|
nextId = 1;
|
|
8089
8647
|
pending = /* @__PURE__ */ new Map();
|
|
8090
8648
|
disposed = false;
|
|
8649
|
+
attached = false;
|
|
8650
|
+
attachPromise;
|
|
8091
8651
|
/** Resolver for the in-flight `cdp_attach` (if any). */
|
|
8092
8652
|
pendingAttach;
|
|
8093
|
-
/**
|
|
8094
|
-
* Opens once the initial `cdp_attach` settles (success OR failure).
|
|
8095
|
-
* `forwardToTab` awaits this so page-domain commands never race the
|
|
8096
|
-
* extension's async `chrome.debugger.attach`. Undefined until {@link attach}.
|
|
8097
|
-
*/
|
|
8098
|
-
attachGate;
|
|
8099
8653
|
/** Called when the extension reports the tab detached. */
|
|
8100
8654
|
onDetach;
|
|
8655
|
+
/** Called when lazy `cdp_attach` fails before any page command can run. */
|
|
8656
|
+
onAttachFailure;
|
|
8101
8657
|
/** Wire the emulator whose `forwardToTab` this link backs. */
|
|
8102
8658
|
bindEmulator(emulator) {
|
|
8103
8659
|
this.emulator = emulator;
|
|
@@ -8110,10 +8666,8 @@ var CdpReverseLink = class {
|
|
|
8110
8666
|
if (this.disposed) {
|
|
8111
8667
|
throw { code: -32e3, message: "CDP tunnel closed" };
|
|
8112
8668
|
}
|
|
8113
|
-
|
|
8114
|
-
|
|
8115
|
-
}
|
|
8116
|
-
return new Promise((resolve4, reject) => {
|
|
8669
|
+
await this.attach();
|
|
8670
|
+
return new Promise((resolve5, reject) => {
|
|
8117
8671
|
if (this.disposed) {
|
|
8118
8672
|
reject({ code: -32e3, message: "CDP tunnel closed" });
|
|
8119
8673
|
return;
|
|
@@ -8124,7 +8678,7 @@ var CdpReverseLink = class {
|
|
|
8124
8678
|
`CDP command id=${id} method=${method} timed out after ${this.commandTimeoutMs}ms`
|
|
8125
8679
|
);
|
|
8126
8680
|
const progressTimer = this.armProgressLog(id, method);
|
|
8127
|
-
this.pending.set(id, { resolve:
|
|
8681
|
+
this.pending.set(id, { resolve: resolve5, reject, timer, progressTimer });
|
|
8128
8682
|
try {
|
|
8129
8683
|
this.log?.(
|
|
8130
8684
|
`qwen serve: /cdp forwarded command id=${id} method=${method} to extension`
|
|
@@ -8148,8 +8702,10 @@ var CdpReverseLink = class {
|
|
|
8148
8702
|
* once the extension acks `cdp_attached` (or rejects on error/timeout).
|
|
8149
8703
|
*/
|
|
8150
8704
|
attach() {
|
|
8151
|
-
|
|
8152
|
-
|
|
8705
|
+
if (this.attached) return Promise.resolve({});
|
|
8706
|
+
if (this.attachPromise) return this.attachPromise;
|
|
8707
|
+
this.attachPromise = new Promise(
|
|
8708
|
+
(resolve5, reject) => {
|
|
8153
8709
|
if (this.disposed) {
|
|
8154
8710
|
reject({ code: -32e3, message: "CDP tunnel closed" });
|
|
8155
8711
|
return;
|
|
@@ -8159,7 +8715,7 @@ var CdpReverseLink = class {
|
|
|
8159
8715
|
this.pendingAttach = {
|
|
8160
8716
|
id,
|
|
8161
8717
|
pending: {
|
|
8162
|
-
resolve: /* @__PURE__ */ __name((
|
|
8718
|
+
resolve: /* @__PURE__ */ __name((result) => resolve5(result ?? {}), "resolve"),
|
|
8163
8719
|
reject,
|
|
8164
8720
|
timer
|
|
8165
8721
|
}
|
|
@@ -8175,12 +8731,24 @@ var CdpReverseLink = class {
|
|
|
8175
8731
|
});
|
|
8176
8732
|
}
|
|
8177
8733
|
}
|
|
8734
|
+
).then(
|
|
8735
|
+
(info) => {
|
|
8736
|
+
this.emulator?.setTabInfo(info);
|
|
8737
|
+
return info;
|
|
8738
|
+
},
|
|
8739
|
+
(err) => {
|
|
8740
|
+
this.attached = false;
|
|
8741
|
+
this.onAttachFailure?.(
|
|
8742
|
+
err instanceof Error ? err.message : err?.message ?? String(err)
|
|
8743
|
+
);
|
|
8744
|
+
throw err;
|
|
8745
|
+
}
|
|
8178
8746
|
);
|
|
8179
|
-
this.
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
);
|
|
8183
|
-
return
|
|
8747
|
+
void this.attachPromise.finally(() => {
|
|
8748
|
+
this.attachPromise = void 0;
|
|
8749
|
+
}).catch(() => {
|
|
8750
|
+
});
|
|
8751
|
+
return this.attachPromise;
|
|
8184
8752
|
}
|
|
8185
8753
|
/**
|
|
8186
8754
|
* Feed one inbound frame from the extension `/acp` socket. Returns true if
|
|
@@ -8236,16 +8804,24 @@ var CdpReverseLink = class {
|
|
|
8236
8804
|
this.pendingAttach = void 0;
|
|
8237
8805
|
clearTimeout(attach.pending.timer);
|
|
8238
8806
|
if (frame.error) {
|
|
8807
|
+
this.attached = false;
|
|
8239
8808
|
attach.pending.reject({
|
|
8240
8809
|
code: -32e3,
|
|
8241
8810
|
message: frame.error.message ?? "cdp_attach failed"
|
|
8242
8811
|
});
|
|
8243
8812
|
return;
|
|
8244
8813
|
}
|
|
8814
|
+
this.attached = true;
|
|
8245
8815
|
attach.pending.resolve({ url: frame.url, title: frame.title });
|
|
8246
8816
|
}
|
|
8247
8817
|
handleDetach(frame) {
|
|
8248
8818
|
const reason = typeof frame.reason === "string" ? frame.reason : "tab detached";
|
|
8819
|
+
this.attached = false;
|
|
8820
|
+
if (this.pendingAttach) {
|
|
8821
|
+
clearTimeout(this.pendingAttach.pending.timer);
|
|
8822
|
+
this.pendingAttach.pending.reject({ code: -32e3, message: reason });
|
|
8823
|
+
this.pendingAttach = void 0;
|
|
8824
|
+
}
|
|
8249
8825
|
this.onDetach?.(reason);
|
|
8250
8826
|
}
|
|
8251
8827
|
armTimeout(id, message) {
|
|
@@ -8261,13 +8837,13 @@ var CdpReverseLink = class {
|
|
|
8261
8837
|
}
|
|
8262
8838
|
armProgressLog(id, method) {
|
|
8263
8839
|
if (!this.log) return void 0;
|
|
8264
|
-
const
|
|
8265
|
-
if (
|
|
8840
|
+
const delay2 = Math.min(COMMAND_PROGRESS_LOG_MS, this.commandTimeoutMs);
|
|
8841
|
+
if (delay2 >= this.commandTimeoutMs) return void 0;
|
|
8266
8842
|
const timer = setTimeout(() => {
|
|
8267
8843
|
this.log?.(
|
|
8268
|
-
`qwen serve: /cdp still waiting for command id=${id} method=${method} after ${
|
|
8844
|
+
`qwen serve: /cdp still waiting for command id=${id} method=${method} after ${delay2}ms`
|
|
8269
8845
|
);
|
|
8270
|
-
},
|
|
8846
|
+
}, delay2);
|
|
8271
8847
|
timer.unref?.();
|
|
8272
8848
|
return timer;
|
|
8273
8849
|
}
|
|
@@ -8306,6 +8882,8 @@ var CdpReverseLink = class {
|
|
|
8306
8882
|
this.pendingAttach.pending.reject({ code: -32e3, message: reason });
|
|
8307
8883
|
this.pendingAttach = void 0;
|
|
8308
8884
|
}
|
|
8885
|
+
this.attached = false;
|
|
8886
|
+
this.attachPromise = void 0;
|
|
8309
8887
|
this.emulator = void 0;
|
|
8310
8888
|
}
|
|
8311
8889
|
};
|
|
@@ -8595,6 +9173,14 @@ function attachCdpClient(ws, registry, log) {
|
|
|
8595
9173
|
bridge.onExtensionGone = void 0;
|
|
8596
9174
|
}
|
|
8597
9175
|
}, "dispose");
|
|
9176
|
+
link.onAttachFailure = (reason) => {
|
|
9177
|
+
log(`qwen serve: /cdp attach failed (${reason}); closing puppeteer socket`);
|
|
9178
|
+
dispose(`cdp_attach failed: ${reason}`, false);
|
|
9179
|
+
try {
|
|
9180
|
+
ws.close(CLOSE_NO_BRIDGE, "cdp attach failed");
|
|
9181
|
+
} catch {
|
|
9182
|
+
}
|
|
9183
|
+
};
|
|
8598
9184
|
ws.on("pong", () => {
|
|
8599
9185
|
heartbeatAlive = true;
|
|
8600
9186
|
});
|
|
@@ -8627,15 +9213,6 @@ function attachCdpClient(ws, registry, log) {
|
|
|
8627
9213
|
);
|
|
8628
9214
|
dispose("puppeteer /cdp socket error");
|
|
8629
9215
|
});
|
|
8630
|
-
void link.attach().then((info) => emulator.setTabInfo(info)).catch((err) => {
|
|
8631
|
-
log(
|
|
8632
|
-
`qwen serve: /cdp attach failed: ${err && typeof err === "object" && "message" in err ? String(err.message) : String(err)}; closing puppeteer socket so a reconnect can retry attach`
|
|
8633
|
-
);
|
|
8634
|
-
try {
|
|
8635
|
-
ws.close(CLOSE_NO_BRIDGE, "cdp_attach failed");
|
|
8636
|
-
} catch {
|
|
8637
|
-
}
|
|
8638
|
-
});
|
|
8639
9216
|
log("qwen serve: /cdp puppeteer client bound to extension bridge");
|
|
8640
9217
|
}
|
|
8641
9218
|
__name(attachCdpClient, "attachCdpClient");
|
|
@@ -8645,6 +9222,71 @@ var ACP_CONNECTION_HEADER = "acp-connection-id";
|
|
|
8645
9222
|
var ACP_SESSION_HEADER = "acp-session-id";
|
|
8646
9223
|
var CDP_PATH = "/cdp";
|
|
8647
9224
|
var CDP_BRIDGE_CLIENT_NAME = "qwen-cdp-bridge";
|
|
9225
|
+
var CHROME_DEVTOOLS_MCP_SERVER_NAME = "chrome-devtools";
|
|
9226
|
+
var RUNTIME_MCP_RETRY_DELAY_MS = 250;
|
|
9227
|
+
var RUNTIME_MCP_RETRY_ATTEMPTS = 20;
|
|
9228
|
+
var requireFromHere = createRequire(import.meta.url);
|
|
9229
|
+
function formatCdpEndpointHost(hostname) {
|
|
9230
|
+
const host = hostname?.trim() || "127.0.0.1";
|
|
9231
|
+
if (host === "0.0.0.0" || host === "::" || host === "[::]") {
|
|
9232
|
+
return "127.0.0.1";
|
|
9233
|
+
}
|
|
9234
|
+
const unbracketed = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
|
|
9235
|
+
return unbracketed.includes(":") ? `[${unbracketed}]` : unbracketed;
|
|
9236
|
+
}
|
|
9237
|
+
__name(formatCdpEndpointHost, "formatCdpEndpointHost");
|
|
9238
|
+
function isAcpChannelUnavailable(err) {
|
|
9239
|
+
return err?.data?.errorKind === "acp_channel_unavailable";
|
|
9240
|
+
}
|
|
9241
|
+
__name(isAcpChannelUnavailable, "isAcpChannelUnavailable");
|
|
9242
|
+
function delay(ms) {
|
|
9243
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
9244
|
+
}
|
|
9245
|
+
__name(delay, "delay");
|
|
9246
|
+
function buildChromeDevToolsMcpRuntimeConfigFromPackage(localPort, pkgJsonPath, pkgBin, hostname) {
|
|
9247
|
+
if (localPort === void 0 || !Number.isInteger(localPort) || localPort <= 0) {
|
|
9248
|
+
return void 0;
|
|
9249
|
+
}
|
|
9250
|
+
const binRel = typeof pkgBin === "string" ? pkgBin : Object.values(pkgBin ?? {})[0];
|
|
9251
|
+
if (!binRel) return void 0;
|
|
9252
|
+
const pkgDir = path9.dirname(pkgJsonPath);
|
|
9253
|
+
const binPath = path9.resolve(pkgDir, binRel);
|
|
9254
|
+
const binRelToPkg = path9.relative(pkgDir, binPath);
|
|
9255
|
+
if (binRelToPkg.startsWith("..") || path9.isAbsolute(binRelToPkg)) {
|
|
9256
|
+
return void 0;
|
|
9257
|
+
}
|
|
9258
|
+
return {
|
|
9259
|
+
command: process.execPath,
|
|
9260
|
+
args: [
|
|
9261
|
+
binPath,
|
|
9262
|
+
"--wsEndpoint",
|
|
9263
|
+
`ws://${formatCdpEndpointHost(hostname)}:${localPort}/cdp`
|
|
9264
|
+
],
|
|
9265
|
+
alwaysLoadTools: true,
|
|
9266
|
+
[RUNTIME_MCP_IF_ABSENT_CONFIG_FLAG]: true
|
|
9267
|
+
};
|
|
9268
|
+
}
|
|
9269
|
+
__name(buildChromeDevToolsMcpRuntimeConfigFromPackage, "buildChromeDevToolsMcpRuntimeConfigFromPackage");
|
|
9270
|
+
function buildChromeDevToolsMcpRuntimeConfig(localPort, hostname) {
|
|
9271
|
+
try {
|
|
9272
|
+
const pkgJsonPath = requireFromHere.resolve(
|
|
9273
|
+
"chrome-devtools-mcp/package.json"
|
|
9274
|
+
);
|
|
9275
|
+
const pkg = requireFromHere("chrome-devtools-mcp/package.json");
|
|
9276
|
+
return buildChromeDevToolsMcpRuntimeConfigFromPackage(
|
|
9277
|
+
localPort,
|
|
9278
|
+
pkgJsonPath,
|
|
9279
|
+
pkg.bin,
|
|
9280
|
+
hostname
|
|
9281
|
+
);
|
|
9282
|
+
} catch (err) {
|
|
9283
|
+
writeStderrLine(
|
|
9284
|
+
`qwen serve: chrome-devtools-mcp package not resolvable: ${err instanceof Error ? err.message : String(err)}`
|
|
9285
|
+
);
|
|
9286
|
+
return void 0;
|
|
9287
|
+
}
|
|
9288
|
+
}
|
|
9289
|
+
__name(buildChromeDevToolsMcpRuntimeConfig, "buildChromeDevToolsMcpRuntimeConfig");
|
|
8648
9290
|
var WS_BEARER_SUBPROTOCOL_PREFIX = "qwen-bearer.";
|
|
8649
9291
|
function extractUpgradeBearer(req) {
|
|
8650
9292
|
const authHeader = req.headers["authorization"];
|
|
@@ -8695,6 +9337,7 @@ var WS_READ_METHODS = /* @__PURE__ */ new Set([
|
|
|
8695
9337
|
"_qwen/workspace/agents/list",
|
|
8696
9338
|
"_qwen/workspace/agents/get",
|
|
8697
9339
|
"_qwen/workspace/memory",
|
|
9340
|
+
"_qwen/workspace/memory/remember/get",
|
|
8698
9341
|
"_qwen/workspace/auth/status",
|
|
8699
9342
|
"_qwen/workspace/auth/device_flow/get",
|
|
8700
9343
|
"_qwen/file/read",
|
|
@@ -8718,7 +9361,7 @@ var MAX_INFLIGHT_MCP_DISPATCH = 8;
|
|
|
8718
9361
|
function mountAcpHttp(app, bridge, opts) {
|
|
8719
9362
|
const enabled = opts.enabled ?? process.env["QWEN_SERVE_ACP_HTTP"] !== "0";
|
|
8720
9363
|
if (!enabled) return void 0;
|
|
8721
|
-
const
|
|
9364
|
+
const path13 = opts.path ?? "/acp";
|
|
8722
9365
|
const dispatcherRef = {};
|
|
8723
9366
|
const registry = new ConnectionRegistry(
|
|
8724
9367
|
(req, clientId) => {
|
|
@@ -8741,17 +9384,121 @@ function mountAcpHttp(app, bridge, opts) {
|
|
|
8741
9384
|
},
|
|
8742
9385
|
opts.maxConnections
|
|
8743
9386
|
);
|
|
9387
|
+
let cdpMcpRegistered = false;
|
|
9388
|
+
let cdpMcpRegistering;
|
|
9389
|
+
let cdpMcpTerminalSkipLogged = false;
|
|
9390
|
+
async function removeChromeDevToolsMcp(originatorClientId) {
|
|
9391
|
+
if (!cdpMcpRegistered) return;
|
|
9392
|
+
cdpMcpRegistered = false;
|
|
9393
|
+
try {
|
|
9394
|
+
await bridge.removeRuntimeMcpServer(
|
|
9395
|
+
CHROME_DEVTOOLS_MCP_SERVER_NAME,
|
|
9396
|
+
originatorClientId
|
|
9397
|
+
);
|
|
9398
|
+
} catch (err) {
|
|
9399
|
+
writeStderrLine(
|
|
9400
|
+
`qwen serve: failed to remove ${CHROME_DEVTOOLS_MCP_SERVER_NAME} runtime MCP: ${err instanceof Error ? err.message : String(err)}`
|
|
9401
|
+
);
|
|
9402
|
+
}
|
|
9403
|
+
}
|
|
9404
|
+
__name(removeChromeDevToolsMcp, "removeChromeDevToolsMcp");
|
|
9405
|
+
function removeChromeDevToolsMcpIfUnused(originatorClientId) {
|
|
9406
|
+
void (async () => {
|
|
9407
|
+
if (opts.cdpTunnelRegistry?.hasActive()) return;
|
|
9408
|
+
if (cdpMcpRegistering) await cdpMcpRegistering;
|
|
9409
|
+
if (opts.cdpTunnelRegistry?.hasActive()) return;
|
|
9410
|
+
await removeChromeDevToolsMcp(originatorClientId);
|
|
9411
|
+
})().catch((err) => {
|
|
9412
|
+
writeStderrLine(
|
|
9413
|
+
`qwen serve: failed to clean up ${CHROME_DEVTOOLS_MCP_SERVER_NAME} runtime MCP: ${err instanceof Error ? err.message : String(err)}`
|
|
9414
|
+
);
|
|
9415
|
+
});
|
|
9416
|
+
}
|
|
9417
|
+
__name(removeChromeDevToolsMcpIfUnused, "removeChromeDevToolsMcpIfUnused");
|
|
9418
|
+
function ensureChromeDevToolsMcpRegistered(localPort, originatorClientId) {
|
|
9419
|
+
if (opts.token) {
|
|
9420
|
+
if (!cdpMcpTerminalSkipLogged) {
|
|
9421
|
+
writeStderrLine(
|
|
9422
|
+
`qwen serve: ${CHROME_DEVTOOLS_MCP_SERVER_NAME} runtime MCP skipped because /cdp requires bearer auth`
|
|
9423
|
+
);
|
|
9424
|
+
cdpMcpTerminalSkipLogged = true;
|
|
9425
|
+
}
|
|
9426
|
+
return;
|
|
9427
|
+
}
|
|
9428
|
+
if (cdpMcpRegistered || cdpMcpRegistering || cdpMcpTerminalSkipLogged) {
|
|
9429
|
+
return;
|
|
9430
|
+
}
|
|
9431
|
+
const runtimeConfig = buildChromeDevToolsMcpRuntimeConfig(
|
|
9432
|
+
localPort,
|
|
9433
|
+
opts.hostname
|
|
9434
|
+
);
|
|
9435
|
+
if (!runtimeConfig) {
|
|
9436
|
+
cdpMcpTerminalSkipLogged = true;
|
|
9437
|
+
return;
|
|
9438
|
+
}
|
|
9439
|
+
cdpMcpRegistering = (async () => {
|
|
9440
|
+
try {
|
|
9441
|
+
let result;
|
|
9442
|
+
for (let attempt = 0; ; attempt++) {
|
|
9443
|
+
try {
|
|
9444
|
+
result = await bridge.addRuntimeMcpServer(
|
|
9445
|
+
CHROME_DEVTOOLS_MCP_SERVER_NAME,
|
|
9446
|
+
runtimeConfig,
|
|
9447
|
+
originatorClientId
|
|
9448
|
+
);
|
|
9449
|
+
break;
|
|
9450
|
+
} catch (err) {
|
|
9451
|
+
if (!isAcpChannelUnavailable(err) || attempt >= RUNTIME_MCP_RETRY_ATTEMPTS || !opts.cdpTunnelRegistry?.hasActive()) {
|
|
9452
|
+
throw err;
|
|
9453
|
+
}
|
|
9454
|
+
await delay(RUNTIME_MCP_RETRY_DELAY_MS);
|
|
9455
|
+
}
|
|
9456
|
+
}
|
|
9457
|
+
if (result.skipped) {
|
|
9458
|
+
writeStderrLine(
|
|
9459
|
+
`qwen serve: ${CHROME_DEVTOOLS_MCP_SERVER_NAME} runtime MCP skipped: ${result.reason ?? "unknown"}`
|
|
9460
|
+
);
|
|
9461
|
+
return;
|
|
9462
|
+
}
|
|
9463
|
+
if (result.shadowedSettings) {
|
|
9464
|
+
await bridge.removeRuntimeMcpServer(
|
|
9465
|
+
CHROME_DEVTOOLS_MCP_SERVER_NAME,
|
|
9466
|
+
originatorClientId
|
|
9467
|
+
).catch(() => {
|
|
9468
|
+
});
|
|
9469
|
+
cdpMcpTerminalSkipLogged = true;
|
|
9470
|
+
writeStderrLine(
|
|
9471
|
+
`qwen serve: ${CHROME_DEVTOOLS_MCP_SERVER_NAME} runtime MCP skipped because settings already define it`
|
|
9472
|
+
);
|
|
9473
|
+
return;
|
|
9474
|
+
}
|
|
9475
|
+
cdpMcpRegistered = true;
|
|
9476
|
+
if (!opts.cdpTunnelRegistry?.hasActive()) {
|
|
9477
|
+
await removeChromeDevToolsMcp(originatorClientId);
|
|
9478
|
+
}
|
|
9479
|
+
} catch (err) {
|
|
9480
|
+
writeStderrLine(
|
|
9481
|
+
`qwen serve: failed to add ${CHROME_DEVTOOLS_MCP_SERVER_NAME} runtime MCP: ${err instanceof Error ? err.message : String(err)}`
|
|
9482
|
+
);
|
|
9483
|
+
} finally {
|
|
9484
|
+
cdpMcpRegistering = void 0;
|
|
9485
|
+
}
|
|
9486
|
+
})();
|
|
9487
|
+
}
|
|
9488
|
+
__name(ensureChromeDevToolsMcpRegistered, "ensureChromeDevToolsMcpRegistered");
|
|
8744
9489
|
const dispatcher = new AcpDispatcher(
|
|
8745
9490
|
bridge,
|
|
8746
9491
|
opts.boundWorkspace,
|
|
8747
9492
|
opts.workspace,
|
|
9493
|
+
opts.workspaceRememberLane,
|
|
8748
9494
|
opts.fsFactory,
|
|
8749
9495
|
opts.deviceFlowRegistry,
|
|
8750
9496
|
opts.sessionShellCommandEnabled === true,
|
|
8751
|
-
registry
|
|
9497
|
+
registry,
|
|
9498
|
+
opts.archiveCoordinator ?? new SessionArchiveCoordinator()
|
|
8752
9499
|
);
|
|
8753
9500
|
dispatcherRef.current = dispatcher;
|
|
8754
|
-
app.post(
|
|
9501
|
+
app.post(path13, async (req, res) => {
|
|
8755
9502
|
const ct = req.headers["content-type"];
|
|
8756
9503
|
if (!ct || !ct.startsWith("application/json")) {
|
|
8757
9504
|
res.status(415).json({ error: "Content-Type must be application/json" });
|
|
@@ -8855,7 +9602,7 @@ function mountAcpHttp(app, bridge, opts) {
|
|
|
8855
9602
|
);
|
|
8856
9603
|
});
|
|
8857
9604
|
});
|
|
8858
|
-
app.get(
|
|
9605
|
+
app.get(path13, (req, res) => {
|
|
8859
9606
|
const accept = req.headers["accept"] ?? "";
|
|
8860
9607
|
if (!accept.includes("text/event-stream")) {
|
|
8861
9608
|
res.status(406).json({ error: "Accept header must include text/event-stream" });
|
|
@@ -8941,7 +9688,7 @@ function mountAcpHttp(app, bridge, opts) {
|
|
|
8941
9688
|
onPumpSettled();
|
|
8942
9689
|
});
|
|
8943
9690
|
});
|
|
8944
|
-
app.delete(
|
|
9691
|
+
app.delete(path13, (req, res) => {
|
|
8945
9692
|
const connectionId = headerOf(req, ACP_CONNECTION_HEADER);
|
|
8946
9693
|
if (!connectionId) {
|
|
8947
9694
|
res.status(400).json({ error: "Missing Acp-Connection-Id" });
|
|
@@ -8983,6 +9730,7 @@ function mountAcpHttp(app, bridge, opts) {
|
|
|
8983
9730
|
const expectedTokenHash = opts.token ? createHash3("sha256").update(opts.token).digest() : void 0;
|
|
8984
9731
|
upgradeListener = /* @__PURE__ */ __name((req, socket, head) => {
|
|
8985
9732
|
const rawAddr = socket.remoteAddress ?? "ws-unknown";
|
|
9733
|
+
const localPort = socket.localPort;
|
|
8986
9734
|
const logReject = /* @__PURE__ */ __name((reason) => {
|
|
8987
9735
|
writeStderrLine(
|
|
8988
9736
|
`qwen serve: WebSocket upgrade rejected (${reason}) from ${rawAddr}`
|
|
@@ -9003,7 +9751,7 @@ function mountAcpHttp(app, bridge, opts) {
|
|
|
9003
9751
|
const extraRoute = opts.extraWsRoutes?.find(
|
|
9004
9752
|
(route) => route.path === url.pathname
|
|
9005
9753
|
);
|
|
9006
|
-
if (url.pathname !==
|
|
9754
|
+
if (url.pathname !== path13 && !isCdpPath && !extraRoute) {
|
|
9007
9755
|
logReject(`unknown-path ${url.pathname}`);
|
|
9008
9756
|
socket.destroy();
|
|
9009
9757
|
return;
|
|
@@ -9011,7 +9759,6 @@ function mountAcpHttp(app, bridge, opts) {
|
|
|
9011
9759
|
const fromLoopback = isLoopbackSocket(socket);
|
|
9012
9760
|
if (fromLoopback) {
|
|
9013
9761
|
const host = (req.headers["host"] ?? "").toLowerCase();
|
|
9014
|
-
const localPort = socket.localPort;
|
|
9015
9762
|
const allowed = /* @__PURE__ */ new Set([
|
|
9016
9763
|
`localhost:${localPort}`,
|
|
9017
9764
|
`127.0.0.1:${localPort}`,
|
|
@@ -9028,7 +9775,6 @@ function mountAcpHttp(app, bridge, opts) {
|
|
|
9028
9775
|
const origin = req.headers["origin"];
|
|
9029
9776
|
if (origin) {
|
|
9030
9777
|
try {
|
|
9031
|
-
const localPort = socket.localPort;
|
|
9032
9778
|
const isLoopbackOrigin = isSameLoopbackOrigin(origin, localPort);
|
|
9033
9779
|
const isAllowlistedOrigin = opts.allowedOrigins !== void 0 && opts.allowedOrigins.origins.has(origin.toLowerCase());
|
|
9034
9780
|
if (!isLoopbackOrigin && !isAllowlistedOrigin) {
|
|
@@ -9101,6 +9847,9 @@ function mountAcpHttp(app, bridge, opts) {
|
|
|
9101
9847
|
if (cdpBridgeUnregister) {
|
|
9102
9848
|
cdpBridgeUnregister();
|
|
9103
9849
|
cdpBridgeUnregister = void 0;
|
|
9850
|
+
removeChromeDevToolsMcpIfUnused(
|
|
9851
|
+
connRef?.connectionId ?? "cdp-bridge"
|
|
9852
|
+
);
|
|
9104
9853
|
}
|
|
9105
9854
|
});
|
|
9106
9855
|
ws.on("message", (rawData) => {
|
|
@@ -9322,6 +10071,7 @@ function mountAcpHttp(app, bridge, opts) {
|
|
|
9322
10071
|
routeInbound: /* @__PURE__ */ __name(() => false, "routeInbound")
|
|
9323
10072
|
};
|
|
9324
10073
|
cdpBridgeUnregister = opts.cdpTunnelRegistry.register(cdpEndpoint);
|
|
10074
|
+
ensureChromeDevToolsMcpRegistered(localPort, conn2.connectionId);
|
|
9325
10075
|
writeStderrLine(
|
|
9326
10076
|
`qwen serve: /acp connection ${conn2.connectionId.slice(0, 8)} registered as CDP bridge`
|
|
9327
10077
|
);
|
|
@@ -9392,7 +10142,7 @@ function mountAcpHttp(app, bridge, opts) {
|
|
|
9392
10142
|
});
|
|
9393
10143
|
}, "upgradeListener");
|
|
9394
10144
|
httpServer.on("upgrade", upgradeListener);
|
|
9395
|
-
writeStderrLine(`qwen serve: /acp WebSocket transport enabled on ${
|
|
10145
|
+
writeStderrLine(`qwen serve: /acp WebSocket transport enabled on ${path13}`);
|
|
9396
10146
|
}
|
|
9397
10147
|
__name(setupWebSocket, "setupWebSocket");
|
|
9398
10148
|
return {
|
|
@@ -9860,7 +10610,7 @@ var CdpTunnelRegistry = class {
|
|
|
9860
10610
|
// packages/cli/src/serve/web-shell-static.ts
|
|
9861
10611
|
init_esbuild_shims();
|
|
9862
10612
|
var import_express2 = __toESM(require_express(), 1);
|
|
9863
|
-
import * as
|
|
10613
|
+
import * as path10 from "node:path";
|
|
9864
10614
|
var WEB_SHELL_CSP_DIRECTIVES = [
|
|
9865
10615
|
"default-src 'self'",
|
|
9866
10616
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval'",
|
|
@@ -9888,7 +10638,7 @@ function isDocumentNavigation(req) {
|
|
|
9888
10638
|
}
|
|
9889
10639
|
__name(isDocumentNavigation, "isDocumentNavigation");
|
|
9890
10640
|
function createSendIndex(webShellDir, frameAncestors = []) {
|
|
9891
|
-
const indexPath =
|
|
10641
|
+
const indexPath = path10.join(webShellDir, "index.html");
|
|
9892
10642
|
const csp = buildWebShellCsp(frameAncestors);
|
|
9893
10643
|
return (res) => {
|
|
9894
10644
|
res.status(200).set("Content-Security-Policy", csp).set("X-Content-Type-Options", "nosniff").set("Referrer-Policy", "no-referrer").set(
|
|
@@ -9924,7 +10674,7 @@ function mountWebShellAssets(app, webShellDir, frameAncestors = []) {
|
|
|
9924
10674
|
const sendIndex = createSendIndex(webShellDir, frameAncestors);
|
|
9925
10675
|
app.use(
|
|
9926
10676
|
"/assets",
|
|
9927
|
-
import_express2.default.static(
|
|
10677
|
+
import_express2.default.static(path10.join(webShellDir, "assets"), {
|
|
9928
10678
|
index: false,
|
|
9929
10679
|
immutable: true,
|
|
9930
10680
|
maxAge: "1y"
|
|
@@ -9956,43 +10706,301 @@ function mountWebShellSpaFallback(app, webShellDir, frameAncestors = []) {
|
|
|
9956
10706
|
}
|
|
9957
10707
|
__name(mountWebShellSpaFallback, "mountWebShellSpaFallback");
|
|
9958
10708
|
|
|
9959
|
-
// packages/cli/src/serve/
|
|
10709
|
+
// packages/cli/src/serve/workspace-remember.ts
|
|
9960
10710
|
init_esbuild_shims();
|
|
9961
|
-
|
|
9962
|
-
|
|
9963
|
-
|
|
9964
|
-
|
|
9965
|
-
|
|
9966
|
-
|
|
9967
|
-
|
|
10711
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
10712
|
+
var debugLogger2 = createDebugLogger("WORKSPACE_REMEMBER");
|
|
10713
|
+
function nowIso() {
|
|
10714
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
10715
|
+
}
|
|
10716
|
+
__name(nowIso, "nowIso");
|
|
10717
|
+
function createRememberTaskId() {
|
|
10718
|
+
return `remember-${randomUUID2()}`;
|
|
10719
|
+
}
|
|
10720
|
+
__name(createRememberTaskId, "createRememberTaskId");
|
|
10721
|
+
function cloneTask(task) {
|
|
10722
|
+
return {
|
|
10723
|
+
taskId: task.taskId,
|
|
10724
|
+
status: task.status,
|
|
10725
|
+
contextMode: task.contextMode,
|
|
10726
|
+
createdAt: task.createdAt,
|
|
10727
|
+
updatedAt: task.updatedAt,
|
|
10728
|
+
result: task.result ? {
|
|
10729
|
+
...task.result,
|
|
10730
|
+
filesTouched: [...task.result.filesTouched],
|
|
10731
|
+
touchedScopes: [...task.result.touchedScopes]
|
|
10732
|
+
} : void 0,
|
|
10733
|
+
error: task.error ? { ...task.error } : void 0
|
|
10734
|
+
};
|
|
10735
|
+
}
|
|
10736
|
+
__name(cloneTask, "cloneTask");
|
|
10737
|
+
function publicErrorMessage(code) {
|
|
10738
|
+
if (code === "managed_memory_unavailable") {
|
|
10739
|
+
return "Managed memory is unavailable for this daemon workspace.";
|
|
10740
|
+
}
|
|
10741
|
+
if (code === "remember_path_escape") {
|
|
10742
|
+
return "Remember agent touched a path outside managed memory.";
|
|
10743
|
+
}
|
|
10744
|
+
if (code === "remember_queue_full") {
|
|
10745
|
+
return "Workspace memory remember queue is full.";
|
|
10746
|
+
}
|
|
10747
|
+
if (code === "remember_timeout") {
|
|
10748
|
+
return "Workspace memory remember timed out.";
|
|
10749
|
+
}
|
|
10750
|
+
return "Workspace memory remember failed.";
|
|
10751
|
+
}
|
|
10752
|
+
__name(publicErrorMessage, "publicErrorMessage");
|
|
10753
|
+
function publicErrorStatus(code) {
|
|
10754
|
+
if (code === "remember_queue_full") return 429;
|
|
10755
|
+
if (code === "managed_memory_unavailable") return 409;
|
|
10756
|
+
return 500;
|
|
10757
|
+
}
|
|
10758
|
+
__name(publicErrorStatus, "publicErrorStatus");
|
|
10759
|
+
var WorkspaceRememberTaskLane = class _WorkspaceRememberTaskLane {
|
|
10760
|
+
constructor(bridge) {
|
|
10761
|
+
this.bridge = bridge;
|
|
10762
|
+
}
|
|
10763
|
+
static {
|
|
10764
|
+
__name(this, "WorkspaceRememberTaskLane");
|
|
10765
|
+
}
|
|
10766
|
+
static MAX_TASKS = 1e3;
|
|
10767
|
+
static MAX_PENDING = 16;
|
|
10768
|
+
tasks = /* @__PURE__ */ new Map();
|
|
10769
|
+
tail = Promise.resolve();
|
|
10770
|
+
pendingCount() {
|
|
10771
|
+
let count = 0;
|
|
10772
|
+
for (const task of this.tasks.values()) {
|
|
10773
|
+
if (task.status === "queued" || task.status === "running") count++;
|
|
10774
|
+
}
|
|
10775
|
+
return count;
|
|
10776
|
+
}
|
|
10777
|
+
evictTerminalTasks() {
|
|
10778
|
+
if (this.tasks.size <= _WorkspaceRememberTaskLane.MAX_TASKS) return;
|
|
10779
|
+
for (const [id, task] of this.tasks) {
|
|
10780
|
+
if (task.status === "completed" || task.status === "failed") {
|
|
10781
|
+
this.tasks.delete(id);
|
|
10782
|
+
if (this.tasks.size <= _WorkspaceRememberTaskLane.MAX_TASKS) break;
|
|
10783
|
+
}
|
|
10784
|
+
}
|
|
10785
|
+
}
|
|
10786
|
+
enqueue(params) {
|
|
10787
|
+
if (this.pendingCount() >= _WorkspaceRememberTaskLane.MAX_PENDING) {
|
|
10788
|
+
throw Object.assign(new Error("Remember queue is full"), {
|
|
10789
|
+
code: "remember_queue_full"
|
|
9968
10790
|
});
|
|
9969
|
-
return;
|
|
9970
10791
|
}
|
|
9971
|
-
|
|
9972
|
-
|
|
9973
|
-
|
|
9974
|
-
|
|
9975
|
-
|
|
9976
|
-
|
|
9977
|
-
|
|
9978
|
-
|
|
9979
|
-
|
|
9980
|
-
|
|
9981
|
-
|
|
9982
|
-
|
|
9983
|
-
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
|
|
9987
|
-
|
|
9988
|
-
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
|
|
9992
|
-
|
|
9993
|
-
|
|
9994
|
-
|
|
9995
|
-
|
|
10792
|
+
const task = {
|
|
10793
|
+
taskId: createRememberTaskId(),
|
|
10794
|
+
status: "queued",
|
|
10795
|
+
contextMode: params.contextMode,
|
|
10796
|
+
createdAt: nowIso(),
|
|
10797
|
+
updatedAt: nowIso(),
|
|
10798
|
+
...params.originatorClientId ? { originatorClientId: params.originatorClientId } : {}
|
|
10799
|
+
};
|
|
10800
|
+
this.tasks.set(task.taskId, task);
|
|
10801
|
+
this.evictTerminalTasks();
|
|
10802
|
+
const run = /* @__PURE__ */ __name(async () => {
|
|
10803
|
+
task.status = "running";
|
|
10804
|
+
task.updatedAt = nowIso();
|
|
10805
|
+
try {
|
|
10806
|
+
const result = await this.bridge.runWorkspaceMemoryRemember({
|
|
10807
|
+
content: params.content,
|
|
10808
|
+
contextMode: params.contextMode
|
|
10809
|
+
});
|
|
10810
|
+
task.status = "completed";
|
|
10811
|
+
task.result = {
|
|
10812
|
+
summary: result.filesTouched.length > 0 ? "Memory update completed." : "No memory files updated.",
|
|
10813
|
+
filesTouched: result.filesTouched,
|
|
10814
|
+
touchedScopes: result.touchedScopes
|
|
10815
|
+
};
|
|
10816
|
+
task.updatedAt = nowIso();
|
|
10817
|
+
} catch (err) {
|
|
10818
|
+
const code = extractRememberErrorCode(err);
|
|
10819
|
+
debugLogger2.error("Remember task failed:", err);
|
|
10820
|
+
task.status = "failed";
|
|
10821
|
+
task.error = {
|
|
10822
|
+
code,
|
|
10823
|
+
message: publicErrorMessage(code)
|
|
10824
|
+
};
|
|
10825
|
+
task.updatedAt = nowIso();
|
|
10826
|
+
} finally {
|
|
10827
|
+
this.evictTerminalTasks();
|
|
10828
|
+
}
|
|
10829
|
+
if (task.status === "completed" && task.result && task.result.touchedScopes.length > 0) {
|
|
10830
|
+
try {
|
|
10831
|
+
this.bridge.publishWorkspaceEvent({
|
|
10832
|
+
type: "memory_changed",
|
|
10833
|
+
data: {
|
|
10834
|
+
scope: "managed",
|
|
10835
|
+
source: "workspace_memory_remember",
|
|
10836
|
+
taskId: task.taskId,
|
|
10837
|
+
touchedScopes: task.result.touchedScopes
|
|
10838
|
+
},
|
|
10839
|
+
...params.originatorClientId ? { originatorClientId: params.originatorClientId } : {}
|
|
10840
|
+
});
|
|
10841
|
+
} catch (err) {
|
|
10842
|
+
debugLogger2.error("Failed to publish memory_changed event:", err);
|
|
10843
|
+
}
|
|
10844
|
+
}
|
|
10845
|
+
}, "run");
|
|
10846
|
+
this.tail = this.tail.then(run, run);
|
|
10847
|
+
void this.tail.catch((err) => {
|
|
10848
|
+
debugLogger2.error("Unhandled task lane error:", err);
|
|
10849
|
+
});
|
|
10850
|
+
return cloneTask(task);
|
|
10851
|
+
}
|
|
10852
|
+
get(taskId, requesterClientId) {
|
|
10853
|
+
const task = this.tasks.get(taskId);
|
|
10854
|
+
if (!task) return void 0;
|
|
10855
|
+
if (task.originatorClientId) {
|
|
10856
|
+
if (task.originatorClientId !== requesterClientId) return void 0;
|
|
10857
|
+
} else if (requesterClientId) {
|
|
10858
|
+
return void 0;
|
|
10859
|
+
}
|
|
10860
|
+
return cloneTask(task);
|
|
10861
|
+
}
|
|
10862
|
+
};
|
|
10863
|
+
function validateOriginatorClientId(deps, req, res) {
|
|
10864
|
+
const clientId = deps.parseClientId(req, res);
|
|
10865
|
+
if (clientId === null) return null;
|
|
10866
|
+
if (clientId === void 0) return void 0;
|
|
10867
|
+
const known = deps.bridge.knownClientIds();
|
|
10868
|
+
if (!known.has(clientId)) {
|
|
10869
|
+
res.status(400).json({
|
|
10870
|
+
error: `Client id "${clientId}" is not registered for this workspace`,
|
|
10871
|
+
code: "invalid_client_id",
|
|
10872
|
+
clientId
|
|
10873
|
+
});
|
|
10874
|
+
return null;
|
|
10875
|
+
}
|
|
10876
|
+
return clientId;
|
|
10877
|
+
}
|
|
10878
|
+
__name(validateOriginatorClientId, "validateOriginatorClientId");
|
|
10879
|
+
function mountWorkspaceMemoryRememberRoutes(app, deps) {
|
|
10880
|
+
app.post(
|
|
10881
|
+
"/workspace/memory/remember",
|
|
10882
|
+
deps.mutate({ strict: true }),
|
|
10883
|
+
async (req, res) => {
|
|
10884
|
+
const body = deps.safeBody(req);
|
|
10885
|
+
const content = body["content"];
|
|
10886
|
+
const trimmedContent = typeof content === "string" ? content.trim() : "";
|
|
10887
|
+
if (!trimmedContent) {
|
|
10888
|
+
res.status(400).json({
|
|
10889
|
+
error: "`content` must be a non-empty string",
|
|
10890
|
+
code: "invalid_content"
|
|
10891
|
+
});
|
|
10892
|
+
return;
|
|
10893
|
+
}
|
|
10894
|
+
if (Buffer.byteLength(trimmedContent, "utf8") > MAX_REMEMBER_CONTENT_BYTES) {
|
|
10895
|
+
res.status(400).json({
|
|
10896
|
+
error: `\`content\` exceeds the ${MAX_REMEMBER_CONTENT_BYTES}-byte limit`,
|
|
10897
|
+
code: "invalid_content"
|
|
10898
|
+
});
|
|
10899
|
+
return;
|
|
10900
|
+
}
|
|
10901
|
+
const contextModeRaw = body["contextMode"] ?? "workspace";
|
|
10902
|
+
if (contextModeRaw !== "workspace" && contextModeRaw !== "clean") {
|
|
10903
|
+
res.status(400).json({
|
|
10904
|
+
error: '`contextMode` must be "workspace", "clean", or omitted',
|
|
10905
|
+
code: "invalid_context_mode"
|
|
10906
|
+
});
|
|
10907
|
+
return;
|
|
10908
|
+
}
|
|
10909
|
+
const originatorClientId = validateOriginatorClientId(deps, req, res);
|
|
10910
|
+
if (originatorClientId === null) return;
|
|
10911
|
+
try {
|
|
10912
|
+
const available = await deps.bridge.isWorkspaceMemoryRememberAvailable();
|
|
10913
|
+
if (!available) {
|
|
10914
|
+
res.status(409).json({
|
|
10915
|
+
error: "Managed memory is unavailable for this daemon workspace",
|
|
10916
|
+
code: "managed_memory_unavailable"
|
|
10917
|
+
});
|
|
10918
|
+
return;
|
|
10919
|
+
}
|
|
10920
|
+
} catch (err) {
|
|
10921
|
+
debugLogger2.error("Availability check failed:", err);
|
|
10922
|
+
res.status(500).json({
|
|
10923
|
+
error: "Workspace memory remember failed.",
|
|
10924
|
+
code: "remember_failed"
|
|
10925
|
+
});
|
|
10926
|
+
return;
|
|
10927
|
+
}
|
|
10928
|
+
let task;
|
|
10929
|
+
try {
|
|
10930
|
+
task = deps.lane.enqueue({
|
|
10931
|
+
content: trimmedContent,
|
|
10932
|
+
contextMode: contextModeRaw,
|
|
10933
|
+
...originatorClientId ? { originatorClientId } : {}
|
|
10934
|
+
});
|
|
10935
|
+
} catch (err) {
|
|
10936
|
+
const code = extractRememberErrorCode(err);
|
|
10937
|
+
res.status(publicErrorStatus(code)).json({
|
|
10938
|
+
error: publicErrorMessage(code),
|
|
10939
|
+
code
|
|
10940
|
+
});
|
|
10941
|
+
return;
|
|
10942
|
+
}
|
|
10943
|
+
res.status(202).json(task);
|
|
10944
|
+
}
|
|
10945
|
+
);
|
|
10946
|
+
app.get(
|
|
10947
|
+
"/workspace/memory/remember/:taskId",
|
|
10948
|
+
deps.mutate({ strict: true }),
|
|
10949
|
+
(req, res) => {
|
|
10950
|
+
const requesterClientId = validateOriginatorClientId(deps, req, res);
|
|
10951
|
+
if (requesterClientId === null) return;
|
|
10952
|
+
const task = deps.lane.get(req.params["taskId"], requesterClientId);
|
|
10953
|
+
if (!task) {
|
|
10954
|
+
res.status(404).json({
|
|
10955
|
+
error: "Workspace memory remember task not found",
|
|
10956
|
+
code: "remember_task_not_found"
|
|
10957
|
+
});
|
|
10958
|
+
return;
|
|
10959
|
+
}
|
|
10960
|
+
res.status(200).json(task);
|
|
10961
|
+
}
|
|
10962
|
+
);
|
|
10963
|
+
}
|
|
10964
|
+
__name(mountWorkspaceMemoryRememberRoutes, "mountWorkspaceMemoryRememberRoutes");
|
|
10965
|
+
|
|
10966
|
+
// packages/cli/src/serve/routes/daemon-status.ts
|
|
10967
|
+
init_esbuild_shims();
|
|
10968
|
+
function registerDaemonStatusRoutes(app, deps) {
|
|
10969
|
+
app.get("/daemon/status", async (req, res) => {
|
|
10970
|
+
const detail = parseDaemonStatusDetail(req.query["detail"]);
|
|
10971
|
+
if (!detail.ok || !detail.detail) {
|
|
10972
|
+
res.status(400).json({
|
|
10973
|
+
error: "detail must be one of: summary, full",
|
|
10974
|
+
code: "invalid_detail"
|
|
10975
|
+
});
|
|
10976
|
+
return;
|
|
10977
|
+
}
|
|
10978
|
+
try {
|
|
10979
|
+
res.status(200).json(
|
|
10980
|
+
await buildDaemonStatusResponse(detail.detail, {
|
|
10981
|
+
opts: deps.opts,
|
|
10982
|
+
boundWorkspace: deps.boundWorkspace,
|
|
10983
|
+
bridge: deps.bridge,
|
|
10984
|
+
workspace: deps.workspace,
|
|
10985
|
+
daemonLog: deps.daemonLog,
|
|
10986
|
+
startup: deps.startup,
|
|
10987
|
+
qwenCodeVersion: deps.qwenCodeVersion,
|
|
10988
|
+
acpHandle: deps.getAcpHandle(),
|
|
10989
|
+
rateLimiter: deps.getRateLimiter(),
|
|
10990
|
+
getRestSseActive: deps.getRestSseActive,
|
|
10991
|
+
features: deps.currentServeFeatures(),
|
|
10992
|
+
protocolVersions: getServeProtocolVersions(),
|
|
10993
|
+
supportedDeviceFlowProviders: deps.getSupportedDeviceFlowProviders(),
|
|
10994
|
+
deviceFlowRegistry: deps.deviceFlowRegistry,
|
|
10995
|
+
sessionShellCommandEnabled: deps.sessionShellCommandEnabled,
|
|
10996
|
+
getChannelWorkerSnapshot: deps.getChannelWorkerSnapshot
|
|
10997
|
+
})
|
|
10998
|
+
);
|
|
10999
|
+
} catch (err) {
|
|
11000
|
+
writeStderrLine(
|
|
11001
|
+
`qwen serve: /daemon/status failed: ${err instanceof Error ? err.message : String(err)}`
|
|
11002
|
+
);
|
|
11003
|
+
res.status(500).json({
|
|
9996
11004
|
error: "Failed to build daemon status",
|
|
9997
11005
|
code: "daemon_status_failed"
|
|
9998
11006
|
});
|
|
@@ -11225,14 +12233,14 @@ function registerWorkspaceExtensionRoutes(app, deps) {
|
|
|
11225
12233
|
code: "extension_queue_full"
|
|
11226
12234
|
});
|
|
11227
12235
|
}, "sendExtensionQueueFull");
|
|
11228
|
-
const withExtensionTimeout = /* @__PURE__ */ __name(async (promise, timeoutMs, operation) => await new Promise((
|
|
12236
|
+
const withExtensionTimeout = /* @__PURE__ */ __name(async (promise, timeoutMs, operation) => await new Promise((resolve5, reject) => {
|
|
11229
12237
|
const timeout = setTimeout(() => {
|
|
11230
12238
|
reject(new Error(`${operation} timed out after ${timeoutMs}ms`));
|
|
11231
12239
|
}, timeoutMs);
|
|
11232
12240
|
promise.then(
|
|
11233
12241
|
(value) => {
|
|
11234
12242
|
clearTimeout(timeout);
|
|
11235
|
-
|
|
12243
|
+
resolve5(value);
|
|
11236
12244
|
},
|
|
11237
12245
|
(err) => {
|
|
11238
12246
|
clearTimeout(timeout);
|
|
@@ -12348,7 +13356,7 @@ __name(registerPermissionRoutes, "registerPermissionRoutes");
|
|
|
12348
13356
|
// packages/cli/src/serve/routes/session.ts
|
|
12349
13357
|
init_esbuild_shims();
|
|
12350
13358
|
import * as crypto2 from "node:crypto";
|
|
12351
|
-
import * as
|
|
13359
|
+
import * as path11 from "node:path";
|
|
12352
13360
|
|
|
12353
13361
|
// packages/cli/src/serve/server/prompt-deadline.ts
|
|
12354
13362
|
init_esbuild_shims();
|
|
@@ -12407,9 +13415,11 @@ async function listWorkspaceSessionsForResponse(bridge, workspaceCwd, options) {
|
|
|
12407
13415
|
}
|
|
12408
13416
|
const isFirstPage = numericCursor === void 0;
|
|
12409
13417
|
const sessionService = new SessionService(workspaceCwd);
|
|
13418
|
+
const archiveState = options?.archiveState ?? "active";
|
|
12410
13419
|
const persisted = await sessionService.listSessions({
|
|
12411
13420
|
cursor: numericCursor,
|
|
12412
|
-
size: pageSize
|
|
13421
|
+
size: pageSize,
|
|
13422
|
+
archiveState
|
|
12413
13423
|
});
|
|
12414
13424
|
const bySessionId = /* @__PURE__ */ new Map();
|
|
12415
13425
|
for (const item of persisted.items) {
|
|
@@ -12420,9 +13430,15 @@ async function listWorkspaceSessionsForResponse(bridge, workspaceCwd, options) {
|
|
|
12420
13430
|
updatedAt: new Date(item.mtime).toISOString(),
|
|
12421
13431
|
displayName: item.customTitle || item.prompt,
|
|
12422
13432
|
clientCount: 0,
|
|
12423
|
-
hasActivePrompt: false
|
|
13433
|
+
hasActivePrompt: false,
|
|
13434
|
+
isArchived: item.isArchived === true
|
|
12424
13435
|
});
|
|
12425
13436
|
}
|
|
13437
|
+
if (archiveState === "archived") {
|
|
13438
|
+
const sessions2 = [...bySessionId.values()];
|
|
13439
|
+
const nextCursor2 = persisted.nextCursor != null ? String(persisted.nextCursor) : void 0;
|
|
13440
|
+
return { sessions: sessions2, nextCursor: nextCursor2 };
|
|
13441
|
+
}
|
|
12426
13442
|
const liveSessions = bridge.listWorkspaceSessions(workspaceCwd);
|
|
12427
13443
|
for (const live of liveSessions) {
|
|
12428
13444
|
const existing = bySessionId.get(live.sessionId);
|
|
@@ -12434,14 +13450,16 @@ async function listWorkspaceSessionsForResponse(bridge, workspaceCwd, options) {
|
|
|
12434
13450
|
displayName: live.displayName ?? existing.displayName,
|
|
12435
13451
|
updatedAt: live.updatedAt ?? existing.updatedAt,
|
|
12436
13452
|
clientCount: live.clientCount,
|
|
12437
|
-
hasActivePrompt: live.hasActivePrompt
|
|
13453
|
+
hasActivePrompt: live.hasActivePrompt,
|
|
13454
|
+
isArchived: false
|
|
12438
13455
|
});
|
|
12439
13456
|
} else if (isFirstPage && !await sessionService.sessionExists(live.sessionId)) {
|
|
12440
13457
|
bySessionId.set(live.sessionId, {
|
|
12441
13458
|
...live,
|
|
12442
13459
|
createdAt: live.createdAt,
|
|
12443
13460
|
clientCount: live.clientCount,
|
|
12444
|
-
hasActivePrompt: live.hasActivePrompt
|
|
13461
|
+
hasActivePrompt: live.hasActivePrompt,
|
|
13462
|
+
isArchived: false
|
|
12445
13463
|
});
|
|
12446
13464
|
}
|
|
12447
13465
|
}
|
|
@@ -12470,6 +13488,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
12470
13488
|
const {
|
|
12471
13489
|
boundWorkspace,
|
|
12472
13490
|
bridge,
|
|
13491
|
+
archiveCoordinator,
|
|
12473
13492
|
mutate,
|
|
12474
13493
|
sendBridgeError: sendBridgeError2,
|
|
12475
13494
|
daemonLog,
|
|
@@ -12477,6 +13496,33 @@ function registerSessionRoutes(app, deps) {
|
|
|
12477
13496
|
sessionShellCommandEnabled
|
|
12478
13497
|
} = deps;
|
|
12479
13498
|
const LANGUAGE_CODES = deps.languageCodes;
|
|
13499
|
+
const parseSessionIdsBody = /* @__PURE__ */ __name((req, res) => {
|
|
13500
|
+
const body = safeBody(req);
|
|
13501
|
+
const sessionIds = body["sessionIds"];
|
|
13502
|
+
if (!Array.isArray(sessionIds) || sessionIds.length === 0 || sessionIds.length > 100 || !sessionIds.every((id) => typeof id === "string")) {
|
|
13503
|
+
res.status(400).json({
|
|
13504
|
+
error: "`sessionIds` must be a non-empty string array (max 100)",
|
|
13505
|
+
code: "invalid_request"
|
|
13506
|
+
});
|
|
13507
|
+
return void 0;
|
|
13508
|
+
}
|
|
13509
|
+
return [...new Set(sessionIds)];
|
|
13510
|
+
}, "parseSessionIdsBody");
|
|
13511
|
+
const serializeSessionErrors = /* @__PURE__ */ __name((errors) => errors.map((e) => ({
|
|
13512
|
+
sessionId: e.sessionId,
|
|
13513
|
+
error: e.error instanceof Error ? e.error.message : String(e.error)
|
|
13514
|
+
})), "serializeSessionErrors");
|
|
13515
|
+
const withMutableSession = /* @__PURE__ */ __name((route, handler) => async (req, res) => {
|
|
13516
|
+
const sessionId = requireSessionId(req, res);
|
|
13517
|
+
if (sessionId === null) return;
|
|
13518
|
+
try {
|
|
13519
|
+
await archiveCoordinator.runSharedMany([sessionId], async () => {
|
|
13520
|
+
await handler(req, res, sessionId);
|
|
13521
|
+
});
|
|
13522
|
+
} catch (err) {
|
|
13523
|
+
sendBridgeError2(res, err, { route, sessionId });
|
|
13524
|
+
}
|
|
13525
|
+
}, "withMutableSession");
|
|
12480
13526
|
app.post("/session", mutate(), async (req, res) => {
|
|
12481
13527
|
const body = safeBody(req);
|
|
12482
13528
|
const cwd = parseOptionalWorkspaceCwd(body, boundWorkspace, res);
|
|
@@ -12542,15 +13588,21 @@ function registerSessionRoutes(app, deps) {
|
|
|
12542
13588
|
const clientId = parseClientIdHeader(req, res);
|
|
12543
13589
|
if (clientId === null) return;
|
|
12544
13590
|
try {
|
|
12545
|
-
const session =
|
|
12546
|
-
sessionId,
|
|
12547
|
-
|
|
12548
|
-
|
|
12549
|
-
|
|
12550
|
-
|
|
12551
|
-
|
|
12552
|
-
|
|
12553
|
-
|
|
13591
|
+
const session = await archiveCoordinator.runSharedMany(
|
|
13592
|
+
[sessionId],
|
|
13593
|
+
async () => {
|
|
13594
|
+
await assertSessionLoadable(cwd, sessionId);
|
|
13595
|
+
return action === "load" ? await bridge.loadSession({
|
|
13596
|
+
sessionId,
|
|
13597
|
+
workspaceCwd: cwd,
|
|
13598
|
+
...clientId !== void 0 ? { clientId } : {}
|
|
13599
|
+
}) : await bridge.resumeSession({
|
|
13600
|
+
sessionId,
|
|
13601
|
+
workspaceCwd: cwd,
|
|
13602
|
+
...clientId !== void 0 ? { clientId } : {}
|
|
13603
|
+
});
|
|
13604
|
+
}
|
|
13605
|
+
);
|
|
12554
13606
|
if (daemonLog) {
|
|
12555
13607
|
daemonLog.info(
|
|
12556
13608
|
`session ${action}${session.attached ? " (attached)" : ""}`,
|
|
@@ -12577,99 +13629,90 @@ function registerSessionRoutes(app, deps) {
|
|
|
12577
13629
|
}, "restoreSessionHandler");
|
|
12578
13630
|
app.post("/session/:id/load", mutate(), restoreSessionHandler("load"));
|
|
12579
13631
|
app.post("/session/:id/resume", mutate(), restoreSessionHandler("resume"));
|
|
12580
|
-
app.post(
|
|
12581
|
-
|
|
12582
|
-
|
|
12583
|
-
|
|
12584
|
-
|
|
12585
|
-
|
|
12586
|
-
|
|
12587
|
-
|
|
12588
|
-
|
|
13632
|
+
app.post(
|
|
13633
|
+
"/session/:id/branch",
|
|
13634
|
+
mutate(),
|
|
13635
|
+
withMutableSession(
|
|
13636
|
+
"POST /session/:id/branch",
|
|
13637
|
+
async (req, res, sessionId) => {
|
|
13638
|
+
const body = safeBody(req);
|
|
13639
|
+
let name = typeof body?.["name"] === "string" ? body["name"] : void 0;
|
|
13640
|
+
if (name) {
|
|
13641
|
+
name = name.replace(/[\x00-\x1F\x7F-\x9F]/g, "");
|
|
13642
|
+
if (name.length > 200) {
|
|
13643
|
+
name = name.slice(0, 200);
|
|
13644
|
+
}
|
|
13645
|
+
}
|
|
13646
|
+
const clientId = parseClientIdHeader(req, res);
|
|
13647
|
+
if (clientId === null) return;
|
|
13648
|
+
const result = await bridge.branchSession(
|
|
13649
|
+
sessionId,
|
|
13650
|
+
{ name },
|
|
13651
|
+
{ clientId }
|
|
13652
|
+
);
|
|
13653
|
+
if (!res.writable) {
|
|
13654
|
+
if (!result.attached) {
|
|
13655
|
+
bridge.killSession(result.sessionId, { requireZeroAttaches: true }).catch(() => {
|
|
13656
|
+
});
|
|
13657
|
+
} else {
|
|
13658
|
+
bridge.detachClient(result.sessionId, result.clientId).catch(() => {
|
|
13659
|
+
});
|
|
13660
|
+
}
|
|
13661
|
+
return;
|
|
13662
|
+
}
|
|
13663
|
+
res.status(201).json(result);
|
|
12589
13664
|
}
|
|
12590
|
-
|
|
12591
|
-
|
|
12592
|
-
|
|
12593
|
-
|
|
12594
|
-
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
|
|
12598
|
-
|
|
12599
|
-
|
|
12600
|
-
if (
|
|
12601
|
-
|
|
12602
|
-
|
|
12603
|
-
|
|
12604
|
-
bridge.detachClient(result.sessionId, result.clientId).catch(() => {
|
|
13665
|
+
)
|
|
13666
|
+
);
|
|
13667
|
+
app.post(
|
|
13668
|
+
"/session/:id/fork",
|
|
13669
|
+
mutate(),
|
|
13670
|
+
withMutableSession(
|
|
13671
|
+
"POST /session/:id/fork",
|
|
13672
|
+
async (req, res, sessionId) => {
|
|
13673
|
+
const body = safeBody(req);
|
|
13674
|
+
const directive = body["directive"];
|
|
13675
|
+
if (typeof directive !== "string" || directive.trim().length === 0) {
|
|
13676
|
+
res.status(400).json({
|
|
13677
|
+
error: "`directive` is required and must be a non-empty string",
|
|
13678
|
+
code: "missing_directive"
|
|
12605
13679
|
});
|
|
13680
|
+
return;
|
|
12606
13681
|
}
|
|
13682
|
+
const clientId = parseClientIdHeader(req, res);
|
|
13683
|
+
if (clientId === null) return;
|
|
13684
|
+
const result = await bridge.launchSessionForkAgent(
|
|
13685
|
+
sessionId,
|
|
13686
|
+
directive,
|
|
13687
|
+
clientId !== void 0 ? { clientId } : void 0
|
|
13688
|
+
);
|
|
13689
|
+
res.status(202).json(result);
|
|
13690
|
+
}
|
|
13691
|
+
)
|
|
13692
|
+
);
|
|
13693
|
+
app.post(
|
|
13694
|
+
"/session/:id/cd",
|
|
13695
|
+
mutate(),
|
|
13696
|
+
withMutableSession("POST /session/:id/cd", async (req, res, sessionId) => {
|
|
13697
|
+
const body = safeBody(req);
|
|
13698
|
+
const targetPath = body["path"];
|
|
13699
|
+
if (typeof targetPath !== "string" || targetPath.length === 0 || !path11.isAbsolute(targetPath)) {
|
|
13700
|
+
res.status(400).json({
|
|
13701
|
+
error: "`path` is required and must be an absolute path",
|
|
13702
|
+
code: "invalid_path"
|
|
13703
|
+
});
|
|
12607
13704
|
return;
|
|
12608
13705
|
}
|
|
12609
|
-
res
|
|
12610
|
-
|
|
12611
|
-
sendBridgeError2(res, err, {
|
|
12612
|
-
route: "POST /session/:id/branch",
|
|
12613
|
-
sessionId
|
|
12614
|
-
});
|
|
12615
|
-
}
|
|
12616
|
-
});
|
|
12617
|
-
app.post("/session/:id/fork", mutate(), async (req, res) => {
|
|
12618
|
-
const sessionId = requireSessionId(req, res);
|
|
12619
|
-
if (sessionId === null) return;
|
|
12620
|
-
const body = safeBody(req);
|
|
12621
|
-
const directive = body["directive"];
|
|
12622
|
-
if (typeof directive !== "string" || directive.trim().length === 0) {
|
|
12623
|
-
res.status(400).json({
|
|
12624
|
-
error: "`directive` is required and must be a non-empty string",
|
|
12625
|
-
code: "missing_directive"
|
|
12626
|
-
});
|
|
12627
|
-
return;
|
|
12628
|
-
}
|
|
12629
|
-
const clientId = parseClientIdHeader(req, res);
|
|
12630
|
-
if (clientId === null) return;
|
|
12631
|
-
try {
|
|
12632
|
-
const result = await bridge.launchSessionForkAgent(
|
|
12633
|
-
sessionId,
|
|
12634
|
-
directive,
|
|
12635
|
-
clientId !== void 0 ? { clientId } : void 0
|
|
12636
|
-
);
|
|
12637
|
-
res.status(202).json(result);
|
|
12638
|
-
} catch (err) {
|
|
12639
|
-
sendBridgeError2(res, err, {
|
|
12640
|
-
route: "POST /session/:id/fork",
|
|
12641
|
-
sessionId
|
|
12642
|
-
});
|
|
12643
|
-
}
|
|
12644
|
-
});
|
|
12645
|
-
app.post("/session/:id/cd", mutate(), async (req, res) => {
|
|
12646
|
-
const sessionId = requireSessionId(req, res);
|
|
12647
|
-
if (sessionId === null) return;
|
|
12648
|
-
const body = safeBody(req);
|
|
12649
|
-
const targetPath = body["path"];
|
|
12650
|
-
if (typeof targetPath !== "string" || targetPath.length === 0 || !path10.isAbsolute(targetPath)) {
|
|
12651
|
-
res.status(400).json({
|
|
12652
|
-
error: "`path` is required and must be an absolute path",
|
|
12653
|
-
code: "invalid_path"
|
|
12654
|
-
});
|
|
12655
|
-
return;
|
|
12656
|
-
}
|
|
12657
|
-
const clientId = parseClientIdHeader(req, res);
|
|
12658
|
-
if (clientId === null) return;
|
|
12659
|
-
try {
|
|
13706
|
+
const clientId = parseClientIdHeader(req, res);
|
|
13707
|
+
if (clientId === null) return;
|
|
12660
13708
|
const result = await bridge.changeSessionCwd(
|
|
12661
13709
|
sessionId,
|
|
12662
13710
|
{ path: targetPath },
|
|
12663
13711
|
clientId !== void 0 ? { clientId } : void 0
|
|
12664
13712
|
);
|
|
12665
13713
|
res.status(200).json(result);
|
|
12666
|
-
}
|
|
12667
|
-
|
|
12668
|
-
route: "POST /session/:id/cd",
|
|
12669
|
-
sessionId
|
|
12670
|
-
});
|
|
12671
|
-
}
|
|
12672
|
-
});
|
|
13714
|
+
})
|
|
13715
|
+
);
|
|
12673
13716
|
app.get("/session/:id/status", (req, res) => {
|
|
12674
13717
|
const sessionId = requireSessionId(req, res);
|
|
12675
13718
|
if (sessionId === null) return;
|
|
@@ -12770,283 +13813,252 @@ function registerSessionRoutes(app, deps) {
|
|
|
12770
13813
|
app.post(
|
|
12771
13814
|
"/session/:id/tasks/:taskId/cancel",
|
|
12772
13815
|
mutate({ strict: true }),
|
|
12773
|
-
|
|
12774
|
-
|
|
12775
|
-
|
|
12776
|
-
|
|
12777
|
-
|
|
12778
|
-
|
|
12779
|
-
|
|
12780
|
-
|
|
12781
|
-
|
|
12782
|
-
|
|
12783
|
-
|
|
12784
|
-
|
|
12785
|
-
|
|
12786
|
-
|
|
12787
|
-
|
|
12788
|
-
|
|
13816
|
+
withMutableSession(
|
|
13817
|
+
"POST /session/:id/tasks/:taskId/cancel",
|
|
13818
|
+
async (req, res, sessionId) => {
|
|
13819
|
+
const taskId = req.params["taskId"];
|
|
13820
|
+
if (!taskId) {
|
|
13821
|
+
res.status(400).json({
|
|
13822
|
+
error: "`taskId` route parameter is required"
|
|
13823
|
+
});
|
|
13824
|
+
return;
|
|
13825
|
+
}
|
|
13826
|
+
const body = safeBody(req);
|
|
13827
|
+
const kind = body["kind"];
|
|
13828
|
+
if (kind !== "agent" && kind !== "shell" && kind !== "monitor") {
|
|
13829
|
+
res.status(400).json({ error: '`kind` must be "agent", "shell", or "monitor"' });
|
|
13830
|
+
return;
|
|
13831
|
+
}
|
|
12789
13832
|
res.status(200).json(await bridge.cancelSessionTask(sessionId, taskId, kind));
|
|
12790
|
-
} catch (err) {
|
|
12791
|
-
sendBridgeError2(res, err, {
|
|
12792
|
-
route: "POST /session/:id/tasks/:taskId/cancel",
|
|
12793
|
-
sessionId
|
|
12794
|
-
});
|
|
12795
13833
|
}
|
|
12796
|
-
|
|
13834
|
+
)
|
|
12797
13835
|
);
|
|
12798
13836
|
app.post(
|
|
12799
13837
|
"/session/:id/goal/clear",
|
|
12800
13838
|
mutate({ strict: true }),
|
|
12801
|
-
|
|
12802
|
-
|
|
12803
|
-
|
|
12804
|
-
res.status(400).json({ error: "`sessionId` route parameter is required" });
|
|
12805
|
-
return;
|
|
12806
|
-
}
|
|
12807
|
-
try {
|
|
13839
|
+
withMutableSession(
|
|
13840
|
+
"POST /session/:id/goal/clear",
|
|
13841
|
+
async (_req, res, sessionId) => {
|
|
12808
13842
|
res.status(200).json(await bridge.clearSessionGoal(sessionId));
|
|
12809
|
-
} catch (err) {
|
|
12810
|
-
sendBridgeError2(res, err, {
|
|
12811
|
-
route: "POST /session/:id/goal/clear",
|
|
12812
|
-
sessionId
|
|
12813
|
-
});
|
|
12814
13843
|
}
|
|
12815
|
-
|
|
13844
|
+
)
|
|
12816
13845
|
);
|
|
12817
13846
|
app.post(
|
|
12818
13847
|
"/session/:id/continue",
|
|
12819
13848
|
mutate({ strict: true }),
|
|
12820
|
-
|
|
12821
|
-
|
|
12822
|
-
|
|
12823
|
-
|
|
12824
|
-
return;
|
|
12825
|
-
|
|
12826
|
-
const clientId = parseClientIdHeader(req, res);
|
|
12827
|
-
if (clientId === null) return;
|
|
12828
|
-
const promptId = crypto2.randomUUID();
|
|
12829
|
-
try {
|
|
13849
|
+
withMutableSession(
|
|
13850
|
+
"POST /session/:id/continue",
|
|
13851
|
+
async (req, res, sessionId) => {
|
|
13852
|
+
const clientId = parseClientIdHeader(req, res);
|
|
13853
|
+
if (clientId === null) return;
|
|
13854
|
+
const promptId = crypto2.randomUUID();
|
|
12830
13855
|
res.status(200).json(
|
|
12831
13856
|
await bridge.continueSession(sessionId, {
|
|
12832
13857
|
...clientId !== void 0 ? { clientId } : {},
|
|
12833
13858
|
promptId
|
|
12834
13859
|
})
|
|
12835
13860
|
);
|
|
12836
|
-
} catch (err) {
|
|
12837
|
-
sendBridgeError2(res, err, {
|
|
12838
|
-
route: "POST /session/:id/continue",
|
|
12839
|
-
sessionId
|
|
12840
|
-
});
|
|
12841
13861
|
}
|
|
12842
|
-
|
|
13862
|
+
)
|
|
12843
13863
|
);
|
|
12844
|
-
app.post(
|
|
12845
|
-
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
12849
|
-
res
|
|
12850
|
-
|
|
12851
|
-
|
|
12852
|
-
|
|
12853
|
-
|
|
12854
|
-
|
|
12855
|
-
|
|
12856
|
-
|
|
12857
|
-
res.status(400).json({
|
|
12858
|
-
error: "each `prompt` element must be an object (content block)"
|
|
12859
|
-
});
|
|
12860
|
-
return;
|
|
12861
|
-
}
|
|
12862
|
-
const rawRequestDeadline = body["deadlineMs"];
|
|
12863
|
-
let requestDeadlineMs;
|
|
12864
|
-
if (rawRequestDeadline !== void 0 && rawRequestDeadline !== null) {
|
|
12865
|
-
if (typeof rawRequestDeadline !== "number" || !Number.isFinite(rawRequestDeadline) || !Number.isInteger(rawRequestDeadline) || rawRequestDeadline <= 0) {
|
|
12866
|
-
res.status(400).json({
|
|
12867
|
-
error: "`deadlineMs` must be a positive integer (milliseconds)",
|
|
12868
|
-
code: "invalid_deadline_ms"
|
|
12869
|
-
});
|
|
12870
|
-
return;
|
|
12871
|
-
}
|
|
12872
|
-
requestDeadlineMs = rawRequestDeadline;
|
|
12873
|
-
}
|
|
12874
|
-
const clientId = parseClientIdHeader(req, res);
|
|
12875
|
-
if (clientId === null) return;
|
|
12876
|
-
const promptId = crypto2.randomUUID();
|
|
12877
|
-
const forwardedBody = { ...body };
|
|
12878
|
-
delete forwardedBody["deadlineMs"];
|
|
12879
|
-
let lastEventId;
|
|
12880
|
-
try {
|
|
12881
|
-
lastEventId = bridge.getSessionLastEventId(sessionId);
|
|
12882
|
-
} catch (err) {
|
|
12883
|
-
sendBridgeError2(res, err, {
|
|
12884
|
-
route: "POST /session/:id/prompt",
|
|
12885
|
-
sessionId
|
|
12886
|
-
});
|
|
12887
|
-
return;
|
|
12888
|
-
}
|
|
12889
|
-
addDaemonRequestAttribute("qwen-code.prompt_id", promptId);
|
|
12890
|
-
const abort = new AbortController();
|
|
12891
|
-
let responseFinished = false;
|
|
12892
|
-
const onResClose = /* @__PURE__ */ __name(() => {
|
|
12893
|
-
if (!responseFinished) abort.abort();
|
|
12894
|
-
}, "onResClose");
|
|
12895
|
-
const onResFinish = /* @__PURE__ */ __name(() => {
|
|
12896
|
-
responseFinished = true;
|
|
12897
|
-
res.off("close", onResClose);
|
|
12898
|
-
}, "onResFinish");
|
|
12899
|
-
res.once("close", onResClose);
|
|
12900
|
-
res.once("finish", onResFinish);
|
|
12901
|
-
const effectiveDeadlineMs = resolvePromptDeadlineMs(
|
|
12902
|
-
promptDeadlineMs,
|
|
12903
|
-
requestDeadlineMs
|
|
12904
|
-
);
|
|
12905
|
-
let deadlineTimer;
|
|
12906
|
-
if (effectiveDeadlineMs !== void 0) {
|
|
12907
|
-
deadlineTimer = setTimeout(() => {
|
|
12908
|
-
if (!abort.signal.aborted) {
|
|
12909
|
-
abort.abort(new PromptDeadlineExceededError(effectiveDeadlineMs));
|
|
13864
|
+
app.post(
|
|
13865
|
+
"/session/:id/prompt",
|
|
13866
|
+
mutate(),
|
|
13867
|
+
withMutableSession(
|
|
13868
|
+
"POST /session/:id/prompt",
|
|
13869
|
+
async (req, res, sessionId) => {
|
|
13870
|
+
const body = safeBody(req);
|
|
13871
|
+
const prompt = body["prompt"];
|
|
13872
|
+
if (!Array.isArray(prompt) || prompt.length === 0) {
|
|
13873
|
+
res.status(400).json({
|
|
13874
|
+
error: "`prompt` is required and must be a non-empty array of content blocks"
|
|
13875
|
+
});
|
|
13876
|
+
return;
|
|
12910
13877
|
}
|
|
12911
|
-
|
|
12912
|
-
|
|
12913
|
-
|
|
12914
|
-
|
|
12915
|
-
|
|
12916
|
-
|
|
12917
|
-
|
|
12918
|
-
{
|
|
12919
|
-
...forwardedBody,
|
|
12920
|
-
sessionId,
|
|
12921
|
-
prompt
|
|
12922
|
-
},
|
|
12923
|
-
abort.signal,
|
|
12924
|
-
{
|
|
12925
|
-
...clientId !== void 0 ? { clientId } : {},
|
|
12926
|
-
promptId
|
|
13878
|
+
if (!prompt.every(
|
|
13879
|
+
(item) => typeof item === "object" && item !== null && !Array.isArray(item)
|
|
13880
|
+
)) {
|
|
13881
|
+
res.status(400).json({
|
|
13882
|
+
error: "each `prompt` element must be an object (content block)"
|
|
13883
|
+
});
|
|
13884
|
+
return;
|
|
12927
13885
|
}
|
|
12928
|
-
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
|
|
12933
|
-
|
|
12934
|
-
|
|
12935
|
-
|
|
12936
|
-
|
|
12937
|
-
|
|
12938
|
-
|
|
12939
|
-
|
|
12940
|
-
|
|
12941
|
-
|
|
12942
|
-
|
|
12943
|
-
|
|
12944
|
-
|
|
12945
|
-
|
|
12946
|
-
|
|
12947
|
-
|
|
12948
|
-
|
|
12949
|
-
|
|
12950
|
-
|
|
12951
|
-
|
|
12952
|
-
|
|
12953
|
-
|
|
12954
|
-
|
|
12955
|
-
|
|
12956
|
-
|
|
12957
|
-
|
|
12958
|
-
|
|
12959
|
-
|
|
13886
|
+
const rawRequestDeadline = body["deadlineMs"];
|
|
13887
|
+
let requestDeadlineMs;
|
|
13888
|
+
if (rawRequestDeadline !== void 0 && rawRequestDeadline !== null) {
|
|
13889
|
+
if (typeof rawRequestDeadline !== "number" || !Number.isFinite(rawRequestDeadline) || !Number.isInteger(rawRequestDeadline) || rawRequestDeadline <= 0) {
|
|
13890
|
+
res.status(400).json({
|
|
13891
|
+
error: "`deadlineMs` must be a positive integer (milliseconds)",
|
|
13892
|
+
code: "invalid_deadline_ms"
|
|
13893
|
+
});
|
|
13894
|
+
return;
|
|
13895
|
+
}
|
|
13896
|
+
requestDeadlineMs = rawRequestDeadline;
|
|
13897
|
+
}
|
|
13898
|
+
const clientId = parseClientIdHeader(req, res);
|
|
13899
|
+
if (clientId === null) return;
|
|
13900
|
+
const promptId = crypto2.randomUUID();
|
|
13901
|
+
const forwardedBody = { ...body };
|
|
13902
|
+
delete forwardedBody["deadlineMs"];
|
|
13903
|
+
const lastEventId = bridge.getSessionLastEventId(sessionId);
|
|
13904
|
+
addDaemonRequestAttribute("qwen-code.prompt_id", promptId);
|
|
13905
|
+
const abort = new AbortController();
|
|
13906
|
+
let responseFinished = false;
|
|
13907
|
+
const onResClose = /* @__PURE__ */ __name(() => {
|
|
13908
|
+
if (!responseFinished) abort.abort();
|
|
13909
|
+
}, "onResClose");
|
|
13910
|
+
const onResFinish = /* @__PURE__ */ __name(() => {
|
|
13911
|
+
responseFinished = true;
|
|
13912
|
+
res.off("close", onResClose);
|
|
13913
|
+
}, "onResFinish");
|
|
13914
|
+
res.once("close", onResClose);
|
|
13915
|
+
res.once("finish", onResFinish);
|
|
13916
|
+
const effectiveDeadlineMs = resolvePromptDeadlineMs(
|
|
13917
|
+
promptDeadlineMs,
|
|
13918
|
+
requestDeadlineMs
|
|
13919
|
+
);
|
|
13920
|
+
let deadlineTimer;
|
|
13921
|
+
if (effectiveDeadlineMs !== void 0) {
|
|
13922
|
+
deadlineTimer = setTimeout(() => {
|
|
13923
|
+
if (!abort.signal.aborted) {
|
|
13924
|
+
abort.abort(new PromptDeadlineExceededError(effectiveDeadlineMs));
|
|
13925
|
+
}
|
|
13926
|
+
}, effectiveDeadlineMs);
|
|
13927
|
+
deadlineTimer.unref();
|
|
13928
|
+
}
|
|
13929
|
+
let promptPromise;
|
|
13930
|
+
try {
|
|
13931
|
+
promptPromise = bridge.sendPrompt(
|
|
12960
13932
|
sessionId,
|
|
12961
|
-
|
|
12962
|
-
|
|
13933
|
+
{
|
|
13934
|
+
...forwardedBody,
|
|
13935
|
+
sessionId,
|
|
13936
|
+
prompt
|
|
13937
|
+
},
|
|
13938
|
+
abort.signal,
|
|
13939
|
+
{
|
|
13940
|
+
...clientId !== void 0 ? { clientId } : {},
|
|
13941
|
+
promptId
|
|
13942
|
+
}
|
|
13943
|
+
);
|
|
13944
|
+
} catch (err) {
|
|
13945
|
+
if (deadlineTimer !== void 0) clearTimeout(deadlineTimer);
|
|
13946
|
+
res.off("close", onResClose);
|
|
13947
|
+
res.off("finish", onResFinish);
|
|
13948
|
+
if (daemonLog && err instanceof PromptQueueFullError) {
|
|
13949
|
+
daemonLog.warn("prompt admission rejected: queue full", {
|
|
13950
|
+
sessionId,
|
|
13951
|
+
promptId,
|
|
13952
|
+
...clientId !== void 0 ? { clientId } : {},
|
|
13953
|
+
limit: err.limit,
|
|
13954
|
+
pendingCount: err.pendingCount
|
|
13955
|
+
});
|
|
13956
|
+
}
|
|
13957
|
+
if (daemonLog && err instanceof InvalidClientIdError) {
|
|
13958
|
+
daemonLog.warn("prompt admission rejected: invalid client id", {
|
|
13959
|
+
sessionId,
|
|
13960
|
+
promptId,
|
|
13961
|
+
...clientId !== void 0 ? { clientId } : {}
|
|
13962
|
+
});
|
|
13963
|
+
}
|
|
13964
|
+
sendBridgeError2(res, err, {
|
|
13965
|
+
route: "POST /session/:id/prompt",
|
|
13966
|
+
sessionId
|
|
12963
13967
|
});
|
|
13968
|
+
return;
|
|
12964
13969
|
}
|
|
12965
|
-
|
|
12966
|
-
|
|
13970
|
+
res.off("close", onResClose);
|
|
13971
|
+
promptPromise.then(
|
|
13972
|
+
() => {
|
|
13973
|
+
if (daemonLog) {
|
|
13974
|
+
daemonLog.info("prompt turn completed", {
|
|
13975
|
+
sessionId,
|
|
13976
|
+
promptId,
|
|
13977
|
+
clientId
|
|
13978
|
+
});
|
|
13979
|
+
}
|
|
13980
|
+
},
|
|
13981
|
+
(err) => {
|
|
13982
|
+
if (daemonLog) {
|
|
13983
|
+
const errName = err instanceof Error ? err.name : void 0;
|
|
13984
|
+
daemonLog.warn(
|
|
13985
|
+
`prompt turn failed: ${errName ? `[${errName}] ` : ""}${err instanceof Error ? err.message : String(err)}`,
|
|
13986
|
+
{ sessionId, promptId, clientId }
|
|
13987
|
+
);
|
|
13988
|
+
}
|
|
13989
|
+
}
|
|
13990
|
+
).finally(() => {
|
|
13991
|
+
if (deadlineTimer !== void 0) clearTimeout(deadlineTimer);
|
|
13992
|
+
}).catch(() => {
|
|
13993
|
+
});
|
|
12967
13994
|
if (daemonLog) {
|
|
12968
|
-
|
|
12969
|
-
daemonLog.warn(
|
|
12970
|
-
`prompt turn failed: ${errName ? `[${errName}] ` : ""}${err instanceof Error ? err.message : String(err)}`,
|
|
12971
|
-
{ sessionId, promptId, clientId }
|
|
12972
|
-
);
|
|
13995
|
+
daemonLog.info("prompt enqueued", { sessionId, promptId, clientId });
|
|
12973
13996
|
}
|
|
13997
|
+
res.status(202).json({ promptId, lastEventId });
|
|
12974
13998
|
}
|
|
12975
|
-
)
|
|
12976
|
-
|
|
12977
|
-
|
|
12978
|
-
|
|
12979
|
-
|
|
12980
|
-
|
|
12981
|
-
|
|
12982
|
-
|
|
12983
|
-
});
|
|
12984
|
-
app.post("/session/:id/heartbeat", mutate(), (req, res) => {
|
|
12985
|
-
const sessionId = requireSessionId(req, res);
|
|
12986
|
-
if (sessionId === null) return;
|
|
12987
|
-
const clientId = parseClientIdHeader(req, res);
|
|
12988
|
-
if (clientId === null) return;
|
|
12989
|
-
try {
|
|
13999
|
+
)
|
|
14000
|
+
);
|
|
14001
|
+
app.post(
|
|
14002
|
+
"/session/:id/heartbeat",
|
|
14003
|
+
mutate(),
|
|
14004
|
+
withMutableSession("POST /session/:id/heartbeat", (req, res, sessionId) => {
|
|
14005
|
+
const clientId = parseClientIdHeader(req, res);
|
|
14006
|
+
if (clientId === null) return;
|
|
12990
14007
|
const result = bridge.recordHeartbeat(
|
|
12991
14008
|
sessionId,
|
|
12992
14009
|
clientId !== void 0 ? { clientId } : void 0
|
|
12993
14010
|
);
|
|
12994
14011
|
res.status(200).json(result);
|
|
12995
|
-
}
|
|
12996
|
-
|
|
12997
|
-
|
|
12998
|
-
|
|
12999
|
-
|
|
13000
|
-
|
|
13001
|
-
|
|
13002
|
-
|
|
13003
|
-
|
|
13004
|
-
|
|
13005
|
-
|
|
13006
|
-
|
|
13007
|
-
try {
|
|
13008
|
-
await bridge.detachClient(sessionId, clientId);
|
|
13009
|
-
res.status(204).end();
|
|
13010
|
-
} catch (err) {
|
|
13011
|
-
sendBridgeError2(res, err, {
|
|
13012
|
-
route: "POST /session/:id/detach",
|
|
13013
|
-
sessionId
|
|
13014
|
-
});
|
|
13015
|
-
}
|
|
13016
|
-
});
|
|
13017
|
-
app.post("/session/:id/cancel", mutate(), async (req, res) => {
|
|
13018
|
-
const sessionId = req.params["id"];
|
|
13019
|
-
const body = safeBody(req);
|
|
13020
|
-
const clientId = parseClientIdHeader(req, res);
|
|
13021
|
-
if (clientId === null) return;
|
|
13022
|
-
try {
|
|
13023
|
-
await bridge.cancelSession(
|
|
13024
|
-
sessionId,
|
|
13025
|
-
{
|
|
13026
|
-
...body,
|
|
13027
|
-
sessionId
|
|
13028
|
-
},
|
|
13029
|
-
clientId !== void 0 ? { clientId } : void 0
|
|
13030
|
-
);
|
|
13031
|
-
if (daemonLog) {
|
|
13032
|
-
daemonLog.info("cancel sent", { sessionId, clientId });
|
|
14012
|
+
})
|
|
14013
|
+
);
|
|
14014
|
+
app.post(
|
|
14015
|
+
"/session/:id/detach",
|
|
14016
|
+
mutate(),
|
|
14017
|
+
withMutableSession(
|
|
14018
|
+
"POST /session/:id/detach",
|
|
14019
|
+
async (req, res, sessionId) => {
|
|
14020
|
+
const clientId = parseClientIdHeader(req, res);
|
|
14021
|
+
if (clientId === null) return;
|
|
14022
|
+
await bridge.detachClient(sessionId, clientId);
|
|
14023
|
+
res.status(204).end();
|
|
13033
14024
|
}
|
|
13034
|
-
|
|
13035
|
-
|
|
13036
|
-
|
|
13037
|
-
|
|
13038
|
-
|
|
13039
|
-
|
|
13040
|
-
|
|
13041
|
-
|
|
14025
|
+
)
|
|
14026
|
+
);
|
|
14027
|
+
app.post(
|
|
14028
|
+
"/session/:id/cancel",
|
|
14029
|
+
mutate(),
|
|
14030
|
+
withMutableSession(
|
|
14031
|
+
"POST /session/:id/cancel",
|
|
14032
|
+
async (req, res, sessionId) => {
|
|
14033
|
+
const body = safeBody(req);
|
|
14034
|
+
const clientId = parseClientIdHeader(req, res);
|
|
14035
|
+
if (clientId === null) return;
|
|
14036
|
+
await bridge.cancelSession(
|
|
14037
|
+
sessionId,
|
|
14038
|
+
{
|
|
14039
|
+
...body,
|
|
14040
|
+
sessionId
|
|
14041
|
+
},
|
|
14042
|
+
clientId !== void 0 ? { clientId } : void 0
|
|
14043
|
+
);
|
|
14044
|
+
if (daemonLog) {
|
|
14045
|
+
daemonLog.info("cancel sent", { sessionId, clientId });
|
|
14046
|
+
}
|
|
14047
|
+
res.status(204).end();
|
|
14048
|
+
}
|
|
14049
|
+
)
|
|
14050
|
+
);
|
|
13042
14051
|
app.delete("/session/:id", async (req, res) => {
|
|
13043
14052
|
const sessionId = req.params["id"];
|
|
13044
14053
|
const clientId = parseClientIdHeader(req, res);
|
|
13045
14054
|
if (clientId === null) return;
|
|
13046
14055
|
try {
|
|
13047
|
-
await
|
|
13048
|
-
sessionId,
|
|
13049
|
-
|
|
14056
|
+
await archiveCoordinator.runExclusiveMany(
|
|
14057
|
+
[sessionId],
|
|
14058
|
+
async () => bridge.closeSession(
|
|
14059
|
+
sessionId,
|
|
14060
|
+
clientId !== void 0 ? { clientId } : void 0
|
|
14061
|
+
)
|
|
13050
14062
|
);
|
|
13051
14063
|
res.status(204).end();
|
|
13052
14064
|
} catch (err) {
|
|
@@ -13059,106 +14071,98 @@ function registerSessionRoutes(app, deps) {
|
|
|
13059
14071
|
app.post("/sessions/delete", mutate(), async (req, res) => {
|
|
13060
14072
|
const clientId = parseClientIdHeader(req, res);
|
|
13061
14073
|
if (clientId === null) return;
|
|
13062
|
-
const
|
|
13063
|
-
|
|
13064
|
-
|
|
13065
|
-
|
|
13066
|
-
|
|
13067
|
-
|
|
14074
|
+
const uniqueIds = parseSessionIdsBody(req, res);
|
|
14075
|
+
if (uniqueIds === void 0) return;
|
|
14076
|
+
try {
|
|
14077
|
+
const service = new SessionService(boundWorkspace);
|
|
14078
|
+
const result = await deleteDaemonSessions({
|
|
14079
|
+
sessionIds: uniqueIds,
|
|
14080
|
+
service,
|
|
14081
|
+
bridge,
|
|
14082
|
+
coordinator: archiveCoordinator,
|
|
14083
|
+
onError: /* @__PURE__ */ __name(({ phase, sessionId, error: error2 }) => {
|
|
14084
|
+
writeStderrLine(
|
|
14085
|
+
`qwen serve: ${phase}Session failed for ${safeLogValue(sessionId)}: ${safeLogValue(error2)}`
|
|
14086
|
+
);
|
|
14087
|
+
}, "onError")
|
|
13068
14088
|
});
|
|
13069
|
-
|
|
14089
|
+
res.status(200).json(result);
|
|
14090
|
+
} catch (err) {
|
|
14091
|
+
sendBridgeError2(res, err, { route: "POST /sessions/delete" });
|
|
13070
14092
|
}
|
|
14093
|
+
});
|
|
14094
|
+
app.post("/sessions/archive", mutate(), async (req, res) => {
|
|
14095
|
+
const uniqueIds = parseSessionIdsBody(req, res);
|
|
14096
|
+
if (uniqueIds === void 0) return;
|
|
14097
|
+
const service = new SessionService(boundWorkspace, {
|
|
14098
|
+
onWarning: logSessionArchiveWarning
|
|
14099
|
+
});
|
|
13071
14100
|
try {
|
|
13072
|
-
const
|
|
13073
|
-
|
|
13074
|
-
|
|
13075
|
-
|
|
13076
|
-
|
|
13077
|
-
|
|
13078
|
-
);
|
|
13079
|
-
const closeErrors = [];
|
|
13080
|
-
const closedIds = [];
|
|
13081
|
-
for (let i = 0; i < closeResults.length; i++) {
|
|
13082
|
-
const r = closeResults[i];
|
|
13083
|
-
const id = uniqueIds[i];
|
|
13084
|
-
if (r.status === "fulfilled") {
|
|
13085
|
-
closedIds.push(id);
|
|
13086
|
-
} else {
|
|
13087
|
-
const closeErr = r.reason;
|
|
13088
|
-
if (closeErr instanceof SessionNotFoundError) {
|
|
13089
|
-
closedIds.push(id);
|
|
13090
|
-
} else {
|
|
13091
|
-
const msg = closeErr instanceof Error ? closeErr.message : String(closeErr);
|
|
13092
|
-
writeStderrLine(
|
|
13093
|
-
`qwen serve: closeSession failed for ${safeLogValue(id)}: ${safeLogValue(msg)}`
|
|
13094
|
-
);
|
|
13095
|
-
closeErrors.push({ sessionId: id, error: msg });
|
|
13096
|
-
}
|
|
13097
|
-
}
|
|
13098
|
-
}
|
|
13099
|
-
const result = await new SessionService(boundWorkspace).removeSessions(
|
|
13100
|
-
closedIds
|
|
13101
|
-
);
|
|
13102
|
-
for (const e of result.errors) {
|
|
13103
|
-
const msg = e.error instanceof Error ? e.error.message : String(e.error);
|
|
13104
|
-
writeStderrLine(
|
|
13105
|
-
`qwen serve: removeSession failed for ${safeLogValue(e.sessionId)}: ${safeLogValue(msg)}`
|
|
13106
|
-
);
|
|
13107
|
-
}
|
|
14101
|
+
const result = await archiveDaemonSessions({
|
|
14102
|
+
sessionIds: uniqueIds,
|
|
14103
|
+
service,
|
|
14104
|
+
bridge,
|
|
14105
|
+
coordinator: archiveCoordinator
|
|
14106
|
+
});
|
|
13108
14107
|
res.status(200).json({
|
|
13109
|
-
|
|
14108
|
+
archived: result.archived,
|
|
14109
|
+
alreadyArchived: result.alreadyArchived,
|
|
13110
14110
|
notFound: result.notFound,
|
|
13111
|
-
errors:
|
|
13112
|
-
...closeErrors,
|
|
13113
|
-
...result.errors.map((e) => ({
|
|
13114
|
-
sessionId: e.sessionId,
|
|
13115
|
-
error: e.error instanceof Error ? e.error.message : String(e.error)
|
|
13116
|
-
}))
|
|
13117
|
-
]
|
|
14111
|
+
errors: serializeSessionErrors(result.errors)
|
|
13118
14112
|
});
|
|
13119
14113
|
} catch (err) {
|
|
13120
|
-
|
|
13121
|
-
writeStderrLine(
|
|
13122
|
-
`qwen serve: failed to batch delete sessions: ${safeLogValue(message)}`
|
|
13123
|
-
);
|
|
13124
|
-
res.status(500).json({
|
|
13125
|
-
error: "Failed to delete sessions",
|
|
13126
|
-
code: "sessions_delete_failed"
|
|
13127
|
-
});
|
|
14114
|
+
sendBridgeError2(res, err, { route: "POST /sessions/archive" });
|
|
13128
14115
|
}
|
|
13129
14116
|
});
|
|
13130
|
-
app.
|
|
13131
|
-
const
|
|
13132
|
-
|
|
13133
|
-
const
|
|
13134
|
-
|
|
13135
|
-
|
|
13136
|
-
|
|
13137
|
-
|
|
13138
|
-
|
|
13139
|
-
|
|
13140
|
-
|
|
14117
|
+
app.post("/sessions/unarchive", mutate(), async (req, res) => {
|
|
14118
|
+
const uniqueIds = parseSessionIdsBody(req, res);
|
|
14119
|
+
if (uniqueIds === void 0) return;
|
|
14120
|
+
const service = new SessionService(boundWorkspace, {
|
|
14121
|
+
onWarning: logSessionArchiveWarning
|
|
14122
|
+
});
|
|
14123
|
+
try {
|
|
14124
|
+
const result = await unarchiveDaemonSessions({
|
|
14125
|
+
sessionIds: uniqueIds,
|
|
14126
|
+
service,
|
|
14127
|
+
coordinator: archiveCoordinator
|
|
13141
14128
|
});
|
|
13142
|
-
|
|
14129
|
+
res.status(200).json({
|
|
14130
|
+
unarchived: result.unarchived,
|
|
14131
|
+
alreadyActive: result.alreadyActive,
|
|
14132
|
+
notFound: result.notFound,
|
|
14133
|
+
errors: serializeSessionErrors(result.errors)
|
|
14134
|
+
});
|
|
14135
|
+
} catch (err) {
|
|
14136
|
+
sendBridgeError2(res, err, { route: "POST /sessions/unarchive" });
|
|
13143
14137
|
}
|
|
13144
|
-
|
|
13145
|
-
|
|
14138
|
+
});
|
|
14139
|
+
app.patch(
|
|
14140
|
+
"/session/:id/metadata",
|
|
14141
|
+
withMutableSession("PATCH /session/:id/metadata", (req, res, sessionId) => {
|
|
14142
|
+
const body = safeBody(req);
|
|
14143
|
+
const clientId = parseClientIdHeader(req, res);
|
|
14144
|
+
if (clientId === null) return;
|
|
14145
|
+
const rawDisplayName = body["displayName"];
|
|
14146
|
+
if (rawDisplayName !== void 0 && typeof rawDisplayName !== "string") {
|
|
14147
|
+
res.status(400).json({
|
|
14148
|
+
error: "`displayName` must be a string",
|
|
14149
|
+
code: "invalid_metadata",
|
|
14150
|
+
field: "displayName"
|
|
14151
|
+
});
|
|
14152
|
+
return;
|
|
14153
|
+
}
|
|
14154
|
+
const displayName = typeof rawDisplayName === "string" ? rawDisplayName.slice(0, 256) : void 0;
|
|
13146
14155
|
const effective = bridge.updateSessionMetadata(
|
|
13147
14156
|
sessionId,
|
|
13148
14157
|
{ displayName },
|
|
13149
14158
|
clientId !== void 0 ? { clientId } : void 0
|
|
13150
14159
|
);
|
|
13151
14160
|
res.status(200).json({ sessionId, ...effective });
|
|
13152
|
-
}
|
|
13153
|
-
|
|
13154
|
-
route: "PATCH /session/:id/metadata",
|
|
13155
|
-
sessionId
|
|
13156
|
-
});
|
|
13157
|
-
}
|
|
13158
|
-
});
|
|
14161
|
+
})
|
|
14162
|
+
);
|
|
13159
14163
|
app.get("/workspace/:id/sessions", async (req, res) => {
|
|
13160
14164
|
const workspaceCwd = req.params["id"] ?? "";
|
|
13161
|
-
if (!
|
|
14165
|
+
if (!path11.isAbsolute(workspaceCwd)) {
|
|
13162
14166
|
res.status(400).json({ error: "`:id` must decode to an absolute workspace path" });
|
|
13163
14167
|
return;
|
|
13164
14168
|
}
|
|
@@ -13175,9 +14179,22 @@ function registerSessionRoutes(app, deps) {
|
|
|
13175
14179
|
try {
|
|
13176
14180
|
const cursor = typeof req.query["cursor"] === "string" ? req.query["cursor"] : void 0;
|
|
13177
14181
|
const size = parseSessionPageSizeQuery(req.query["size"]);
|
|
14182
|
+
const rawArchiveState = req.query["archiveState"];
|
|
14183
|
+
let archiveState;
|
|
14184
|
+
if (rawArchiveState !== void 0) {
|
|
14185
|
+
if (typeof rawArchiveState !== "string" || rawArchiveState !== "active" && rawArchiveState !== "archived") {
|
|
14186
|
+
res.status(400).json({
|
|
14187
|
+
error: '`archiveState` must be "active" or "archived"',
|
|
14188
|
+
code: "invalid_archive_state"
|
|
14189
|
+
});
|
|
14190
|
+
return;
|
|
14191
|
+
}
|
|
14192
|
+
archiveState = rawArchiveState;
|
|
14193
|
+
}
|
|
13178
14194
|
const result = await listWorkspaceSessionsForResponse(bridge, key, {
|
|
13179
14195
|
cursor,
|
|
13180
|
-
size
|
|
14196
|
+
size,
|
|
14197
|
+
archiveState
|
|
13181
14198
|
});
|
|
13182
14199
|
res.status(200).json({
|
|
13183
14200
|
sessions: result.sessions,
|
|
@@ -13202,137 +14219,134 @@ function registerSessionRoutes(app, deps) {
|
|
|
13202
14219
|
});
|
|
13203
14220
|
}
|
|
13204
14221
|
});
|
|
13205
|
-
app.post(
|
|
13206
|
-
|
|
13207
|
-
|
|
13208
|
-
|
|
13209
|
-
|
|
13210
|
-
res
|
|
13211
|
-
|
|
13212
|
-
|
|
13213
|
-
|
|
13214
|
-
|
|
13215
|
-
|
|
13216
|
-
|
|
13217
|
-
|
|
13218
|
-
|
|
13219
|
-
|
|
13220
|
-
|
|
13221
|
-
|
|
14222
|
+
app.post(
|
|
14223
|
+
"/session/:id/model",
|
|
14224
|
+
mutate(),
|
|
14225
|
+
withMutableSession(
|
|
14226
|
+
"POST /session/:id/model",
|
|
14227
|
+
async (req, res, sessionId) => {
|
|
14228
|
+
const body = safeBody(req);
|
|
14229
|
+
const modelId = body["modelId"];
|
|
14230
|
+
if (typeof modelId !== "string" || !modelId) {
|
|
14231
|
+
res.status(400).json({
|
|
14232
|
+
error: "`modelId` is required and must be a non-empty string"
|
|
14233
|
+
});
|
|
14234
|
+
return;
|
|
14235
|
+
}
|
|
14236
|
+
const clientId = parseClientIdHeader(req, res);
|
|
14237
|
+
if (clientId === null) return;
|
|
14238
|
+
const response = await bridge.setSessionModel(
|
|
13222
14239
|
sessionId,
|
|
13223
|
-
|
|
13224
|
-
|
|
13225
|
-
|
|
13226
|
-
|
|
13227
|
-
|
|
13228
|
-
|
|
13229
|
-
sendBridgeError2(res, err, {
|
|
13230
|
-
route: "POST /session/:id/model",
|
|
13231
|
-
sessionId
|
|
13232
|
-
});
|
|
13233
|
-
}
|
|
13234
|
-
});
|
|
13235
|
-
app.post("/session/:id/recap", mutate(), async (req, res) => {
|
|
13236
|
-
const sessionId = requireSessionId(req, res);
|
|
13237
|
-
if (sessionId === null) return;
|
|
13238
|
-
const clientId = parseClientIdHeader(req, res);
|
|
13239
|
-
if (clientId === null) return;
|
|
13240
|
-
try {
|
|
13241
|
-
const response = await bridge.generateSessionRecap(
|
|
13242
|
-
sessionId,
|
|
13243
|
-
clientId !== void 0 ? { clientId } : void 0
|
|
13244
|
-
);
|
|
13245
|
-
if (daemonLog) {
|
|
13246
|
-
const recap = response.recap;
|
|
13247
|
-
daemonLog.info(
|
|
13248
|
-
recap ? `recap generated len=${recap.length}` : "recap returned null",
|
|
13249
|
-
{ sessionId, clientId }
|
|
14240
|
+
{
|
|
14241
|
+
...body,
|
|
14242
|
+
sessionId,
|
|
14243
|
+
modelId
|
|
14244
|
+
},
|
|
14245
|
+
clientId !== void 0 ? { clientId } : void 0
|
|
13250
14246
|
);
|
|
14247
|
+
res.status(200).json(response);
|
|
13251
14248
|
}
|
|
13252
|
-
|
|
13253
|
-
|
|
13254
|
-
|
|
13255
|
-
|
|
13256
|
-
|
|
13257
|
-
|
|
13258
|
-
|
|
13259
|
-
|
|
13260
|
-
|
|
13261
|
-
|
|
13262
|
-
|
|
13263
|
-
|
|
13264
|
-
|
|
13265
|
-
|
|
13266
|
-
|
|
13267
|
-
|
|
13268
|
-
|
|
13269
|
-
|
|
13270
|
-
|
|
13271
|
-
|
|
13272
|
-
|
|
13273
|
-
|
|
13274
|
-
|
|
13275
|
-
|
|
13276
|
-
|
|
13277
|
-
|
|
13278
|
-
|
|
13279
|
-
|
|
13280
|
-
|
|
13281
|
-
|
|
13282
|
-
const
|
|
13283
|
-
|
|
13284
|
-
|
|
13285
|
-
|
|
13286
|
-
|
|
13287
|
-
|
|
13288
|
-
|
|
13289
|
-
|
|
13290
|
-
|
|
14249
|
+
)
|
|
14250
|
+
);
|
|
14251
|
+
app.post(
|
|
14252
|
+
"/session/:id/recap",
|
|
14253
|
+
mutate(),
|
|
14254
|
+
withMutableSession(
|
|
14255
|
+
"POST /session/:id/recap",
|
|
14256
|
+
async (req, res, sessionId) => {
|
|
14257
|
+
const clientId = parseClientIdHeader(req, res);
|
|
14258
|
+
if (clientId === null) return;
|
|
14259
|
+
const response = await bridge.generateSessionRecap(
|
|
14260
|
+
sessionId,
|
|
14261
|
+
clientId !== void 0 ? { clientId } : void 0
|
|
14262
|
+
);
|
|
14263
|
+
if (daemonLog) {
|
|
14264
|
+
const recap = response.recap;
|
|
14265
|
+
daemonLog.info(
|
|
14266
|
+
recap ? `recap generated len=${recap.length}` : "recap returned null",
|
|
14267
|
+
{ sessionId, clientId }
|
|
14268
|
+
);
|
|
14269
|
+
}
|
|
14270
|
+
res.status(200).json(response);
|
|
14271
|
+
}
|
|
14272
|
+
)
|
|
14273
|
+
);
|
|
14274
|
+
app.post(
|
|
14275
|
+
"/session/:id/btw",
|
|
14276
|
+
mutate(),
|
|
14277
|
+
withMutableSession("POST /session/:id/btw", async (req, res, sessionId) => {
|
|
14278
|
+
const body = safeBody(req);
|
|
14279
|
+
const question = body["question"];
|
|
14280
|
+
if (typeof question !== "string" || question.trim().length === 0 || question.length > BTW_MAX_INPUT_LENGTH) {
|
|
14281
|
+
res.status(400).json({
|
|
14282
|
+
error: `\`question\` is required, must be a non-empty string, and at most ${BTW_MAX_INPUT_LENGTH} characters`
|
|
14283
|
+
});
|
|
14284
|
+
return;
|
|
14285
|
+
}
|
|
14286
|
+
const abort = new AbortController();
|
|
14287
|
+
const onResClose = /* @__PURE__ */ __name(() => {
|
|
14288
|
+
if (!res.writableEnded) abort.abort();
|
|
14289
|
+
}, "onResClose");
|
|
14290
|
+
res.once("close", onResClose);
|
|
14291
|
+
const clientId = parseClientIdHeader(req, res);
|
|
14292
|
+
if (clientId === null) {
|
|
14293
|
+
res.off("close", onResClose);
|
|
13291
14294
|
return;
|
|
13292
14295
|
}
|
|
13293
|
-
|
|
13294
|
-
|
|
13295
|
-
|
|
13296
|
-
|
|
13297
|
-
|
|
13298
|
-
|
|
13299
|
-
|
|
13300
|
-
|
|
13301
|
-
|
|
13302
|
-
|
|
13303
|
-
|
|
13304
|
-
|
|
13305
|
-
|
|
13306
|
-
|
|
13307
|
-
|
|
13308
|
-
|
|
13309
|
-
|
|
13310
|
-
|
|
13311
|
-
}
|
|
13312
|
-
|
|
13313
|
-
|
|
13314
|
-
|
|
13315
|
-
|
|
13316
|
-
|
|
13317
|
-
|
|
13318
|
-
|
|
13319
|
-
|
|
13320
|
-
|
|
13321
|
-
|
|
13322
|
-
|
|
13323
|
-
|
|
13324
|
-
|
|
13325
|
-
|
|
13326
|
-
|
|
13327
|
-
|
|
13328
|
-
|
|
13329
|
-
|
|
13330
|
-
|
|
13331
|
-
|
|
13332
|
-
|
|
13333
|
-
|
|
13334
|
-
|
|
13335
|
-
|
|
14296
|
+
try {
|
|
14297
|
+
const result = await bridge.generateSessionBtw(
|
|
14298
|
+
sessionId,
|
|
14299
|
+
question.trim(),
|
|
14300
|
+
abort.signal,
|
|
14301
|
+
clientId !== void 0 ? { clientId } : void 0
|
|
14302
|
+
);
|
|
14303
|
+
res.status(200).json(result);
|
|
14304
|
+
} catch (err) {
|
|
14305
|
+
if (err instanceof DOMException && err.name === "AbortError" && abort.signal.aborted) {
|
|
14306
|
+
return;
|
|
14307
|
+
}
|
|
14308
|
+
sendBridgeError2(res, err, {
|
|
14309
|
+
route: "POST /session/:id/btw",
|
|
14310
|
+
sessionId
|
|
14311
|
+
});
|
|
14312
|
+
} finally {
|
|
14313
|
+
res.off("close", onResClose);
|
|
14314
|
+
}
|
|
14315
|
+
})
|
|
14316
|
+
);
|
|
14317
|
+
const MID_TURN_MESSAGE_MAX_LENGTH = 16 * 1024;
|
|
14318
|
+
app.post(
|
|
14319
|
+
"/session/:id/mid-turn-message",
|
|
14320
|
+
mutate(),
|
|
14321
|
+
withMutableSession(
|
|
14322
|
+
"POST /session/:id/mid-turn-message",
|
|
14323
|
+
(req, res, sessionId) => {
|
|
14324
|
+
const body = safeBody(req);
|
|
14325
|
+
const message = body["message"];
|
|
14326
|
+
const trimmed = typeof message === "string" ? message.trim() : "";
|
|
14327
|
+
if (trimmed.length === 0) {
|
|
14328
|
+
res.status(400).json({
|
|
14329
|
+
error: "`message` is required and must be a non-empty string"
|
|
14330
|
+
});
|
|
14331
|
+
return;
|
|
14332
|
+
}
|
|
14333
|
+
if (trimmed.length > MID_TURN_MESSAGE_MAX_LENGTH) {
|
|
14334
|
+
res.status(400).json({
|
|
14335
|
+
error: `\`message\` must be at most ${MID_TURN_MESSAGE_MAX_LENGTH} characters`
|
|
14336
|
+
});
|
|
14337
|
+
return;
|
|
14338
|
+
}
|
|
14339
|
+
const clientId = parseClientIdHeader(req, res);
|
|
14340
|
+
if (clientId === null) return;
|
|
14341
|
+
const result = bridge.enqueueMidTurnMessage(
|
|
14342
|
+
sessionId,
|
|
14343
|
+
trimmed,
|
|
14344
|
+
clientId !== void 0 ? { clientId } : void 0
|
|
14345
|
+
);
|
|
14346
|
+
res.status(200).json(result);
|
|
14347
|
+
}
|
|
14348
|
+
)
|
|
14349
|
+
);
|
|
13336
14350
|
app.get("/session/:id/pending-prompts", (req, res) => {
|
|
13337
14351
|
const sessionId = requireSessionId(req, res);
|
|
13338
14352
|
if (sessionId === null) return;
|
|
@@ -13351,90 +14365,94 @@ function registerSessionRoutes(app, deps) {
|
|
|
13351
14365
|
});
|
|
13352
14366
|
}
|
|
13353
14367
|
});
|
|
13354
|
-
app.delete(
|
|
13355
|
-
|
|
13356
|
-
|
|
13357
|
-
|
|
13358
|
-
|
|
13359
|
-
|
|
13360
|
-
|
|
13361
|
-
|
|
13362
|
-
|
|
13363
|
-
|
|
13364
|
-
|
|
13365
|
-
|
|
13366
|
-
|
|
13367
|
-
|
|
13368
|
-
clientId !== void 0 ? { clientId } : void 0
|
|
13369
|
-
);
|
|
13370
|
-
res.status(200).json(result);
|
|
13371
|
-
} catch (err) {
|
|
13372
|
-
sendBridgeError2(res, err, {
|
|
13373
|
-
route: "DELETE /session/:id/pending-prompts/:promptId",
|
|
13374
|
-
sessionId
|
|
13375
|
-
});
|
|
13376
|
-
}
|
|
13377
|
-
});
|
|
13378
|
-
app.post("/session/:id/shell", mutate({ strict: true }), async (req, res) => {
|
|
13379
|
-
const sessionId = req.params["id"];
|
|
13380
|
-
if (!sessionShellCommandEnabled) {
|
|
13381
|
-
sendBridgeError2(res, new SessionShellDisabledError(), {
|
|
13382
|
-
route: "POST /session/:id/shell",
|
|
13383
|
-
sessionId
|
|
13384
|
-
});
|
|
13385
|
-
return;
|
|
13386
|
-
}
|
|
13387
|
-
const clientId = parseClientIdHeader(req, res);
|
|
13388
|
-
if (clientId === null) {
|
|
13389
|
-
return;
|
|
13390
|
-
}
|
|
13391
|
-
if (clientId === void 0) {
|
|
13392
|
-
sendBridgeError2(res, new SessionShellClientRequiredError(), {
|
|
13393
|
-
route: "POST /session/:id/shell",
|
|
13394
|
-
sessionId
|
|
13395
|
-
});
|
|
13396
|
-
return;
|
|
13397
|
-
}
|
|
13398
|
-
const body = safeBody(req);
|
|
13399
|
-
const command = body["command"];
|
|
13400
|
-
if (typeof command !== "string" || command.trim().length === 0) {
|
|
13401
|
-
res.status(400).json({
|
|
13402
|
-
error: "`command` is required and must be a non-empty string"
|
|
13403
|
-
});
|
|
13404
|
-
return;
|
|
13405
|
-
}
|
|
13406
|
-
const abort = new AbortController();
|
|
13407
|
-
const onResClose = /* @__PURE__ */ __name(() => {
|
|
13408
|
-
if (!res.writableEnded) abort.abort();
|
|
13409
|
-
}, "onResClose");
|
|
13410
|
-
res.once("close", onResClose);
|
|
13411
|
-
try {
|
|
13412
|
-
const result = await bridge.executeShellCommand(
|
|
13413
|
-
sessionId,
|
|
13414
|
-
command.trim(),
|
|
13415
|
-
abort.signal,
|
|
13416
|
-
{ clientId }
|
|
13417
|
-
);
|
|
13418
|
-
if (daemonLog) {
|
|
13419
|
-
daemonLog.info("shell command completed", {
|
|
14368
|
+
app.delete(
|
|
14369
|
+
"/session/:id/pending-prompts/:promptId",
|
|
14370
|
+
mutate(),
|
|
14371
|
+
withMutableSession(
|
|
14372
|
+
"DELETE /session/:id/pending-prompts/:promptId",
|
|
14373
|
+
(req, res, sessionId) => {
|
|
14374
|
+
const clientId = parseClientIdHeader(req, res);
|
|
14375
|
+
if (clientId === null) return;
|
|
14376
|
+
const promptId = req.params["promptId"];
|
|
14377
|
+
if (!promptId) {
|
|
14378
|
+
res.status(400).json({ error: "`promptId` route parameter is required" });
|
|
14379
|
+
return;
|
|
14380
|
+
}
|
|
14381
|
+
const result = bridge.removePendingPrompt(
|
|
13420
14382
|
sessionId,
|
|
13421
|
-
|
|
13422
|
-
|
|
13423
|
-
|
|
14383
|
+
promptId,
|
|
14384
|
+
clientId !== void 0 ? { clientId } : void 0
|
|
14385
|
+
);
|
|
14386
|
+
res.status(200).json(result);
|
|
13424
14387
|
}
|
|
13425
|
-
|
|
13426
|
-
|
|
13427
|
-
|
|
13428
|
-
|
|
14388
|
+
)
|
|
14389
|
+
);
|
|
14390
|
+
app.post(
|
|
14391
|
+
"/session/:id/shell",
|
|
14392
|
+
mutate({ strict: true }),
|
|
14393
|
+
withMutableSession(
|
|
14394
|
+
"POST /session/:id/shell",
|
|
14395
|
+
async (req, res, sessionId) => {
|
|
14396
|
+
if (!sessionShellCommandEnabled) {
|
|
14397
|
+
sendBridgeError2(res, new SessionShellDisabledError(), {
|
|
14398
|
+
route: "POST /session/:id/shell",
|
|
14399
|
+
sessionId
|
|
14400
|
+
});
|
|
14401
|
+
return;
|
|
14402
|
+
}
|
|
14403
|
+
const clientId = parseClientIdHeader(req, res);
|
|
14404
|
+
if (clientId === null) {
|
|
14405
|
+
return;
|
|
14406
|
+
}
|
|
14407
|
+
if (clientId === void 0) {
|
|
14408
|
+
sendBridgeError2(res, new SessionShellClientRequiredError(), {
|
|
14409
|
+
route: "POST /session/:id/shell",
|
|
14410
|
+
sessionId
|
|
14411
|
+
});
|
|
14412
|
+
return;
|
|
14413
|
+
}
|
|
14414
|
+
const body = safeBody(req);
|
|
14415
|
+
const command = body["command"];
|
|
14416
|
+
if (typeof command !== "string" || command.trim().length === 0) {
|
|
14417
|
+
res.status(400).json({
|
|
14418
|
+
error: "`command` is required and must be a non-empty string"
|
|
14419
|
+
});
|
|
14420
|
+
return;
|
|
14421
|
+
}
|
|
14422
|
+
const abort = new AbortController();
|
|
14423
|
+
const onResClose = /* @__PURE__ */ __name(() => {
|
|
14424
|
+
if (!res.writableEnded) abort.abort();
|
|
14425
|
+
}, "onResClose");
|
|
14426
|
+
res.once("close", onResClose);
|
|
14427
|
+
try {
|
|
14428
|
+
const result = await bridge.executeShellCommand(
|
|
14429
|
+
sessionId,
|
|
14430
|
+
command.trim(),
|
|
14431
|
+
abort.signal,
|
|
14432
|
+
{ clientId }
|
|
14433
|
+
);
|
|
14434
|
+
if (daemonLog) {
|
|
14435
|
+
daemonLog.info("shell command completed", {
|
|
14436
|
+
sessionId,
|
|
14437
|
+
clientId,
|
|
14438
|
+
exitCode: result.exitCode
|
|
14439
|
+
});
|
|
14440
|
+
}
|
|
14441
|
+
res.status(200).json(result);
|
|
14442
|
+
} catch (err) {
|
|
14443
|
+
if (err instanceof DOMException && err.name === "AbortError" && abort.signal.aborted) {
|
|
14444
|
+
return;
|
|
14445
|
+
}
|
|
14446
|
+
sendBridgeError2(res, err, {
|
|
14447
|
+
route: "POST /session/:id/shell",
|
|
14448
|
+
sessionId
|
|
14449
|
+
});
|
|
14450
|
+
} finally {
|
|
14451
|
+
res.off("close", onResClose);
|
|
14452
|
+
}
|
|
13429
14453
|
}
|
|
13430
|
-
|
|
13431
|
-
|
|
13432
|
-
sessionId
|
|
13433
|
-
});
|
|
13434
|
-
} finally {
|
|
13435
|
-
res.off("close", onResClose);
|
|
13436
|
-
}
|
|
13437
|
-
});
|
|
14454
|
+
)
|
|
14455
|
+
);
|
|
13438
14456
|
app.get("/session/:id/rewind/snapshots", async (req, res) => {
|
|
13439
14457
|
const sessionId = req.params["id"];
|
|
13440
14458
|
if (!sessionId) {
|
|
@@ -13453,60 +14471,55 @@ function registerSessionRoutes(app, deps) {
|
|
|
13453
14471
|
app.post(
|
|
13454
14472
|
"/session/:id/rewind",
|
|
13455
14473
|
mutate({ strict: true }),
|
|
13456
|
-
|
|
13457
|
-
|
|
13458
|
-
|
|
13459
|
-
|
|
13460
|
-
|
|
13461
|
-
|
|
13462
|
-
|
|
13463
|
-
|
|
13464
|
-
|
|
13465
|
-
|
|
13466
|
-
|
|
13467
|
-
|
|
13468
|
-
|
|
13469
|
-
|
|
14474
|
+
withMutableSession(
|
|
14475
|
+
"POST /session/:id/rewind",
|
|
14476
|
+
async (req, res, sessionId) => {
|
|
14477
|
+
const body = safeBody(req);
|
|
14478
|
+
const promptId = body["promptId"];
|
|
14479
|
+
if (typeof promptId !== "string" || promptId.length === 0) {
|
|
14480
|
+
res.status(400).json({
|
|
14481
|
+
error: "`promptId` is required and must be a non-empty string",
|
|
14482
|
+
code: "missing_prompt_id"
|
|
14483
|
+
});
|
|
14484
|
+
return;
|
|
14485
|
+
}
|
|
14486
|
+
const clientId = parseClientIdHeader(req, res);
|
|
14487
|
+
if (clientId === null) return;
|
|
13470
14488
|
const response = await bridge.rewindSession(
|
|
13471
14489
|
sessionId,
|
|
13472
14490
|
{ promptId, rewindFiles: body["rewindFiles"] !== false },
|
|
13473
14491
|
clientId !== void 0 ? { clientId } : void 0
|
|
13474
14492
|
);
|
|
13475
14493
|
res.status(200).json(response);
|
|
13476
|
-
} catch (err) {
|
|
13477
|
-
sendBridgeError2(res, err, {
|
|
13478
|
-
route: "POST /session/:id/rewind",
|
|
13479
|
-
sessionId
|
|
13480
|
-
});
|
|
13481
14494
|
}
|
|
13482
|
-
|
|
14495
|
+
)
|
|
13483
14496
|
);
|
|
13484
14497
|
app.post(
|
|
13485
14498
|
"/session/:id/approval-mode",
|
|
13486
14499
|
mutate({ strict: true }),
|
|
13487
|
-
|
|
13488
|
-
|
|
13489
|
-
|
|
13490
|
-
|
|
13491
|
-
|
|
13492
|
-
|
|
13493
|
-
|
|
13494
|
-
|
|
13495
|
-
|
|
13496
|
-
|
|
13497
|
-
|
|
13498
|
-
|
|
13499
|
-
|
|
13500
|
-
|
|
13501
|
-
|
|
13502
|
-
|
|
13503
|
-
|
|
13504
|
-
|
|
13505
|
-
|
|
13506
|
-
|
|
13507
|
-
|
|
13508
|
-
|
|
13509
|
-
|
|
14500
|
+
withMutableSession(
|
|
14501
|
+
"POST /session/:id/approval-mode",
|
|
14502
|
+
async (req, res, sessionId) => {
|
|
14503
|
+
const body = safeBody(req);
|
|
14504
|
+
const mode = body["mode"];
|
|
14505
|
+
const persist = body["persist"];
|
|
14506
|
+
if (typeof mode !== "string" || !APPROVAL_MODES.includes(mode)) {
|
|
14507
|
+
res.status(400).json({
|
|
14508
|
+
error: "`mode` is required and must be one of the allowed values",
|
|
14509
|
+
code: "invalid_approval_mode",
|
|
14510
|
+
allowed: APPROVAL_MODES
|
|
14511
|
+
});
|
|
14512
|
+
return;
|
|
14513
|
+
}
|
|
14514
|
+
if (persist !== void 0 && typeof persist !== "boolean") {
|
|
14515
|
+
res.status(400).json({
|
|
14516
|
+
error: "`persist` must be a boolean when provided",
|
|
14517
|
+
code: "invalid_persist_flag"
|
|
14518
|
+
});
|
|
14519
|
+
return;
|
|
14520
|
+
}
|
|
14521
|
+
const clientId = parseClientIdHeader(req, res);
|
|
14522
|
+
if (clientId === null) return;
|
|
13510
14523
|
const response = await bridge.setSessionApprovalMode(
|
|
13511
14524
|
sessionId,
|
|
13512
14525
|
mode,
|
|
@@ -13514,53 +14527,47 @@ function registerSessionRoutes(app, deps) {
|
|
|
13514
14527
|
clientId !== void 0 ? { clientId } : void 0
|
|
13515
14528
|
);
|
|
13516
14529
|
res.status(200).json(response);
|
|
13517
|
-
} catch (err) {
|
|
13518
|
-
sendBridgeError2(res, err, {
|
|
13519
|
-
route: "POST /session/:id/approval-mode",
|
|
13520
|
-
sessionId
|
|
13521
|
-
});
|
|
13522
14530
|
}
|
|
13523
|
-
|
|
14531
|
+
)
|
|
14532
|
+
);
|
|
14533
|
+
app.post(
|
|
14534
|
+
"/session/:id/language",
|
|
14535
|
+
mutate(),
|
|
14536
|
+
withMutableSession(
|
|
14537
|
+
"POST /session/:id/language",
|
|
14538
|
+
async (req, res, sessionId) => {
|
|
14539
|
+
const body = safeBody(req);
|
|
14540
|
+
const language = body["language"];
|
|
14541
|
+
const syncOutputLanguage = body["syncOutputLanguage"];
|
|
14542
|
+
if (typeof language !== "string" || !LANGUAGE_CODES.includes(language)) {
|
|
14543
|
+
res.status(400).json({
|
|
14544
|
+
error: "`language` is required and must be one of: " + LANGUAGE_CODES.join(", "),
|
|
14545
|
+
code: "invalid_language",
|
|
14546
|
+
allowed: LANGUAGE_CODES
|
|
14547
|
+
});
|
|
14548
|
+
return;
|
|
14549
|
+
}
|
|
14550
|
+
if (syncOutputLanguage !== void 0 && typeof syncOutputLanguage !== "boolean") {
|
|
14551
|
+
res.status(400).json({
|
|
14552
|
+
error: "`syncOutputLanguage` must be a boolean when provided",
|
|
14553
|
+
code: "invalid_sync_flag"
|
|
14554
|
+
});
|
|
14555
|
+
return;
|
|
14556
|
+
}
|
|
14557
|
+
const clientId = parseClientIdHeader(req, res);
|
|
14558
|
+
if (clientId === null) return;
|
|
14559
|
+
const response = await bridge.setSessionLanguage(
|
|
14560
|
+
sessionId,
|
|
14561
|
+
{
|
|
14562
|
+
language,
|
|
14563
|
+
syncOutputLanguage: syncOutputLanguage === true
|
|
14564
|
+
},
|
|
14565
|
+
clientId !== void 0 ? { clientId } : void 0
|
|
14566
|
+
);
|
|
14567
|
+
res.status(200).json(response);
|
|
14568
|
+
}
|
|
14569
|
+
)
|
|
13524
14570
|
);
|
|
13525
|
-
app.post("/session/:id/language", mutate(), async (req, res) => {
|
|
13526
|
-
const sessionId = req.params["id"];
|
|
13527
|
-
const body = safeBody(req);
|
|
13528
|
-
const language = body["language"];
|
|
13529
|
-
const syncOutputLanguage = body["syncOutputLanguage"];
|
|
13530
|
-
if (typeof language !== "string" || !LANGUAGE_CODES.includes(language)) {
|
|
13531
|
-
res.status(400).json({
|
|
13532
|
-
error: "`language` is required and must be one of: " + LANGUAGE_CODES.join(", "),
|
|
13533
|
-
code: "invalid_language",
|
|
13534
|
-
allowed: LANGUAGE_CODES
|
|
13535
|
-
});
|
|
13536
|
-
return;
|
|
13537
|
-
}
|
|
13538
|
-
if (syncOutputLanguage !== void 0 && typeof syncOutputLanguage !== "boolean") {
|
|
13539
|
-
res.status(400).json({
|
|
13540
|
-
error: "`syncOutputLanguage` must be a boolean when provided",
|
|
13541
|
-
code: "invalid_sync_flag"
|
|
13542
|
-
});
|
|
13543
|
-
return;
|
|
13544
|
-
}
|
|
13545
|
-
const clientId = parseClientIdHeader(req, res);
|
|
13546
|
-
if (clientId === null) return;
|
|
13547
|
-
try {
|
|
13548
|
-
const response = await bridge.setSessionLanguage(
|
|
13549
|
-
sessionId,
|
|
13550
|
-
{
|
|
13551
|
-
language,
|
|
13552
|
-
syncOutputLanguage: syncOutputLanguage === true
|
|
13553
|
-
},
|
|
13554
|
-
clientId !== void 0 ? { clientId } : void 0
|
|
13555
|
-
);
|
|
13556
|
-
res.status(200).json(response);
|
|
13557
|
-
} catch (err) {
|
|
13558
|
-
sendBridgeError2(res, err, {
|
|
13559
|
-
route: "POST /session/:id/language",
|
|
13560
|
-
sessionId
|
|
13561
|
-
});
|
|
13562
|
-
}
|
|
13563
|
-
});
|
|
13564
14571
|
}
|
|
13565
14572
|
__name(registerSessionRoutes, "registerSessionRoutes");
|
|
13566
14573
|
|
|
@@ -14213,6 +15220,31 @@ function sendBridgeError(res, err, ctx, daemonLog) {
|
|
|
14213
15220
|
res.status(404).json({ error: err.message, sessionId: err.sessionId });
|
|
14214
15221
|
return;
|
|
14215
15222
|
}
|
|
15223
|
+
if (err instanceof SessionArchivedError) {
|
|
15224
|
+
res.status(409).json({
|
|
15225
|
+
error: err.message,
|
|
15226
|
+
code: "session_archived",
|
|
15227
|
+
sessionId: err.sessionId
|
|
15228
|
+
});
|
|
15229
|
+
return;
|
|
15230
|
+
}
|
|
15231
|
+
if (err instanceof SessionConflictError) {
|
|
15232
|
+
res.status(409).json({
|
|
15233
|
+
error: err.message,
|
|
15234
|
+
code: "session_conflict",
|
|
15235
|
+
sessionId: err.sessionId
|
|
15236
|
+
});
|
|
15237
|
+
return;
|
|
15238
|
+
}
|
|
15239
|
+
if (err instanceof SessionArchivingError) {
|
|
15240
|
+
res.set("Retry-After", "5");
|
|
15241
|
+
res.status(409).json({
|
|
15242
|
+
error: err.message,
|
|
15243
|
+
code: "session_archiving",
|
|
15244
|
+
sessionId: err.sessionId
|
|
15245
|
+
});
|
|
15246
|
+
return;
|
|
15247
|
+
}
|
|
14216
15248
|
if (err instanceof InvalidClientIdError) {
|
|
14217
15249
|
res.status(400).json({
|
|
14218
15250
|
error: err.message,
|
|
@@ -14320,7 +15352,7 @@ function sendBridgeError(res, err, ctx, daemonLog) {
|
|
|
14320
15352
|
if (kind === "mcp_budget_would_exceed") {
|
|
14321
15353
|
const d = data;
|
|
14322
15354
|
res.status(409).json({
|
|
14323
|
-
error:
|
|
15355
|
+
error: errorMessage2(err),
|
|
14324
15356
|
code: "mcp_budget_would_exceed",
|
|
14325
15357
|
serverName: d.serverName
|
|
14326
15358
|
});
|
|
@@ -14329,7 +15361,7 @@ function sendBridgeError(res, err, ctx, daemonLog) {
|
|
|
14329
15361
|
if (kind === "mcp_server_spawn_failed") {
|
|
14330
15362
|
const d = data;
|
|
14331
15363
|
res.status(502).json({
|
|
14332
|
-
error:
|
|
15364
|
+
error: errorMessage2(err),
|
|
14333
15365
|
code: "mcp_server_spawn_failed",
|
|
14334
15366
|
serverName: d.serverName,
|
|
14335
15367
|
exitCode: d.exitCode,
|
|
@@ -14341,7 +15373,7 @@ function sendBridgeError(res, err, ctx, daemonLog) {
|
|
|
14341
15373
|
if (kind === "invalid_config") {
|
|
14342
15374
|
const d = data;
|
|
14343
15375
|
res.status(400).json({
|
|
14344
|
-
error:
|
|
15376
|
+
error: errorMessage2(err),
|
|
14345
15377
|
code: "invalid_config",
|
|
14346
15378
|
serverName: d.serverName,
|
|
14347
15379
|
reason: d.reason
|
|
@@ -14350,14 +15382,14 @@ function sendBridgeError(res, err, ctx, daemonLog) {
|
|
|
14350
15382
|
}
|
|
14351
15383
|
if (kind === "acp_channel_unavailable") {
|
|
14352
15384
|
res.status(503).json({
|
|
14353
|
-
error:
|
|
15385
|
+
error: errorMessage2(err),
|
|
14354
15386
|
code: "acp_channel_unavailable"
|
|
14355
15387
|
});
|
|
14356
15388
|
return;
|
|
14357
15389
|
}
|
|
14358
15390
|
if (kind === "restrictive_sandbox") {
|
|
14359
15391
|
res.status(403).json({
|
|
14360
|
-
error:
|
|
15392
|
+
error: errorMessage2(err),
|
|
14361
15393
|
code: "restrictive_sandbox"
|
|
14362
15394
|
});
|
|
14363
15395
|
return;
|
|
@@ -14365,7 +15397,7 @@ function sendBridgeError(res, err, ctx, daemonLog) {
|
|
|
14365
15397
|
if (kind === "directory_not_found") {
|
|
14366
15398
|
const d = data;
|
|
14367
15399
|
res.status(400).json({
|
|
14368
|
-
error:
|
|
15400
|
+
error: errorMessage2(err),
|
|
14369
15401
|
code: "directory_not_found",
|
|
14370
15402
|
path: d.path
|
|
14371
15403
|
});
|
|
@@ -14374,7 +15406,7 @@ function sendBridgeError(res, err, ctx, daemonLog) {
|
|
|
14374
15406
|
if (kind === "directory_not_trusted") {
|
|
14375
15407
|
const d = data;
|
|
14376
15408
|
res.status(403).json({
|
|
14377
|
-
error:
|
|
15409
|
+
error: errorMessage2(err),
|
|
14378
15410
|
code: "directory_not_trusted",
|
|
14379
15411
|
path: d.path
|
|
14380
15412
|
});
|
|
@@ -14422,7 +15454,7 @@ function sendBridgeError(res, err, ctx, daemonLog) {
|
|
|
14422
15454
|
res.status(500).json(errorPayload(err));
|
|
14423
15455
|
}
|
|
14424
15456
|
__name(sendBridgeError, "sendBridgeError");
|
|
14425
|
-
function
|
|
15457
|
+
function errorMessage2(err) {
|
|
14426
15458
|
if (err instanceof Error) return err.message;
|
|
14427
15459
|
if (err && typeof err === "object") {
|
|
14428
15460
|
const maybe = err.message;
|
|
@@ -14434,10 +15466,10 @@ function errorMessage(err) {
|
|
|
14434
15466
|
}
|
|
14435
15467
|
return String(err);
|
|
14436
15468
|
}
|
|
14437
|
-
__name(
|
|
15469
|
+
__name(errorMessage2, "errorMessage");
|
|
14438
15470
|
function errorPayload(err) {
|
|
14439
15471
|
const out = {
|
|
14440
|
-
error:
|
|
15472
|
+
error: errorMessage2(err)
|
|
14441
15473
|
};
|
|
14442
15474
|
if (err && typeof err === "object") {
|
|
14443
15475
|
const obj = err;
|
|
@@ -14540,9 +15572,9 @@ function registerSseEventsRoutes(app, deps) {
|
|
|
14540
15572
|
let writeChain = Promise.resolve();
|
|
14541
15573
|
const trackWriterIdle = writerIdleTimeoutMs !== void 0 && writerIdleTimeoutMs > 0;
|
|
14542
15574
|
let lastWriteAt = trackWriterIdle ? Date.now() : 0;
|
|
14543
|
-
const doWrite = /* @__PURE__ */ __name((chunk) => new Promise((
|
|
15575
|
+
const doWrite = /* @__PURE__ */ __name((chunk) => new Promise((resolve5, reject) => {
|
|
14544
15576
|
if (res.writableEnded) {
|
|
14545
|
-
|
|
15577
|
+
resolve5();
|
|
14546
15578
|
return;
|
|
14547
15579
|
}
|
|
14548
15580
|
let ok;
|
|
@@ -14554,19 +15586,19 @@ function registerSseEventsRoutes(app, deps) {
|
|
|
14554
15586
|
}
|
|
14555
15587
|
if (ok) {
|
|
14556
15588
|
if (trackWriterIdle) lastWriteAt = Date.now();
|
|
14557
|
-
|
|
15589
|
+
resolve5();
|
|
14558
15590
|
return;
|
|
14559
15591
|
}
|
|
14560
15592
|
const onDrain = /* @__PURE__ */ __name(() => {
|
|
14561
15593
|
res.off("close", onClose);
|
|
14562
15594
|
res.off("error", onError);
|
|
14563
15595
|
if (trackWriterIdle) lastWriteAt = Date.now();
|
|
14564
|
-
|
|
15596
|
+
resolve5();
|
|
14565
15597
|
}, "onDrain");
|
|
14566
15598
|
const onClose = /* @__PURE__ */ __name(() => {
|
|
14567
15599
|
res.off("drain", onDrain);
|
|
14568
15600
|
res.off("error", onError);
|
|
14569
|
-
|
|
15601
|
+
resolve5();
|
|
14570
15602
|
}, "onClose");
|
|
14571
15603
|
const onError = /* @__PURE__ */ __name((err) => {
|
|
14572
15604
|
res.off("drain", onDrain);
|
|
@@ -14662,14 +15694,14 @@ function registerSseEventsRoutes(app, deps) {
|
|
|
14662
15694
|
if (!res.writableEnded) {
|
|
14663
15695
|
const errorKind = mapDomainErrorToErrorKind(err);
|
|
14664
15696
|
writeStderrLine(
|
|
14665
|
-
`qwen serve: bridge iterator error (session ${sessionId}): ${
|
|
15697
|
+
`qwen serve: bridge iterator error (session ${sessionId}): ${errorMessage2(err)}` + (errorKind ? ` [${errorKind}]` : "")
|
|
14666
15698
|
);
|
|
14667
15699
|
await writeWithBackpressure(
|
|
14668
15700
|
formatSseFrame({
|
|
14669
15701
|
v: 1,
|
|
14670
15702
|
type: "stream_error",
|
|
14671
15703
|
data: {
|
|
14672
|
-
error:
|
|
15704
|
+
error: errorMessage2(err),
|
|
14673
15705
|
...errorKind ? { errorKind } : {}
|
|
14674
15706
|
}
|
|
14675
15707
|
})
|
|
@@ -14688,7 +15720,7 @@ __name(registerSseEventsRoutes, "registerSseEventsRoutes");
|
|
|
14688
15720
|
// packages/cli/src/serve/routes/a2ui-action.ts
|
|
14689
15721
|
init_esbuild_shims();
|
|
14690
15722
|
import * as fsp4 from "node:fs/promises";
|
|
14691
|
-
import * as
|
|
15723
|
+
import * as path12 from "node:path";
|
|
14692
15724
|
var A2UI_MIME = "application/a2ui+json";
|
|
14693
15725
|
var ACTION_TOOL = "action";
|
|
14694
15726
|
var CALL_TIMEOUT_MS = 15e3;
|
|
@@ -14708,7 +15740,7 @@ __name(usableServerConfig, "usableServerConfig");
|
|
|
14708
15740
|
async function findFromSettingsFile(workspaceCwd) {
|
|
14709
15741
|
try {
|
|
14710
15742
|
const raw = await fsp4.readFile(
|
|
14711
|
-
|
|
15743
|
+
path12.join(workspaceCwd, ".qwen", "settings.json"),
|
|
14712
15744
|
"utf8"
|
|
14713
15745
|
);
|
|
14714
15746
|
const settings = JSON.parse(raw);
|
|
@@ -14866,17 +15898,17 @@ __name(resolveBridgeFsFactory, "resolveBridgeFsFactory");
|
|
|
14866
15898
|
// packages/cli/src/serve/server/telemetry.ts
|
|
14867
15899
|
init_esbuild_shims();
|
|
14868
15900
|
function resolveDaemonTelemetryRoute(req) {
|
|
14869
|
-
const
|
|
14870
|
-
if (req.method === "POST" &&
|
|
15901
|
+
const path13 = req.path.replace(/\/$/, "") || "/";
|
|
15902
|
+
if (req.method === "POST" && path13 === "/session") {
|
|
14871
15903
|
return { route: "POST /session" };
|
|
14872
15904
|
}
|
|
14873
|
-
if (req.method === "POST" &&
|
|
15905
|
+
if (req.method === "POST" && path13 === "/sessions/delete") {
|
|
14874
15906
|
return { route: "POST /sessions/delete" };
|
|
14875
15907
|
}
|
|
14876
|
-
if (req.method === "GET" &&
|
|
15908
|
+
if (req.method === "GET" && path13 === "/daemon/status") {
|
|
14877
15909
|
return { route: "GET /daemon/status" };
|
|
14878
15910
|
}
|
|
14879
|
-
const sessionAction =
|
|
15911
|
+
const sessionAction = path13.match(
|
|
14880
15912
|
/^\/session\/([^/]+)\/(load|resume|prompt|cancel|recap|btw|mid-turn-message|model|shell|detach|rewind|approval-mode|language|a2ui-action)$/
|
|
14881
15913
|
);
|
|
14882
15914
|
const sessionActionId = sessionAction?.[1];
|
|
@@ -14887,14 +15919,14 @@ function resolveDaemonTelemetryRoute(req) {
|
|
|
14887
15919
|
sessionId: sessionActionId
|
|
14888
15920
|
};
|
|
14889
15921
|
}
|
|
14890
|
-
const sessionMetadata =
|
|
15922
|
+
const sessionMetadata = path13.match(/^\/session\/([^/]+)\/metadata$/);
|
|
14891
15923
|
if (sessionMetadata?.[1] && req.method === "PATCH") {
|
|
14892
15924
|
return {
|
|
14893
15925
|
route: "PATCH /session/:id/metadata",
|
|
14894
15926
|
sessionId: sessionMetadata[1]
|
|
14895
15927
|
};
|
|
14896
15928
|
}
|
|
14897
|
-
const sessionPermission =
|
|
15929
|
+
const sessionPermission = path13.match(
|
|
14898
15930
|
/^\/session\/([^/]+)\/permission\/([^/]+)$/
|
|
14899
15931
|
);
|
|
14900
15932
|
if (sessionPermission?.[1] && sessionPermission?.[2] && req.method === "POST") {
|
|
@@ -14905,7 +15937,7 @@ function resolveDaemonTelemetryRoute(req) {
|
|
|
14905
15937
|
...rawRequestId.length <= MAX_CLIENT_ID_LENGTH && CLIENT_ID_RE.test(rawRequestId) ? { permissionRequestId: rawRequestId } : {}
|
|
14906
15938
|
};
|
|
14907
15939
|
}
|
|
14908
|
-
const globalPermission =
|
|
15940
|
+
const globalPermission = path13.match(/^\/permission\/([^/]+)$/);
|
|
14909
15941
|
if (globalPermission?.[1] && req.method === "POST") {
|
|
14910
15942
|
const rawRequestId = globalPermission[1];
|
|
14911
15943
|
return {
|
|
@@ -14913,66 +15945,66 @@ function resolveDaemonTelemetryRoute(req) {
|
|
|
14913
15945
|
...rawRequestId.length <= MAX_CLIENT_ID_LENGTH && CLIENT_ID_RE.test(rawRequestId) ? { permissionRequestId: rawRequestId } : {}
|
|
14914
15946
|
};
|
|
14915
15947
|
}
|
|
14916
|
-
const deleteSession =
|
|
15948
|
+
const deleteSession = path13.match(/^\/session\/([^/]+)$/);
|
|
14917
15949
|
const deleteSessionId = deleteSession?.[1];
|
|
14918
15950
|
if (deleteSessionId && req.method === "DELETE") {
|
|
14919
15951
|
return { route: "DELETE /session/:id", sessionId: deleteSessionId };
|
|
14920
15952
|
}
|
|
14921
|
-
if (req.method === "GET" && /^\/workspace\/[^/]+\/sessions$/.test(
|
|
15953
|
+
if (req.method === "GET" && /^\/workspace\/[^/]+\/sessions$/.test(path13)) {
|
|
14922
15954
|
return { route: "GET /workspace/:id/sessions" };
|
|
14923
15955
|
}
|
|
14924
|
-
if (req.method === "POST" &&
|
|
15956
|
+
if (req.method === "POST" && path13 === "/workspace/init") {
|
|
14925
15957
|
return { route: "POST /workspace/init" };
|
|
14926
15958
|
}
|
|
14927
|
-
if (req.method === "POST" &&
|
|
15959
|
+
if (req.method === "POST" && path13 === "/workspace/setup-github") {
|
|
14928
15960
|
return { route: "POST /workspace/setup-github" };
|
|
14929
15961
|
}
|
|
14930
|
-
if (req.method === "POST" &&
|
|
15962
|
+
if (req.method === "POST" && path13 === "/workspace/reload") {
|
|
14931
15963
|
return { route: "POST /workspace/reload" };
|
|
14932
15964
|
}
|
|
14933
|
-
const mcpRestart =
|
|
15965
|
+
const mcpRestart = path13.match(/^\/workspace\/mcp\/([^/]+)\/restart$/);
|
|
14934
15966
|
if (mcpRestart?.[1] && req.method === "POST") {
|
|
14935
15967
|
return { route: "POST /workspace/mcp/:server/restart" };
|
|
14936
15968
|
}
|
|
14937
|
-
if (req.method === "POST" &&
|
|
15969
|
+
if (req.method === "POST" && path13 === "/workspace/mcp/servers") {
|
|
14938
15970
|
return { route: "POST /workspace/mcp/servers" };
|
|
14939
15971
|
}
|
|
14940
|
-
const mcpDelete =
|
|
15972
|
+
const mcpDelete = path13.match(/^\/workspace\/mcp\/servers\/([^/]+)$/);
|
|
14941
15973
|
if (mcpDelete?.[1] && req.method === "DELETE") {
|
|
14942
15974
|
return { route: "DELETE /workspace/mcp/servers/:name" };
|
|
14943
15975
|
}
|
|
14944
|
-
if (req.method === "POST" &&
|
|
15976
|
+
if (req.method === "POST" && path13 === "/workspace/auth/device-flow") {
|
|
14945
15977
|
return { route: "POST /workspace/auth/device-flow" };
|
|
14946
15978
|
}
|
|
14947
|
-
const deviceFlowDelete =
|
|
15979
|
+
const deviceFlowDelete = path13.match(
|
|
14948
15980
|
/^\/workspace\/auth\/device-flow\/([^/]+)$/
|
|
14949
15981
|
);
|
|
14950
15982
|
if (deviceFlowDelete?.[1] && req.method === "DELETE") {
|
|
14951
15983
|
return { route: "DELETE /workspace/auth/device-flow/:id" };
|
|
14952
15984
|
}
|
|
14953
|
-
const toolEnable =
|
|
15985
|
+
const toolEnable = path13.match(/^\/workspace\/tools\/([^/]+)\/enable$/);
|
|
14954
15986
|
if (toolEnable?.[1] && req.method === "POST") {
|
|
14955
15987
|
return { route: "POST /workspace/tools/:name/enable" };
|
|
14956
15988
|
}
|
|
14957
|
-
if (
|
|
15989
|
+
if (path13 === "/workspace/settings") {
|
|
14958
15990
|
if (req.method === "GET") return { route: "GET /workspace/settings" };
|
|
14959
15991
|
if (req.method === "POST") return { route: "POST /workspace/settings" };
|
|
14960
15992
|
}
|
|
14961
|
-
if (
|
|
15993
|
+
if (path13 === "/workspace/permissions") {
|
|
14962
15994
|
if (req.method === "GET") return { route: "GET /workspace/permissions" };
|
|
14963
15995
|
if (req.method === "POST") return { route: "POST /workspace/permissions" };
|
|
14964
15996
|
}
|
|
14965
|
-
if (
|
|
15997
|
+
if (path13 === "/workspace/trust") {
|
|
14966
15998
|
if (req.method === "GET") return { route: "GET /workspace/trust" };
|
|
14967
15999
|
}
|
|
14968
|
-
if (req.method === "POST" &&
|
|
16000
|
+
if (req.method === "POST" && path13 === "/workspace/trust/request") {
|
|
14969
16001
|
return { route: "POST /workspace/trust/request" };
|
|
14970
16002
|
}
|
|
14971
|
-
if (
|
|
16003
|
+
if (path13 === "/workspace/voice") {
|
|
14972
16004
|
if (req.method === "GET") return { route: "GET /workspace/voice" };
|
|
14973
16005
|
if (req.method === "POST") return { route: "POST /workspace/voice" };
|
|
14974
16006
|
}
|
|
14975
|
-
if (req.method === "POST" &&
|
|
16007
|
+
if (req.method === "POST" && path13 === "/workspace/voice/transcribe") {
|
|
14976
16008
|
return { route: "POST /workspace/voice/transcribe" };
|
|
14977
16009
|
}
|
|
14978
16010
|
return void 0;
|
|
@@ -14998,7 +16030,7 @@ function daemonTelemetryMiddleware(boundWorkspace) {
|
|
|
14998
16030
|
...route.permissionRequestId ? { permissionRequestId: route.permissionRequestId } : {},
|
|
14999
16031
|
...clientId ? { clientId } : {}
|
|
15000
16032
|
},
|
|
15001
|
-
async (span) => await new Promise((
|
|
16033
|
+
async (span) => await new Promise((resolve5, reject) => {
|
|
15002
16034
|
let done = false;
|
|
15003
16035
|
const finish = /* @__PURE__ */ __name(() => {
|
|
15004
16036
|
if (done) return;
|
|
@@ -15009,7 +16041,7 @@ function daemonTelemetryMiddleware(boundWorkspace) {
|
|
|
15009
16041
|
route.route,
|
|
15010
16042
|
res.statusCode
|
|
15011
16043
|
);
|
|
15012
|
-
|
|
16044
|
+
resolve5();
|
|
15013
16045
|
}, "finish");
|
|
15014
16046
|
res.once("finish", finish);
|
|
15015
16047
|
res.once("close", finish);
|
|
@@ -15332,10 +16364,10 @@ function installRateLimiter(app, opts, daemonLog) {
|
|
|
15332
16364
|
{ tier, key: key.slice(0, 64) }
|
|
15333
16365
|
);
|
|
15334
16366
|
} : void 0,
|
|
15335
|
-
onError: daemonLog ? (err,
|
|
16367
|
+
onError: daemonLog ? (err, path13) => {
|
|
15336
16368
|
daemonLog.warn(
|
|
15337
16369
|
`rate limiter error (fail-open): ${err instanceof Error ? err.message : String(err)}`,
|
|
15338
|
-
{ path:
|
|
16370
|
+
{ path: path13 }
|
|
15339
16371
|
);
|
|
15340
16372
|
} : void 0
|
|
15341
16373
|
});
|
|
@@ -15716,6 +16748,7 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
15716
16748
|
// ext-method by reaching the WS connection that hosts the named server.
|
|
15717
16749
|
clientMcpSender: clientMcpSenderRegistry.lookup
|
|
15718
16750
|
});
|
|
16751
|
+
const archiveCoordinator = new SessionArchiveCoordinator();
|
|
15719
16752
|
installSelfOriginStripMiddleware(app, getPort);
|
|
15720
16753
|
app.locals.fsFactory = fsFactory;
|
|
15721
16754
|
app.locals.boundWorkspace = boundWorkspace;
|
|
@@ -15794,6 +16827,7 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
15794
16827
|
app.use(daemonTelemetryMiddleware(boundWorkspace));
|
|
15795
16828
|
const buildWorkspaceCtx = createBuildWorkspaceCtx(boundWorkspace);
|
|
15796
16829
|
const acpHandleRef = {};
|
|
16830
|
+
const workspaceRememberLane = new WorkspaceRememberTaskLane(bridge);
|
|
15797
16831
|
const cdpTunnelRegistry = opts.cdpTunnelOverWs === true ? new CdpTunnelRegistry() : void 0;
|
|
15798
16832
|
registerDaemonStatusRoutes(app, {
|
|
15799
16833
|
opts,
|
|
@@ -15809,7 +16843,8 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
15809
16843
|
currentServeFeatures,
|
|
15810
16844
|
getSupportedDeviceFlowProviders,
|
|
15811
16845
|
deviceFlowRegistry,
|
|
15812
|
-
sessionShellCommandEnabled
|
|
16846
|
+
sessionShellCommandEnabled,
|
|
16847
|
+
getChannelWorkerSnapshot: deps.getChannelWorkerSnapshot
|
|
15813
16848
|
});
|
|
15814
16849
|
registerCapabilitiesRoutes(app, {
|
|
15815
16850
|
qwenCodeVersion: deps.qwenCodeVersion,
|
|
@@ -15833,6 +16868,13 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
15833
16868
|
parseClientId: parseClientIdHeader,
|
|
15834
16869
|
safeBody
|
|
15835
16870
|
});
|
|
16871
|
+
mountWorkspaceMemoryRememberRoutes(app, {
|
|
16872
|
+
bridge,
|
|
16873
|
+
lane: workspaceRememberLane,
|
|
16874
|
+
mutate,
|
|
16875
|
+
parseClientId: parseClientIdHeader,
|
|
16876
|
+
safeBody
|
|
16877
|
+
});
|
|
15836
16878
|
mountWorkspaceAgentsRoutes(app, {
|
|
15837
16879
|
bridge,
|
|
15838
16880
|
boundWorkspace,
|
|
@@ -15940,6 +16982,7 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
15940
16982
|
registerSessionRoutes(app, {
|
|
15941
16983
|
boundWorkspace,
|
|
15942
16984
|
bridge,
|
|
16985
|
+
archiveCoordinator,
|
|
15943
16986
|
mutate,
|
|
15944
16987
|
sendBridgeError: sendBridgeError2,
|
|
15945
16988
|
daemonLog,
|
|
@@ -15986,6 +17029,7 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
15986
17029
|
});
|
|
15987
17030
|
acpHandleRef.current = mountAcpHttp(app, bridge, {
|
|
15988
17031
|
boundWorkspace,
|
|
17032
|
+
archiveCoordinator,
|
|
15989
17033
|
workspace,
|
|
15990
17034
|
fsFactory,
|
|
15991
17035
|
deviceFlowRegistry,
|
|
@@ -15994,7 +17038,9 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
|
|
|
15994
17038
|
// explicitly permitted origin (e.g. the extension's
|
|
15995
17039
|
// `chrome-extension://<id>`) can open the reverse tool channel.
|
|
15996
17040
|
allowedOrigins: opts.allowOrigins && opts.allowOrigins.length > 0 ? parseAllowOriginPatterns(opts.allowOrigins) : void 0,
|
|
17041
|
+
hostname: opts.hostname,
|
|
15997
17042
|
sessionShellCommandEnabled,
|
|
17043
|
+
workspaceRememberLane,
|
|
15998
17044
|
checkRate: rateLimiter?.checkRate,
|
|
15999
17045
|
clientMcpOverWs: opts.clientMcpOverWs === true,
|
|
16000
17046
|
// Reverse tool channel (issue #5626, Phase 2). Per-connection provider:
|