@nextclaw/ui 0.27.0 → 0.27.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/dist/.vite/manifest.json +1246 -1175
- package/dist/assets/{abnfDiagram-VRR7QNED-DmeCRAkd.js → abnfDiagram-VRR7QNED-BT0_ycaq.js} +1 -1
- package/dist/assets/account-panel-svU9I6E6.js +1 -0
- package/dist/assets/agents-CtI89gpb.js +1 -0
- package/dist/assets/agents-DWygCmAK.js +1 -0
- package/dist/assets/{aiden0z-pptx-renderer.es-ChX5DHqP.js → aiden0z-pptx-renderer.es-ClCaf_Wh.js} +1 -1
- package/dist/assets/api-CI6_8Qt1.js +20 -0
- package/dist/assets/app-presenter-provider-_UK6sArj.js +28 -0
- package/dist/assets/appearance-settings-page-BhedtMx9.js +1 -0
- package/dist/assets/apps-C4c4NXqB.js +1 -0
- package/dist/assets/{arc-Cu6Ols44.js → arc-cmSdpq1Z.js} +1 -1
- package/dist/assets/architecture-TIHT7OUA-BImoGh10.js +1 -0
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-DrNB0eLE.js → architectureDiagram-ZJ3FMSHR-aYglVeoS.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-08rHt6DJ.js → blockDiagram-677ZJIJ3-Di_PBl5q.js} +1 -1
- package/dist/assets/{book-open-ClbJmmxP.js → book-open-i_BjUBqL.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-CCVgRG-u.js → c4Diagram-LMCZKHZV-CfuxybOL.js} +1 -1
- package/dist/assets/{card-BdC01NFF.js → card-DokH7OBp.js} +1 -1
- package/dist/assets/channel-BDQry0ob.js +1 -0
- package/dist/assets/{channels-list-page-BrIvJlop.js → channels-list-page-B7G9lHUm.js} +2 -2
- package/dist/assets/chunk-2Q5K7J3B-DkgSFfIy.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-BeCaI-JN.js → chunk-32BRIVSS-CCGc4qg4.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-ZXIcYPYs.js → chunk-52WLFC77-aJoxU2Ow.js} +1 -1
- package/dist/assets/{chunk-5FCAYU7R-DDwTPcy8.js → chunk-5FCAYU7R-CGwtZSax.js} +1 -1
- package/dist/assets/{chunk-5HE753X5-WygrMYPL.js → chunk-5HE753X5-DqaMSMeS.js} +1 -1
- package/dist/assets/{chunk-5JV3BV7I-o8T3KZgk.js → chunk-5JV3BV7I-DnabINcd.js} +1 -1
- package/dist/assets/{chunk-5TONJI2A-BKFbUT7J.js → chunk-5TONJI2A-BhpKv1k2.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-DYxCRV86.js → chunk-5VM5RSS4-DvkRPit_.js} +1 -1
- package/dist/assets/{chunk-7BUUIJ7U-CvSeJlKZ.js → chunk-7BUUIJ7U-CgDQOLnC.js} +1 -1
- package/dist/assets/{chunk-BIQX33UG-BhH3RHCY.js → chunk-BIQX33UG-9FEv5b7T.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-BTL0-xL3.js → chunk-C7G6YPKG-CNGFAFYL.js} +1 -1
- package/dist/assets/{chunk-CQNSW5MT-DWAU4QaD.js → chunk-CQNSW5MT-DovTZx0M.js} +1 -1
- package/dist/assets/{chunk-CYSBUYHQ-B7ZBMXTx.js → chunk-CYSBUYHQ-DpLEgYa1.js} +1 -1
- package/dist/assets/{chunk-EMLP6XTP-D92HvnHE.js → chunk-EMLP6XTP-D4NuN01W.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-DfOGKwW8.js → chunk-EX3LRPZG-D8Jf4kc2.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-DUuJUrXJ.js → chunk-FWX5IMBZ-BdHymAs0.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-i3MDyUY5.js → chunk-HOUHSVGY-Dd_Ycn9Z.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-F_pmNX6t.js → chunk-ICXQ74PX-BRPUu3ff.js} +1 -1
- package/dist/assets/{chunk-JG7HCLWE-DygOelrU.js → chunk-JG7HCLWE-DnCasQja.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-BpiZMeRl.js → chunk-JWPE2WC7-C3CkbEj1.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-CV10sB1I.js → chunk-MOJQB5TN-Pi--K4-1.js} +1 -1
- package/dist/assets/{chunk-MOZMSUNE-Cf1WZNm0.js → chunk-MOZMSUNE-3VTspvlh.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-DuSw54VR.js → chunk-OGEWGWER-BetLLyJt.js} +1 -1
- package/dist/assets/{chunk-OSBZ3O6U-_rt_XKSZ.js → chunk-OSBZ3O6U-D1VcupAj.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-BFPaAGNW.js → chunk-PUDLZKDR-Cy4G8SjV.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-GE0v_E5y.js → chunk-Q4XR5HBZ-UYxXzi7_.js} +1 -1
- package/dist/assets/{chunk-QBLGF6JB-BK_FpnlJ.js → chunk-QBLGF6JB-CHiGeRY1.js} +1 -1
- package/dist/assets/{chunk-R7FJI6CG-CpYh6rGo.js → chunk-R7FJI6CG-C5kVJus8.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-htnxL_Cl.js → chunk-RYQCIY6F-C6cuuEBn.js} +1 -1
- package/dist/assets/{chunk-U6XO7XAA-wDbE4BZ7.js → chunk-U6XO7XAA-CM0EK7Y0.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-DDRSVFTB.js → chunk-V7JOEXUC-DWJZIsME.js} +1 -1
- package/dist/assets/chunk-VAUOI2AC-CsTIB8qD.js +1 -0
- package/dist/assets/{chunk-VR4S4FIN-COdhHR1o.js → chunk-VR4S4FIN-CY5KYeEi.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-DPDGhyF6.js → chunk-WYO6CB5R-B30N3ZP8.js} +3 -3
- package/dist/assets/chunk-XXDRQBXY-DK6dz0Q3.js +1 -0
- package/dist/assets/{chunk-YOTPTUD7-D8ir9Nx5.js → chunk-YOTPTUD7-CBu82JpO.js} +1 -1
- package/dist/assets/{chunk-ZGVPDNZ5-BuNKD836.js → chunk-ZGVPDNZ5-BN5NX5qA.js} +1 -1
- package/dist/assets/{chunk-ZIRB5QZD-DyHzq8jH.js → chunk-ZIRB5QZD-DcDXK29L.js} +1 -1
- package/dist/assets/{circle-dashed-nDaMRmP-.js → circle-dashed-Cx8CIEbg.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-BYygHCrp.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-BgQ0XKYz.js +1 -0
- package/dist/assets/{client-DlFB5dW5.js → client-CUJUci3r.js} +1 -1
- package/dist/assets/{config-split-page-C06fU9OQ.js → config-split-page-hY1xug28.js} +1 -1
- package/dist/assets/{confirm-dialog-DpcmlXhd.js → confirm-dialog-B-ejV666.js} +1 -1
- package/dist/assets/{context-menu-DKIEaY7f.js → context-menu-dGVv8klA.js} +9 -9
- package/dist/assets/{cose-bilkent-JH36ORCC-B1I1_P6a.js → cose-bilkent-JH36ORCC-CmeJVoTw.js} +1 -1
- package/dist/assets/{createLucideIcon-Lnt-4LMC.js → createLucideIcon-reFMl-W-.js} +1 -1
- package/dist/assets/cron-DkYSmE-f.js +1 -0
- package/dist/assets/cynefin-VYW2F7L2-DKbotNWS.js +1 -0
- package/dist/assets/{cynefinDiagram-TSTJHNR4-DcOnhnCo.js → cynefinDiagram-TSTJHNR4-DGOUMgPP.js} +1 -1
- package/dist/assets/{dagre-nSYhf9yY.js → dagre-CMxSkbJJ.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB-hAX89lg3.js → dagre-VKFMJZFB-3YUBEXwo.js} +1 -1
- package/dist/assets/{desktop-capabilities-page-DPORyZAu.js → desktop-capabilities-page-CYJTQ3nC.js} +1 -1
- package/dist/assets/{desktop-update-config-BMTeWZmn.js → desktop-update-config-guKAozXO.js} +1 -1
- package/dist/assets/{diagram-FQU43EPY-C1mRMD2k.js → diagram-FQU43EPY-Br1g--ww.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-D2XBn5al.js → diagram-G47NLZAW-D1wJCBgr.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-3LmRsmkC.js → diagram-NH7WQ7WH-Cgg2auLH.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-CFWn-osC.js → diagram-OA4YK3LP-R-wHiWqh.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-BqNRbUvU.js → diagram-WEI45ONY-CsNnKV0P.js} +1 -1
- package/dist/assets/{dialog-BNXSPMX_.js → dialog-DLOwdjT1.js} +1 -1
- package/dist/assets/{dist-Cuiqqp1O.js → dist-ZaFFmAGl.js} +1 -1
- package/dist/assets/doc-browser-B0zg0EXA.js +1 -0
- package/dist/assets/doc-browser-context-CTmOmuqa.js +1 -0
- package/dist/assets/{docx-preview-CunAATyC.js → docx-preview-kPn_EtAr.js} +1 -1
- package/dist/assets/{download-f_sPcoLZ.js → download-DovI3Yvf.js} +1 -1
- package/dist/assets/{ebnfDiagram-CCIWWBDH-CYb8IwqD.js → ebnfDiagram-CCIWWBDH-DCyhIPdi.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-H47mUoeK.js → erDiagram-Q63AITRT-D0ZJYAUf.js} +1 -1
- package/dist/assets/eventmodeling-45OFAUF4-SIhbIFIw.js +1 -0
- package/dist/assets/{extensions-B-0xQz3L.js → extensions-BZ3qrM8M.js} +1 -1
- package/dist/assets/{external-link-CbVlnlNy.js → external-link-DUD3AbsF.js} +1 -1
- package/dist/assets/flowDiagram-23GEKE2U-DdAjkU7I.js +1 -0
- package/dist/assets/{form-actions-BoTqELHB.js → form-actions-B8VniHts.js} +1 -1
- package/dist/assets/{ganttDiagram-NO4QXBWP-hSX45zfV.js → ganttDiagram-NO4QXBWP-CiXDcXZD.js} +1 -1
- package/dist/assets/gitGraph-TEB2WS4Q-DnMNpESZ.js +1 -0
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-Br0UW2qg.js → gitGraphDiagram-IHSO6WYX-BSiIV48S.js} +1 -1
- package/dist/assets/{i18n-B0V5NkFj.js → i18n-IGQbjBqS.js} +2 -2
- package/dist/assets/inbox-CJBCLB1X.js +1 -0
- package/dist/assets/{inbox-Bna8iUy0.js → inbox-ClAG7SI_.js} +1 -1
- package/dist/assets/info-DKCQHKI2-BSm-69p_.js +1 -0
- package/dist/assets/{infoDiagram-FWYZ7A6U-DQmP_67w.js → infoDiagram-FWYZ7A6U-CCIgBCVq.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-BHAV2oes.js → ishikawaDiagram-FXEZZL3T-nQ_baN5u.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-CFHax4_d.js → journeyDiagram-5HDEW3XC-ABueIPzj.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-B4ZA9rp5.js → kanban-definition-HUTT4EX6-8FKS3fmQ.js} +1 -1
- package/dist/assets/katex-DPgHtix3.js +1 -0
- package/dist/assets/keyboard-shortcuts-settings-page-B5oxyPS2.js +1 -0
- package/dist/assets/{line-BboM0HLF.js → line-Biw8iEUz.js} +1 -1
- package/dist/assets/{linear-4K_M1oYM.js → linear-D7CWUHTF.js} +1 -1
- package/dist/assets/login-page-BxSMwUsc.js +1 -0
- package/dist/assets/{login-page-JNr5JCEw.js → login-page-DYaozpzz.js} +1 -1
- package/dist/assets/main-D_zmSVS1.css +1 -0
- package/dist/assets/main-gFUvLF57.js +2 -0
- package/dist/assets/main-i18n-catalog.config-itg3Gvzp.js +1 -0
- package/dist/assets/{map-26GpO1wn.js → map-CDu47gTf.js} +1 -1
- package/dist/assets/marketplace-DsAf_rnt.js +1 -0
- package/dist/assets/mcp-marketplace-page-BqUz09cj.js +1 -0
- package/dist/assets/{mermaid-parser.core-BTJFnvRC.js → mermaid-parser.core-Cc9wjl82.js} +2 -2
- package/dist/assets/{mermaid.core-B7ufoXgq.js → mermaid.core-GWlaVWz1.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-BHyru6KE.js → mindmap-definition-LN4V7U3C-oqnKsz-R.js} +1 -1
- package/dist/assets/{model-config-page-C-wbM6Fe.js → model-config-page-Cduyz2Hy.js} +1 -1
- package/dist/assets/{navigation-link-DFLSVIut.js → navigation-link-D-JSWFxj.js} +1 -1
- package/dist/assets/{ordinal-BqFe2ePF.js → ordinal-DImbxqBz.js} +1 -1
- package/dist/assets/packet-7NZHBO7P-1YQvC6mT.js +1 -0
- package/dist/assets/panel-app-i18n-catalog.config-BfLHr1tc.js +1 -0
- package/dist/assets/panel-apps-Bg5Fzm3T.js +1 -0
- package/dist/assets/panelStandalone-oHxbXvYt.js +2 -0
- package/dist/assets/{pegDiagram-2B236MQR-PVTatU5j.js → pegDiagram-2B236MQR-DYAB8CEz.js} +1 -1
- package/dist/assets/pie-RZYD4A2V-BGoNOSwu.js +1 -0
- package/dist/assets/{pieDiagram-ENE6RG2P-CGc-QCc7.js → pieDiagram-ENE6RG2P-DGsH0Kk5.js} +1 -1
- package/dist/assets/plus-DUdl68Eo.js +1 -0
- package/dist/assets/{privacy-settings-page-CPdEiouE.js → privacy-settings-page-DG9E8N-F.js} +1 -1
- package/dist/assets/projects-DvHyoKIF.js +1 -0
- package/dist/assets/{provider-models-XRArsply.js → provider-models-COzjYl29.js} +1 -1
- package/dist/assets/{provider-scoped-model-input-C8JI-hus.js → provider-scoped-model-input-Cvy74uj8.js} +1 -1
- package/dist/assets/{providers-config-page-fyMlVgvq.js → providers-config-page-7rCwVnhh.js} +1 -1
- package/dist/assets/{puzzle-Bk3ud1KI.js → puzzle-C5Wfpjk_.js} +1 -1
- package/dist/assets/{pwa-Cx6Fnox4.js → pwa-CAg_9zFs.js} +1 -1
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-DGL36riv.js → quadrantDiagram-ABIIQ3AL-BegGcT3l.js} +1 -1
- package/dist/assets/radar-I7S5WNFK-uPku9RRq.js +1 -0
- package/dist/assets/railroad-3IZDKUUU-DHnmbuIo.js +1 -0
- package/dist/assets/railroad-abnf-AHOZXSZD-DiZs--Iw.js +1 -0
- package/dist/assets/railroad-ebnf-EBAXGLYW-Nr53gnNE.js +1 -0
- package/dist/assets/railroad-peg-LSFZ7HO6-Cm_PuwJS.js +1 -0
- package/dist/assets/{railroadDiagram-RFXS5EU6-CyNwvhSb.js → railroadDiagram-RFXS5EU6-B3yJ2yxE.js} +1 -1
- package/dist/assets/{refresh-cw-Ca2napNx.js → refresh-cw-OhwaPDn1.js} +1 -1
- package/dist/assets/remote-Dhr4K1oK.js +1 -0
- package/dist/assets/{requirementDiagram-TGXJPOKE-C31NMKJ0.js → requirementDiagram-TGXJPOKE-DtEyoEXG.js} +1 -1
- package/dist/assets/{rotate-cw-DXg7sNUE.js → rotate-cw-aBponvp7.js} +1 -1
- package/dist/assets/runtime-config-page-FI2pk3HH.js +1 -0
- package/dist/assets/{sankeyDiagram-HTMAVEWB-CwrQ_7PA.js → sankeyDiagram-HTMAVEWB-h9AYS2UB.js} +1 -1
- package/dist/assets/{save-DNigywZ9.js → save-394QCMXn.js} +1 -1
- package/dist/assets/{search-U9C0oTQJ.js → search-BG2qAlNZ.js} +1 -1
- package/dist/assets/{search-config-page-Bdx_RR1q.js → search-config-page-D4LeiB9y.js} +1 -1
- package/dist/assets/{secrets-config-page-CvMIcsfJ.js → secrets-config-page-BzGcr5Vw.js} +2 -2
- package/dist/assets/security-config-vD_V0BQF.js +1 -0
- package/dist/assets/{select-DxwDeKUR.js → select-xUXYV0Gr.js} +1 -1
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-EFD482_y.js → sequenceDiagram-DBY2YBRQ-D-uxCkob.js} +1 -1
- package/dist/assets/service-apps-panel-BTqXn0pg.js +1 -0
- package/dist/assets/{settings-2-BrBUdRoY.js → settings-2-CWOGRRaA.js} +1 -1
- package/dist/assets/{sizeCapture-X5ZJPWSS-TDvULTE1.js → sizeCapture-X5ZJPWSS-DBzZytQo.js} +1 -1
- package/dist/assets/{src-CI2AvEMj.js → src-zrGbZsfc.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-JdPsIYHo.js → stateDiagram-2N3HPSRC-DyDy7AHq.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-CtkpO6aA.js +1 -0
- package/dist/assets/{status-dot-BbGsrkIX.js → status-dot-CYifnSnt.js} +1 -1
- package/dist/assets/{swimlanes-5IMT3BWC-BnxpKeK_.js → swimlanes-5IMT3BWC-BzOduS38.js} +2 -2
- package/dist/assets/swimlanesDiagram-G3AALYLV-BSdi6mvi.js +8 -0
- package/dist/assets/{tag-chip-BDbxcf6P.js → tag-chip-CjM6lyeh.js} +1 -1
- package/dist/assets/theme-provider-BToFZGZG.js +1 -0
- package/dist/assets/{timeline-definition-FHXFAJF6-2IL22-uC.js → timeline-definition-FHXFAJF6-CmgnEF2t.js} +1 -1
- package/dist/assets/{trash-2-DwSkRfeB.js → trash-2-DvJ6duOW.js} +1 -1
- package/dist/assets/treeView-QDETBFTQ-DBoqypPR.js +1 -0
- package/dist/assets/treemap-6X3UGDF4-BMeL3P0q.js +1 -0
- package/dist/assets/use-config-COusaY5p.js +1 -0
- package/dist/assets/{use-desktop-capabilities-D7odgaTB.js → use-desktop-capabilities-B-u6ojjW.js} +1 -1
- package/dist/assets/{use-viewport-layout-BANU5E1t.js → use-viewport-layout-IKKGpjzF.js} +1 -1
- package/dist/assets/{vennDiagram-L72KCM5P-Cjf6n5Va.js → vennDiagram-L72KCM5P-DwS7D2kg.js} +1 -1
- package/dist/assets/wardley-OPB4EBWU-Dqxe7zN6.js +1 -0
- package/dist/assets/{wardleyDiagram-EHGQE667-yv4qKbBO.js → wardleyDiagram-EHGQE667-B4X8OOL4.js} +1 -1
- package/dist/assets/{x-BqGj6_Vq.js → x-E9VB4HQx.js} +1 -1
- package/dist/assets/{xychartDiagram-FW5EYKEG-Bmp8Ck8X.js → xychartDiagram-FW5EYKEG-BjRQ_0nx.js} +1 -1
- package/dist/index.html +41 -40
- package/dist/panel-standalone.html +16 -16
- package/package.json +6 -6
- package/src/features/agents/components/__tests__/agents-page.test.tsx +8 -5
- package/src/features/agents/components/agent-details-dialog.tsx +19 -15
- package/src/features/agents/components/agents-page.tsx +27 -38
- package/src/features/app-data/components/app-retained-data-section.tsx +2 -2
- package/src/features/apps/components/app-marketplace-catalog.tsx +5 -5
- package/src/features/apps/components/app-marketplace-detail.tsx +11 -2
- package/src/features/apps/components/app-packages-panel.tsx +4 -4
- package/src/features/apps/components/apps-panel.tsx +3 -1
- package/src/features/chat/components/providers/chat-runtime.provider.tsx +1 -1
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +0 -1
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +13 -2
- package/src/features/chat/features/conversation/components/session-conversation-area.tsx +8 -1
- package/src/features/chat/features/conversation/components/session-conversation-input-top-slot.tsx +44 -0
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +4 -38
- package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +127 -10
- package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-input-state.test.tsx +12 -2
- package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +88 -73
- package/src/features/chat/features/conversation/hooks/use-session-conversation-input-query.ts +5 -15
- package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +53 -13
- package/src/features/chat/features/conversation/hooks/use-session-submission-recovery.ts +83 -0
- package/src/features/chat/features/conversation/types/session-conversation-input.types.ts +38 -0
- package/src/features/chat/features/input/utils/__tests__/ncp-chat-input-availability.utils.test.ts +1 -29
- package/src/features/chat/features/input/utils/ncp-chat-input-availability.utils.ts +1 -8
- package/src/features/chat/features/message/components/__tests__/chat-message-list-skill-preview.container.test.tsx +14 -1
- package/src/features/chat/features/message/hooks/use-chat-inline-token-actions.ts +7 -2
- package/src/features/chat/features/session/utils/__tests__/chat-session-route.utils.test.ts +26 -0
- package/src/features/chat/features/session/utils/chat-session-preference-governance.utils.ts +14 -8
- package/src/features/chat/features/session/utils/chat-session-route.utils.ts +9 -12
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.selection.test.tsx +14 -2
- package/src/features/chat/features/workspace/components/__tests__/session-cron-job-content.test.tsx +5 -1
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +1 -1
- package/src/features/chat/features/workspace/components/session-cron-job-content.tsx +6 -22
- package/src/features/chat/index.ts +1 -0
- package/src/features/chat/pages/__tests__/ncp-chat-page.test.ts +37 -47
- package/src/features/chat/pages/ncp-chat-page.tsx +3 -3
- package/src/features/chat/stores/__tests__/chat-composer-draft.store.test.ts +67 -1
- package/src/features/chat/stores/chat-composer-draft.store.ts +230 -29
- package/src/features/cron/components/__tests__/cron-config.test.tsx +8 -2
- package/src/features/cron/components/cron-config.tsx +6 -17
- package/src/features/cron/components/cron-job-detail-dialog.tsx +10 -9
- package/src/features/cron/components/cron-job-resource.tsx +51 -0
- package/src/features/cron/index.ts +1 -0
- package/src/features/inbox/pages/inbox-page.tsx +8 -5
- package/src/features/marketplace/components/__tests__/marketplace-page-detail.test.tsx +1 -0
- package/src/features/marketplace/components/__tests__/marketplace-page.test.tsx +1 -0
- package/src/features/marketplace/components/mcp/__tests__/mcp-marketplace-page.test.tsx +2 -0
- package/src/features/marketplace/components/mcp/mcp-marketplace-card.tsx +2 -2
- package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +23 -1
- package/src/features/marketplace/hooks/use-marketplace-item-detail.ts +11 -0
- package/src/features/panel-apps/index.ts +1 -0
- package/src/features/projects/components/work/project-work-item-drawer.tsx +7 -10
- package/src/features/projects/pages/project-home-page.tsx +18 -23
- package/src/features/projects/pages/projects-page.test.tsx +4 -1
- package/src/features/right-panel-resources/components/__tests__/system-object-resource.test.tsx +74 -6
- package/src/features/right-panel-resources/components/native-object-resource.tsx +134 -0
- package/src/features/right-panel-resources/components/page-resource-icon.tsx +1 -1
- package/src/features/right-panel-resources/components/system-object-resource.tsx +66 -1
- package/src/features/right-panel-resources/components/workspace-file-resource.tsx +9 -0
- package/src/features/right-panel-resources/configs/right-panel-resource-routes.config.ts +11 -21
- package/src/features/right-panel-resources/managers/__tests__/page-resource.manager.test.ts +14 -4
- package/src/features/right-panel-resources/managers/page-resource.manager.ts +38 -21
- package/src/features/right-panel-resources/utils/__tests__/right-panel-resource-route-resolver.utils.test.ts +12 -0
- package/src/features/service-apps/components/service-apps-panel.tsx +5 -2
- package/src/shared/components/doc-browser/__tests__/doc-browser-context.test.tsx +16 -0
- package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +2 -2
- package/src/shared/components/doc-browser/__tests__/resource-link-interceptor.test.tsx +29 -1
- package/src/shared/components/doc-browser/stores/doc-browser.store.ts +13 -7
- package/src/shared/components/doc-browser/utils/doc-browser-url.utils.ts +2 -1
- package/src/shared/components/ui/skeleton.tsx +16 -2
- package/src/shared/lib/i18n/locales/en-US/chat.json +8 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +8 -0
- package/src/shared/lib/transport/__tests__/local-transport.service.test.ts +24 -0
- package/src/shared/lib/transport/local-transport.service.ts +8 -6
- package/dist/assets/account-panel-BYxGT4m4.js +0 -1
- package/dist/assets/api-CG22BskB.js +0 -20
- package/dist/assets/app-presenter-provider-CJFjhVYI.js +0 -28
- package/dist/assets/appearance-settings-page-BcXr0vHp.js +0 -1
- package/dist/assets/architecture-TIHT7OUA-CLjV_xu4.js +0 -1
- package/dist/assets/channel-BgpbPyEH.js +0 -1
- package/dist/assets/chunk-2Q5K7J3B-DjhMPqAC.js +0 -1
- package/dist/assets/chunk-VAUOI2AC-GYimZYx9.js +0 -1
- package/dist/assets/chunk-XXDRQBXY-KKCf0HV5.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-iRMPRR8M.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-BguFy_wj.js +0 -1
- package/dist/assets/cynefin-VYW2F7L2-CtbjXuCW.js +0 -1
- package/dist/assets/doc-browser-CDEapjmj.js +0 -1
- package/dist/assets/doc-browser-context-0QDWYorh.js +0 -1
- package/dist/assets/eventmodeling-45OFAUF4-Djd6id_u.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-DUrsM_ZL.js +0 -1
- package/dist/assets/gitGraph-TEB2WS4Q-CU6P_2Cm.js +0 -1
- package/dist/assets/info-DKCQHKI2-Bj2P_7RC.js +0 -1
- package/dist/assets/katex-BprZ-Wwb.js +0 -1
- package/dist/assets/keyboard-shortcuts-settings-page-BluWKrSo.js +0 -1
- package/dist/assets/login-page-BMHW0Hh0.js +0 -1
- package/dist/assets/main-BCX_Untx.css +0 -1
- package/dist/assets/main-C5kbmGUV.js +0 -2
- package/dist/assets/main-i18n-catalog.config-DO4wNT7T.js +0 -1
- package/dist/assets/mcp-marketplace-page-CV-PHKTN.js +0 -1
- package/dist/assets/packet-7NZHBO7P-ti5ge2jr.js +0 -1
- package/dist/assets/panel-app-i18n-catalog.config-Bo52dPuF.js +0 -1
- package/dist/assets/panelStandalone-PuuMF65O.js +0 -2
- package/dist/assets/pie-RZYD4A2V-Bnep8TyV.js +0 -1
- package/dist/assets/plus-CywSlIbG.js +0 -1
- package/dist/assets/projects-D_cd_t4G.js +0 -1
- package/dist/assets/radar-I7S5WNFK-BYg-icAP.js +0 -1
- package/dist/assets/railroad-3IZDKUUU-_CHO-Fi1.js +0 -1
- package/dist/assets/railroad-abnf-AHOZXSZD-hbQDevHe.js +0 -1
- package/dist/assets/railroad-ebnf-EBAXGLYW-v4RZkOfi.js +0 -1
- package/dist/assets/railroad-peg-LSFZ7HO6-bC9BqkI5.js +0 -1
- package/dist/assets/remote-D5MBQNZE.js +0 -1
- package/dist/assets/runtime-config-page--ivCxBT4.js +0 -1
- package/dist/assets/security-config-CQmMqQ8v.js +0 -1
- package/dist/assets/service-apps-panel-CiYO1Be4.js +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-4_aAQfB_.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-wCqfwsw-.js +0 -8
- package/dist/assets/theme-provider-D98XGa-c.js +0 -1
- package/dist/assets/treeView-QDETBFTQ-BAyOb14Z.js +0 -1
- package/dist/assets/treemap-6X3UGDF4-DgPDM0zS.js +0 -1
- package/dist/assets/use-config-D3cNzk2k.js +0 -1
- package/dist/assets/wardley-OPB4EBWU-eCElh8z5.js +0 -1
- /package/dist/assets/{array-DstOKG9P.js → array-DsNX3jRj.js} +0 -0
- /package/dist/assets/{chunk-KEIR6QF5-0TA8nwgf.js → chunk-KEIR6QF5-BOAXXeyv.js} +0 -0
- /package/dist/assets/{chunk-Y2CYZVJY-rfGCG6pM.js → chunk-Y2CYZVJY-CHjSjwoV.js} +0 -0
- /package/dist/assets/{config-hints-BfGoD7-o.js → config-hints-CaB3I7KP.js} +0 -0
- /package/dist/assets/{cytoscape.esm-DdIcNEda.js → cytoscape.esm-j4J9a91l.js} +0 -0
- /package/dist/assets/{defaultLocale-CzXix2iJ.js → defaultLocale-Bmxjp-V_.js} +0 -0
- /package/dist/assets/{dist-D5dwMwKA.js → dist-DwLj1Wg5.js} +0 -0
- /package/dist/assets/{graphlib-BKOe0eFe.js → graphlib-CC0HTn1v.js} +0 -0
- /package/dist/assets/{host-capabilities-CnSVTaot.js → host-capabilities-B107nwS3.js} +0 -0
- /package/dist/assets/{init-OCFb3zgY.js → init-Biz9Rus1.js} +0 -0
- /package/dist/assets/{jszip.min-B2uhSxvV.js → jszip.min-DQvBVV_R.js} +0 -0
- /package/dist/assets/{katex-dBrYsE7B.js → katex-BzT-4KBu.js} +0 -0
- /package/dist/assets/{middleware-CAXxl-af.js → middleware-B--6mAUM.js} +0 -0
- /package/dist/assets/{navigation-history-CzErJuPG.js → navigation-history-CCnxsAKR.js} +0 -0
- /package/dist/assets/{panel-app-CSQmm_7E.js → panel-app-BBTg0tVp.js} +0 -0
- /package/dist/assets/{path-T5eoIRSJ.js → path-4azsBPZv.js} +0 -0
- /package/dist/assets/{rough.esm-DmGXmZEB.js → rough.esm-w1lBArN6.js} +0 -0
- /package/dist/assets/{xlsx-CoYacou7.js → xlsx--SB9KlFl.js} +0 -0
|
@@ -2,8 +2,13 @@ import { act, renderHook, waitFor } from '@testing-library/react';
|
|
|
2
2
|
import { describe, expect, it, vi } from 'vitest';
|
|
3
3
|
import { NextClawClientError, type UiNcpSessionQueuedInputView } from '@nextclaw/client-sdk';
|
|
4
4
|
import type { NcpAgentSendEnvelope, NcpMessage, NcpRunHandle } from '@nextclaw/ncp';
|
|
5
|
+
import { NcpHttpSendError } from '@nextclaw/ncp-http-agent-client';
|
|
6
|
+
|
|
7
|
+
const fetchNcpSessionMessages = vi.hoisted(() => vi.fn());
|
|
8
|
+
vi.mock('@/shared/lib/api', () => ({ fetchNcpSessionMessages }));
|
|
5
9
|
|
|
6
10
|
import { useSessionConversationController } from '@/features/chat/features/conversation/hooks/use-session-conversation-controller';
|
|
11
|
+
import type { ChatComposerSubmission } from '@/features/chat/stores/chat-composer-draft.store';
|
|
7
12
|
|
|
8
13
|
type TestAgentSend = (envelope: NcpAgentSendEnvelope) => Promise<NcpRunHandle | null>;
|
|
9
14
|
|
|
@@ -107,6 +112,7 @@ function createControllerParams(params: {
|
|
|
107
112
|
composerFocusRequestId: 0,
|
|
108
113
|
nodes: [createTextNode('next task')],
|
|
109
114
|
pendingProjectRoot: null,
|
|
115
|
+
pendingSubmission: null as ChatComposerSubmission | null,
|
|
110
116
|
pendingSessionType: 'default',
|
|
111
117
|
selectedModel: undefined,
|
|
112
118
|
selectedSessionType: 'default',
|
|
@@ -126,13 +132,58 @@ function createControllerParams(params: {
|
|
|
126
132
|
},
|
|
127
133
|
selectedAgentId: 'main',
|
|
128
134
|
sessionKey: 'session-1',
|
|
129
|
-
resetComposer: vi.fn(),
|
|
130
135
|
restoreComposer: vi.fn(),
|
|
136
|
+
beginSubmission: vi.fn(),
|
|
137
|
+
acceptSubmission: vi.fn(),
|
|
138
|
+
failSubmission: vi.fn(),
|
|
139
|
+
restoreSubmission: vi.fn(),
|
|
140
|
+
discardSubmission: vi.fn(),
|
|
131
141
|
onSessionMaterialized: vi.fn(),
|
|
132
142
|
setSendError: vi.fn(),
|
|
133
143
|
};
|
|
134
144
|
}
|
|
135
145
|
|
|
146
|
+
describe('useSessionConversationController HTTP message identity', () => {
|
|
147
|
+
it.each(['send', 'sendSteering', 'sendPresetMessage'] as const)(
|
|
148
|
+
'supports %s over HTTP without randomUUID and keeps submission identities distinct',
|
|
149
|
+
async (action) => {
|
|
150
|
+
const params = createControllerParams({ isRunning: false });
|
|
151
|
+
const { result } = renderHook(() => useSessionConversationController(params));
|
|
152
|
+
const getRandomValues = globalThis.crypto.getRandomValues.bind(globalThis.crypto);
|
|
153
|
+
const now = vi.spyOn(Date, 'now').mockReturnValue(1780000000000);
|
|
154
|
+
vi.stubGlobal('crypto', { getRandomValues });
|
|
155
|
+
try {
|
|
156
|
+
await act(async () => {
|
|
157
|
+
if (action === 'sendPresetMessage') {
|
|
158
|
+
await result.current.sendPresetMessage('Update this session title');
|
|
159
|
+
await result.current.sendPresetMessage('Update this session title again');
|
|
160
|
+
} else {
|
|
161
|
+
await result.current[action]();
|
|
162
|
+
await result.current[action]();
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
} finally {
|
|
166
|
+
now.mockRestore();
|
|
167
|
+
vi.unstubAllGlobals();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
expect(params.agent.send).toHaveBeenCalledTimes(2);
|
|
171
|
+
const envelopes = params.agent.send.mock.calls.map(([envelope]) => envelope);
|
|
172
|
+
expect(envelopes[0].message.id).not.toBe(envelopes[1].message.id);
|
|
173
|
+
for (const envelope of envelopes) {
|
|
174
|
+
expect(envelope.message.id).toMatch(/^user-/);
|
|
175
|
+
expect(envelope.idempotencyKey).toBe(envelope.message.id);
|
|
176
|
+
expect(envelope.sessionId).toBe('session-1');
|
|
177
|
+
if (action !== 'sendPresetMessage') {
|
|
178
|
+
expect(params.beginSubmission).toHaveBeenCalledWith(envelope, expect.any(Object));
|
|
179
|
+
expect(params.acceptSubmission).toHaveBeenCalledWith(envelope.message.id);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (action === 'sendPresetMessage') expect(params.beginSubmission).not.toHaveBeenCalled();
|
|
183
|
+
},
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
|
|
136
187
|
describe('useSessionConversationController backend run queue', () => {
|
|
137
188
|
it('preallocates one stable session identity for the first root message', async () => {
|
|
138
189
|
const send = vi.fn<TestAgentSend>(async (envelope) => {
|
|
@@ -162,6 +213,7 @@ describe('useSessionConversationController backend run queue', () => {
|
|
|
162
213
|
const envelope = send.mock.calls[0]?.[0];
|
|
163
214
|
expect(envelope?.sessionId).toMatch(/^ncp-/);
|
|
164
215
|
expect(envelope?.message.sessionId).toBe(envelope?.sessionId);
|
|
216
|
+
expect(envelope?.idempotencyKey).toBe(envelope?.message.id);
|
|
165
217
|
expect(params.onSessionMaterialized).toHaveBeenCalledWith(envelope?.sessionId);
|
|
166
218
|
});
|
|
167
219
|
|
|
@@ -182,7 +234,21 @@ describe('useSessionConversationController backend run queue', () => {
|
|
|
182
234
|
parts: [{ type: 'text', text: 'next task' }],
|
|
183
235
|
}),
|
|
184
236
|
});
|
|
185
|
-
expect(params.
|
|
237
|
+
expect(params.beginSubmission).toHaveBeenCalledTimes(1);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('attempts a send when a nonempty draft has a stale blocked runtime status', async () => {
|
|
241
|
+
const send = createSendMock();
|
|
242
|
+
const params = { ...createControllerParams({ isRunning: false, send }), isRuntimeBlocked: true };
|
|
243
|
+
const { result } = renderHook(() => useSessionConversationController(params));
|
|
244
|
+
|
|
245
|
+
expect(result.current.sendDisabled).toBe(false);
|
|
246
|
+
await act(async () => {
|
|
247
|
+
await result.current.send();
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
expect(send).toHaveBeenCalledTimes(1);
|
|
251
|
+
expect(params.setSendError).toHaveBeenLastCalledWith(null);
|
|
186
252
|
});
|
|
187
253
|
|
|
188
254
|
it('uses prefer-steer for command/control-enter submission without changing the send button path', async () => {
|
|
@@ -241,7 +307,7 @@ describe('useSessionConversationController backend run queue', () => {
|
|
|
241
307
|
}),
|
|
242
308
|
});
|
|
243
309
|
expect(envelope?.metadata).not.toHaveProperty('ui_inline_tokens');
|
|
244
|
-
expect(params.
|
|
310
|
+
expect(params.beginSubmission).not.toHaveBeenCalled();
|
|
245
311
|
expect(params.restoreComposer).not.toHaveBeenCalled();
|
|
246
312
|
});
|
|
247
313
|
|
|
@@ -257,7 +323,7 @@ describe('useSessionConversationController backend run queue', () => {
|
|
|
257
323
|
await expect(result.current.sendPresetMessage('Update title')).rejects.toThrow(sendError);
|
|
258
324
|
});
|
|
259
325
|
|
|
260
|
-
expect(params.
|
|
326
|
+
expect(params.beginSubmission).not.toHaveBeenCalled();
|
|
261
327
|
expect(params.restoreComposer).not.toHaveBeenCalled();
|
|
262
328
|
expect(params.setSendError).toHaveBeenLastCalledWith(sendError.message);
|
|
263
329
|
});
|
|
@@ -275,7 +341,7 @@ describe('useSessionConversationController backend run queue', () => {
|
|
|
275
341
|
submission = result.current.send();
|
|
276
342
|
});
|
|
277
343
|
|
|
278
|
-
expect(params.
|
|
344
|
+
expect(params.beginSubmission).toHaveBeenCalledTimes(1);
|
|
279
345
|
expect(result.current.queuedInputs).toEqual([
|
|
280
346
|
expect.objectContaining({
|
|
281
347
|
isSubmitting: true,
|
|
@@ -306,13 +372,64 @@ describe('useSessionConversationController backend run queue', () => {
|
|
|
306
372
|
});
|
|
307
373
|
|
|
308
374
|
expect(result.current.queuedInputs).toEqual([]);
|
|
309
|
-
expect(params.
|
|
310
|
-
text: 'next task',
|
|
311
|
-
}));
|
|
312
|
-
expect(params.setSendError).toHaveBeenLastCalledWith(sendError.message);
|
|
375
|
+
expect(params.failSubmission).toHaveBeenCalledWith(expect.any(String), 'uncertain', sendError.message);
|
|
313
376
|
expect(params.runQueue.refreshQueuedInputs).not.toHaveBeenCalled();
|
|
314
377
|
});
|
|
315
378
|
|
|
379
|
+
it('shows a send failure and restores the draft when the client returns no acceptance handle', async () => {
|
|
380
|
+
const send = createSendMock(null);
|
|
381
|
+
const params = createControllerParams({ isRunning: false, send });
|
|
382
|
+
const { result } = renderHook(() => useSessionConversationController(params));
|
|
383
|
+
|
|
384
|
+
await act(async () => {
|
|
385
|
+
await expect(result.current.send()).rejects.toThrow('Failed to send message');
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
expect(params.failSubmission).toHaveBeenCalledWith(expect.any(String), 'rejected', 'Failed to send message');
|
|
389
|
+
expect(params.onSessionMaterialized).not.toHaveBeenCalled();
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('classifies an explicit HTTP rejection so the draft can be restored', async () => {
|
|
393
|
+
const error = new NcpHttpSendError('invalid request', true);
|
|
394
|
+
const send = vi.fn<TestAgentSend>(async () => { throw error; });
|
|
395
|
+
const params = createControllerParams({ isRunning: false, send });
|
|
396
|
+
const { result } = renderHook(() => useSessionConversationController(params));
|
|
397
|
+
await act(async () => {
|
|
398
|
+
await expect(result.current.send()).rejects.toThrow(error);
|
|
399
|
+
});
|
|
400
|
+
expect(params.failSubmission).toHaveBeenCalledWith(expect.any(String), 'rejected', 'invalid request');
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it('rechecks the original message before retrying with the same identity', async () => {
|
|
404
|
+
fetchNcpSessionMessages.mockResolvedValue({ messages: [] });
|
|
405
|
+
const params = createControllerParams({ isRunning: false });
|
|
406
|
+
const envelope: NcpAgentSendEnvelope = {
|
|
407
|
+
sessionId: 'session-1', idempotencyKey: 'user-original',
|
|
408
|
+
message: {
|
|
409
|
+
id: 'user-original', sessionId: 'session-1', role: 'user', status: 'final',
|
|
410
|
+
timestamp: '2026-09-24T00:00:00.000Z', parts: [{ type: 'text', text: 'next task' }],
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
params.inputSnapshot.pendingSubmission = {
|
|
414
|
+
envelope,
|
|
415
|
+
composer: {
|
|
416
|
+
text: 'next task', nodes: [createTextNode('next task')],
|
|
417
|
+
selectedSkills: [], skillRecords: [], attachments: [],
|
|
418
|
+
},
|
|
419
|
+
status: 'uncertain',
|
|
420
|
+
};
|
|
421
|
+
const { result } = renderHook(() => useSessionConversationController(params));
|
|
422
|
+
expect(result.current.sendDisabled).toBe(true);
|
|
423
|
+
await act(async () => {
|
|
424
|
+
await result.current.send();
|
|
425
|
+
await result.current.retryPendingSubmission();
|
|
426
|
+
});
|
|
427
|
+
expect(fetchNcpSessionMessages).toHaveBeenCalledWith('session-1', { limit: 100 });
|
|
428
|
+
expect(params.agent.send).toHaveBeenCalledTimes(1);
|
|
429
|
+
expect(params.agent.send).toHaveBeenCalledWith(envelope);
|
|
430
|
+
expect(params.acceptSubmission).toHaveBeenCalledWith('user-original');
|
|
431
|
+
});
|
|
432
|
+
|
|
316
433
|
it('keeps an existing session bound to its agent instead of the global draft selection', async () => {
|
|
317
434
|
const send = createSendMock();
|
|
318
435
|
const params = createControllerParams({ isRunning: false, send });
|
|
@@ -406,7 +523,7 @@ describe('useSessionConversationController backend run queue', () => {
|
|
|
406
523
|
|
|
407
524
|
expect(params.agent.continueRun).toHaveBeenCalledWith({ sessionId: 'session-1' });
|
|
408
525
|
expect(params.agent.send).not.toHaveBeenCalled();
|
|
409
|
-
expect(params.
|
|
526
|
+
expect(params.beginSubmission).not.toHaveBeenCalled();
|
|
410
527
|
});
|
|
411
528
|
|
|
412
529
|
it('keeps continuation available but restores send as primary when the user types', () => {
|
|
@@ -36,7 +36,7 @@ const THINKING_MODEL_OPTIONS: ChatModelOption[] = [
|
|
|
36
36
|
describe('useSessionConversationInputState session preferences', () => {
|
|
37
37
|
beforeEach(() => {
|
|
38
38
|
window.localStorage.clear();
|
|
39
|
-
useChatComposerDraftStore.setState({ drafts: {} });
|
|
39
|
+
useChatComposerDraftStore.setState({ drafts: {}, submissions: {}, recentSubmittedNodes: {} });
|
|
40
40
|
useChatThreadStore.getState().setSnapshot({ draftProjectRoot: null });
|
|
41
41
|
});
|
|
42
42
|
|
|
@@ -213,7 +213,17 @@ describe('useSessionConversationInputState session preferences', () => {
|
|
|
213
213
|
selectedSkills: [],
|
|
214
214
|
skillRecords: [],
|
|
215
215
|
});
|
|
216
|
-
result.current.inputActions.
|
|
216
|
+
result.current.inputActions.beginSubmission({
|
|
217
|
+
sessionId: 'session-sent',
|
|
218
|
+
message: {
|
|
219
|
+
id: 'user-sent', sessionId: 'session-sent', role: 'user', status: 'final',
|
|
220
|
+
timestamp: '2026-09-24T00:00:00.000Z',
|
|
221
|
+
parts: [{ type: 'text', text: '已经发送的消息' }],
|
|
222
|
+
},
|
|
223
|
+
}, {
|
|
224
|
+
text: '已经发送的消息', nodes: [], selectedSkills: [], skillRecords: [], attachments: [],
|
|
225
|
+
});
|
|
226
|
+
result.current.inputActions.acceptSubmission('user-sent');
|
|
217
227
|
});
|
|
218
228
|
rerender({ sessionKey: 'session-unsent' });
|
|
219
229
|
act(() => {
|
package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts
CHANGED
|
@@ -5,12 +5,14 @@ import type {
|
|
|
5
5
|
UiNcpSessionQueuedInputView,
|
|
6
6
|
} from '@nextclaw/client-sdk';
|
|
7
7
|
import type { NcpAgentSendEnvelope, NcpRunHandle } from '@nextclaw/ncp';
|
|
8
|
+
import { NcpHttpSendError } from '@nextclaw/ncp-http-agent-client';
|
|
8
9
|
|
|
9
10
|
import {
|
|
10
11
|
createChatComposerNodesFromDraft,
|
|
11
12
|
deriveNcpMessagePartsFromComposer,
|
|
12
13
|
} from '@/features/chat/features/input/utils/chat-composer-state.utils';
|
|
13
14
|
import { isNcpChatSendDisabled } from '@/features/chat/features/input/utils/ncp-chat-input-availability.utils';
|
|
15
|
+
import { t } from '@/shared/lib/i18n';
|
|
14
16
|
import { buildChatRunMetadata } from '@/features/chat/features/session/utils/chat-run-metadata.utils';
|
|
15
17
|
import { createNcpSessionId } from '@/features/chat/features/session/utils/ncp-session-adapter.utils';
|
|
16
18
|
import type { SessionQueuedInputAttachmentPreview } from '@/features/chat/features/conversation/utils/session-queued-input.utils';
|
|
@@ -25,6 +27,7 @@ import {
|
|
|
25
27
|
useSessionConversationRecoveryActions,
|
|
26
28
|
type SessionConversationRecoveryAgent,
|
|
27
29
|
} from './use-session-conversation-recovery-actions';
|
|
30
|
+
import { useSessionSubmissionRecovery } from './use-session-submission-recovery';
|
|
28
31
|
|
|
29
32
|
type SessionConversationInputQuery = ReturnType<typeof useSessionConversationInputQuery>;
|
|
30
33
|
type ComposerDraftSnapshot = Pick<
|
|
@@ -80,8 +83,12 @@ type UseSessionConversationControllerParams = {
|
|
|
80
83
|
readonly selectedAgentId: string;
|
|
81
84
|
readonly sessionKey: string | null;
|
|
82
85
|
readonly onSessionMaterialized?: (sessionKey: string) => void;
|
|
83
|
-
readonly resetComposer: () => void;
|
|
84
86
|
readonly restoreComposer: (snapshot: ComposerDraftSnapshot) => void;
|
|
87
|
+
readonly beginSubmission: (envelope: NcpAgentSendEnvelope, composer: ComposerDraftSnapshot) => void;
|
|
88
|
+
readonly acceptSubmission: (messageId: string) => void;
|
|
89
|
+
readonly failSubmission: (messageId: string, status: 'uncertain' | 'rejected', message: string) => void;
|
|
90
|
+
readonly restoreSubmission: (messageId: string) => void;
|
|
91
|
+
readonly discardSubmission: (messageId: string) => void;
|
|
85
92
|
readonly setSendError: (message: string | null) => void;
|
|
86
93
|
};
|
|
87
94
|
|
|
@@ -90,7 +97,6 @@ type BuildSubmissionDraftParams = {
|
|
|
90
97
|
readonly composerSnapshot?: ComposerDraftSnapshot;
|
|
91
98
|
readonly inputSnapshot: SessionConversationInputSnapshot;
|
|
92
99
|
readonly inputQuery: SessionConversationInputQuery;
|
|
93
|
-
readonly isRuntimeBlocked: boolean;
|
|
94
100
|
readonly materializationContext?: SessionConversationMaterializationContext | null;
|
|
95
101
|
readonly selectedAgentId: string;
|
|
96
102
|
readonly sessionKey: string | null;
|
|
@@ -111,21 +117,12 @@ const resolveModelForSend = (value: string | null | undefined): string | undefin
|
|
|
111
117
|
return trimmed || undefined;
|
|
112
118
|
};
|
|
113
119
|
|
|
114
|
-
function buildInputAvailabilitySnapshot(inputQuery: SessionConversationInputQuery) {
|
|
115
|
-
return {
|
|
116
|
-
isProviderStateResolved: inputQuery.isProviderStateResolved,
|
|
117
|
-
modelOptions: inputQuery.modelOptions,
|
|
118
|
-
sessionTypeUnavailable: inputQuery.sessionTypeState.sessionTypeUnavailable,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
120
|
function buildSubmissionDraft(params: BuildSubmissionDraftParams): SubmissionDraft | null {
|
|
123
121
|
const {
|
|
124
122
|
agentIsSending,
|
|
125
123
|
composerSnapshot: requestedComposerSnapshot,
|
|
126
124
|
inputQuery,
|
|
127
125
|
inputSnapshot,
|
|
128
|
-
isRuntimeBlocked,
|
|
129
126
|
materializationContext,
|
|
130
127
|
selectedAgentId,
|
|
131
128
|
sessionKey,
|
|
@@ -142,10 +139,9 @@ function buildSubmissionDraft(params: BuildSubmissionDraftParams): SubmissionDra
|
|
|
142
139
|
composerSnapshot.attachments,
|
|
143
140
|
);
|
|
144
141
|
if (
|
|
142
|
+
inputSnapshot.pendingSubmission ||
|
|
145
143
|
isNcpChatSendDisabled({
|
|
146
|
-
snapshot: buildInputAvailabilitySnapshot(inputQuery),
|
|
147
144
|
hasSendableDraft: hasSendableMessagePart(currentParts),
|
|
148
|
-
isRuntimeBlocked,
|
|
149
145
|
}) ||
|
|
150
146
|
agentIsSending
|
|
151
147
|
) {
|
|
@@ -195,8 +191,12 @@ function buildSubmissionEnvelope(
|
|
|
195
191
|
sessionKey: string | null,
|
|
196
192
|
delivery: NcpAgentSendEnvelope['delivery'] = 'queue',
|
|
197
193
|
): NcpAgentSendEnvelope | null {
|
|
194
|
+
// getRandomValues is also available on HTTP origins, unlike randomUUID.
|
|
195
|
+
const messageIdBytes = crypto.getRandomValues(new Uint8Array(16));
|
|
196
|
+
const messageId = `user-${Array.from(messageIdBytes, (byte) => byte.toString(16).padStart(2, '0')).join('')}`;
|
|
198
197
|
const envelope = buildNcpRequestEnvelope({
|
|
199
198
|
sessionId: sessionKey ?? undefined,
|
|
199
|
+
messageId,
|
|
200
200
|
text: draft.composerSnapshot.text.trim(),
|
|
201
201
|
attachments: [...draft.composerSnapshot.attachments],
|
|
202
202
|
parts: deriveNcpMessagePartsFromComposer(
|
|
@@ -205,7 +205,7 @@ function buildSubmissionEnvelope(
|
|
|
205
205
|
),
|
|
206
206
|
metadata: draft.metadata,
|
|
207
207
|
});
|
|
208
|
-
return envelope ? { ...envelope, delivery } : null;
|
|
208
|
+
return envelope ? { ...envelope, delivery, idempotencyKey: envelope.message.id } : null;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
function useSubmissionDraftRunner(params: {
|
|
@@ -213,8 +213,9 @@ function useSubmissionDraftRunner(params: {
|
|
|
213
213
|
readonly clearSubmittingInput: (input: SubmittingQueuedInput) => void;
|
|
214
214
|
readonly materializationContext?: SessionConversationMaterializationContext | null;
|
|
215
215
|
readonly onSessionMaterialized?: (sessionKey: string) => void;
|
|
216
|
-
readonly
|
|
217
|
-
readonly
|
|
216
|
+
readonly beginSubmission: (envelope: NcpAgentSendEnvelope, composer: ComposerDraftSnapshot) => void;
|
|
217
|
+
readonly acceptSubmission: (messageId: string) => void;
|
|
218
|
+
readonly failSubmission: (messageId: string, status: 'uncertain' | 'rejected', message: string) => void;
|
|
218
219
|
readonly runQueue: SessionRunQueue;
|
|
219
220
|
readonly sessionKey: string | null;
|
|
220
221
|
readonly setSendError: (message: string | null) => void;
|
|
@@ -225,8 +226,9 @@ function useSubmissionDraftRunner(params: {
|
|
|
225
226
|
clearSubmittingInput,
|
|
226
227
|
materializationContext,
|
|
227
228
|
onSessionMaterialized,
|
|
228
|
-
|
|
229
|
-
|
|
229
|
+
beginSubmission,
|
|
230
|
+
acceptSubmission,
|
|
231
|
+
failSubmission,
|
|
230
232
|
runQueue,
|
|
231
233
|
sessionKey,
|
|
232
234
|
setSendError,
|
|
@@ -244,10 +246,16 @@ function useSubmissionDraftRunner(params: {
|
|
|
244
246
|
const queuedSubmission = agent.isRunning && delivery === 'queue'
|
|
245
247
|
? stageSubmittingInput(envelope)
|
|
246
248
|
: null;
|
|
247
|
-
if (composerBehavior === 'reset-and-restore')
|
|
249
|
+
if (composerBehavior === 'reset-and-restore') beginSubmission(envelope, draft.composerSnapshot);
|
|
248
250
|
setSendError(null);
|
|
251
|
+
let definitelyRejected = false;
|
|
249
252
|
try {
|
|
250
253
|
const handle = await agent.send(envelope);
|
|
254
|
+
if (!handle) {
|
|
255
|
+
definitelyRejected = true;
|
|
256
|
+
throw new Error(t('chatSendFailed'));
|
|
257
|
+
}
|
|
258
|
+
if (composerBehavior === 'reset-and-restore') acceptSubmission(envelope.message.id);
|
|
251
259
|
if (handle?.delivery === 'steered') {
|
|
252
260
|
await runQueue.refreshPendingInputs().catch(() => undefined);
|
|
253
261
|
} else if (handle?.delivery === 'queued') {
|
|
@@ -261,8 +269,12 @@ function useSubmissionDraftRunner(params: {
|
|
|
261
269
|
}
|
|
262
270
|
} catch (error) {
|
|
263
271
|
if (queuedSubmission) clearSubmittingInput(queuedSubmission);
|
|
264
|
-
|
|
265
|
-
|
|
272
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
273
|
+
if (composerBehavior === 'reset-and-restore') {
|
|
274
|
+
failSubmission(envelope.message.id,
|
|
275
|
+
definitelyRejected || (error instanceof NcpHttpSendError && error.sendRejected) ? 'rejected' : 'uncertain',
|
|
276
|
+
message);
|
|
277
|
+
} else setSendError(message);
|
|
266
278
|
throw error;
|
|
267
279
|
}
|
|
268
280
|
}, [
|
|
@@ -270,8 +282,9 @@ function useSubmissionDraftRunner(params: {
|
|
|
270
282
|
clearSubmittingInput,
|
|
271
283
|
materializationContext,
|
|
272
284
|
onSessionMaterialized,
|
|
273
|
-
|
|
274
|
-
|
|
285
|
+
beginSubmission,
|
|
286
|
+
acceptSubmission,
|
|
287
|
+
failSubmission,
|
|
275
288
|
runQueue,
|
|
276
289
|
sessionKey,
|
|
277
290
|
setSendError,
|
|
@@ -283,7 +296,6 @@ function useSteeringSubmission(params: {
|
|
|
283
296
|
readonly agent: SessionConversationAgent;
|
|
284
297
|
readonly inputSnapshot: SessionConversationInputSnapshot;
|
|
285
298
|
readonly inputQuery: SessionConversationInputQuery;
|
|
286
|
-
readonly isRuntimeBlocked: boolean;
|
|
287
299
|
readonly materializationContext?: SessionConversationMaterializationContext | null;
|
|
288
300
|
readonly selectedAgentId: string;
|
|
289
301
|
readonly sessionKey: string | null;
|
|
@@ -297,7 +309,6 @@ function useSteeringSubmission(params: {
|
|
|
297
309
|
agent,
|
|
298
310
|
inputQuery,
|
|
299
311
|
inputSnapshot,
|
|
300
|
-
isRuntimeBlocked,
|
|
301
312
|
materializationContext,
|
|
302
313
|
selectedAgentId,
|
|
303
314
|
sessionKey,
|
|
@@ -308,7 +319,6 @@ function useSteeringSubmission(params: {
|
|
|
308
319
|
agentIsSending: agent.isSending,
|
|
309
320
|
inputSnapshot,
|
|
310
321
|
inputQuery,
|
|
311
|
-
isRuntimeBlocked,
|
|
312
322
|
materializationContext,
|
|
313
323
|
selectedAgentId,
|
|
314
324
|
sessionKey,
|
|
@@ -318,7 +328,6 @@ function useSteeringSubmission(params: {
|
|
|
318
328
|
agent.isSending,
|
|
319
329
|
inputQuery,
|
|
320
330
|
inputSnapshot,
|
|
321
|
-
isRuntimeBlocked,
|
|
322
331
|
materializationContext,
|
|
323
332
|
selectedAgentId,
|
|
324
333
|
sessionKey,
|
|
@@ -331,7 +340,6 @@ function usePrimarySubmission(params: {
|
|
|
331
340
|
readonly continueRun: () => Promise<void>;
|
|
332
341
|
readonly inputSnapshot: SessionConversationInputSnapshot;
|
|
333
342
|
readonly inputQuery: SessionConversationInputQuery;
|
|
334
|
-
readonly isRuntimeBlocked: boolean;
|
|
335
343
|
readonly materializationContext?: SessionConversationMaterializationContext | null;
|
|
336
344
|
readonly primaryAction: 'continue' | 'send';
|
|
337
345
|
readonly selectedAgentId: string;
|
|
@@ -346,7 +354,6 @@ function usePrimarySubmission(params: {
|
|
|
346
354
|
continueRun,
|
|
347
355
|
inputQuery,
|
|
348
356
|
inputSnapshot,
|
|
349
|
-
isRuntimeBlocked,
|
|
350
357
|
materializationContext,
|
|
351
358
|
primaryAction,
|
|
352
359
|
selectedAgentId,
|
|
@@ -359,7 +366,6 @@ function usePrimarySubmission(params: {
|
|
|
359
366
|
agentIsSending: agent.isSending,
|
|
360
367
|
inputSnapshot,
|
|
361
368
|
inputQuery,
|
|
362
|
-
isRuntimeBlocked,
|
|
363
369
|
materializationContext,
|
|
364
370
|
selectedAgentId,
|
|
365
371
|
sessionKey,
|
|
@@ -370,7 +376,6 @@ function usePrimarySubmission(params: {
|
|
|
370
376
|
continueRun,
|
|
371
377
|
inputQuery,
|
|
372
378
|
inputSnapshot,
|
|
373
|
-
isRuntimeBlocked,
|
|
374
379
|
materializationContext,
|
|
375
380
|
primaryAction,
|
|
376
381
|
selectedAgentId,
|
|
@@ -379,6 +384,32 @@ function usePrimarySubmission(params: {
|
|
|
379
384
|
]);
|
|
380
385
|
}
|
|
381
386
|
|
|
387
|
+
function usePresetSubmission(params: {
|
|
388
|
+
readonly agent: SessionConversationAgent;
|
|
389
|
+
readonly inputSnapshot: SessionConversationInputSnapshot;
|
|
390
|
+
readonly inputQuery: SessionConversationInputQuery;
|
|
391
|
+
readonly materializationContext?: SessionConversationMaterializationContext | null;
|
|
392
|
+
readonly selectedAgentId: string;
|
|
393
|
+
readonly sessionKey: string | null;
|
|
394
|
+
readonly submitDraft: (draft: SubmissionDraft, behavior: SubmissionComposerBehavior) => Promise<void>;
|
|
395
|
+
}) {
|
|
396
|
+
const { agent, inputSnapshot, inputQuery, materializationContext, selectedAgentId, sessionKey, submitDraft } = params;
|
|
397
|
+
return useCallback(async (message: string) => {
|
|
398
|
+
const text = message.trim();
|
|
399
|
+
if (!text) return;
|
|
400
|
+
const draft = buildSubmissionDraft({
|
|
401
|
+
agentIsSending: agent.isSending,
|
|
402
|
+
composerSnapshot: {
|
|
403
|
+
text,
|
|
404
|
+
nodes: createChatComposerNodesFromDraft(text),
|
|
405
|
+
selectedSkills: [], skillRecords: [], attachments: [],
|
|
406
|
+
},
|
|
407
|
+
inputSnapshot, inputQuery, materializationContext, selectedAgentId, sessionKey,
|
|
408
|
+
});
|
|
409
|
+
if (draft) await submitDraft(draft, 'preserve');
|
|
410
|
+
}, [agent.isSending, inputQuery, inputSnapshot, materializationContext, selectedAgentId, sessionKey, submitDraft]);
|
|
411
|
+
}
|
|
412
|
+
|
|
382
413
|
export function useSessionConversationController(params: UseSessionConversationControllerParams) {
|
|
383
414
|
const {
|
|
384
415
|
agent,
|
|
@@ -390,8 +421,12 @@ export function useSessionConversationController(params: UseSessionConversationC
|
|
|
390
421
|
selectedAgentId,
|
|
391
422
|
sessionKey,
|
|
392
423
|
onSessionMaterialized,
|
|
393
|
-
resetComposer,
|
|
394
424
|
restoreComposer,
|
|
425
|
+
beginSubmission,
|
|
426
|
+
acceptSubmission,
|
|
427
|
+
failSubmission,
|
|
428
|
+
restoreSubmission,
|
|
429
|
+
discardSubmission,
|
|
395
430
|
setSendError,
|
|
396
431
|
} = params;
|
|
397
432
|
const {
|
|
@@ -421,12 +456,10 @@ export function useSessionConversationController(params: UseSessionConversationC
|
|
|
421
456
|
);
|
|
422
457
|
const primaryAction: 'continue' | 'send' =
|
|
423
458
|
canContinue && !hasSendableDraft ? 'continue' : 'send';
|
|
424
|
-
const sendDisabled = primaryAction === 'continue'
|
|
459
|
+
const sendDisabled = inputSnapshot.pendingSubmission ? true : primaryAction === 'continue'
|
|
425
460
|
? false
|
|
426
461
|
: isNcpChatSendDisabled({
|
|
427
|
-
snapshot: buildInputAvailabilitySnapshot(inputQuery),
|
|
428
462
|
hasSendableDraft,
|
|
429
|
-
isRuntimeBlocked,
|
|
430
463
|
}) || agent.isSending;
|
|
431
464
|
|
|
432
465
|
const { continueRun, editMessage } = useSessionConversationRecoveryActions({
|
|
@@ -440,8 +473,9 @@ export function useSessionConversationController(params: UseSessionConversationC
|
|
|
440
473
|
clearSubmittingInput,
|
|
441
474
|
materializationContext,
|
|
442
475
|
onSessionMaterialized,
|
|
443
|
-
|
|
444
|
-
|
|
476
|
+
beginSubmission,
|
|
477
|
+
acceptSubmission,
|
|
478
|
+
failSubmission,
|
|
445
479
|
runQueue,
|
|
446
480
|
sessionKey,
|
|
447
481
|
setSendError,
|
|
@@ -453,7 +487,6 @@ export function useSessionConversationController(params: UseSessionConversationC
|
|
|
453
487
|
continueRun,
|
|
454
488
|
inputQuery,
|
|
455
489
|
inputSnapshot,
|
|
456
|
-
isRuntimeBlocked,
|
|
457
490
|
materializationContext,
|
|
458
491
|
primaryAction,
|
|
459
492
|
selectedAgentId,
|
|
@@ -465,52 +498,33 @@ export function useSessionConversationController(params: UseSessionConversationC
|
|
|
465
498
|
agent,
|
|
466
499
|
inputQuery,
|
|
467
500
|
inputSnapshot,
|
|
468
|
-
isRuntimeBlocked,
|
|
469
501
|
materializationContext,
|
|
470
502
|
selectedAgentId,
|
|
471
503
|
sessionKey,
|
|
472
504
|
submitDraft,
|
|
473
505
|
});
|
|
474
506
|
|
|
475
|
-
const sendPresetMessage =
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
}
|
|
480
|
-
const draft = buildSubmissionDraft({
|
|
481
|
-
agentIsSending: agent.isSending,
|
|
482
|
-
composerSnapshot: {
|
|
483
|
-
text,
|
|
484
|
-
nodes: createChatComposerNodesFromDraft(text),
|
|
485
|
-
selectedSkills: [],
|
|
486
|
-
skillRecords: [],
|
|
487
|
-
attachments: [],
|
|
488
|
-
},
|
|
489
|
-
inputSnapshot,
|
|
490
|
-
inputQuery,
|
|
491
|
-
isRuntimeBlocked,
|
|
492
|
-
materializationContext,
|
|
493
|
-
selectedAgentId,
|
|
494
|
-
sessionKey,
|
|
495
|
-
});
|
|
496
|
-
if (draft) {
|
|
497
|
-
await submitDraft(draft, 'preserve');
|
|
498
|
-
}
|
|
499
|
-
}, [
|
|
500
|
-
agent.isSending,
|
|
501
|
-
inputQuery,
|
|
502
|
-
inputSnapshot,
|
|
503
|
-
isRuntimeBlocked,
|
|
504
|
-
materializationContext,
|
|
505
|
-
selectedAgentId,
|
|
506
|
-
sessionKey,
|
|
507
|
-
submitDraft,
|
|
508
|
-
]);
|
|
507
|
+
const sendPresetMessage = usePresetSubmission({
|
|
508
|
+
agent, inputSnapshot, inputQuery, materializationContext,
|
|
509
|
+
selectedAgentId, sessionKey, submitDraft,
|
|
510
|
+
});
|
|
509
511
|
|
|
510
512
|
const stop = useCallback(async () => {
|
|
511
513
|
await agent.abort();
|
|
512
514
|
}, [agent]);
|
|
513
515
|
|
|
516
|
+
const submissionRecovery = useSessionSubmissionRecovery({
|
|
517
|
+
agent,
|
|
518
|
+
submission: inputSnapshot.pendingSubmission,
|
|
519
|
+
sessionKey,
|
|
520
|
+
onSessionMaterialized,
|
|
521
|
+
acceptSubmission,
|
|
522
|
+
failSubmission,
|
|
523
|
+
restoreSubmission,
|
|
524
|
+
discardSubmission,
|
|
525
|
+
setSendError,
|
|
526
|
+
});
|
|
527
|
+
|
|
514
528
|
return {
|
|
515
529
|
canContinue,
|
|
516
530
|
canStopGeneration: agent.isRunning,
|
|
@@ -525,6 +539,7 @@ export function useSessionConversationController(params: UseSessionConversationC
|
|
|
525
539
|
send,
|
|
526
540
|
sendSteering,
|
|
527
541
|
sendPresetMessage,
|
|
542
|
+
...submissionRecovery,
|
|
528
543
|
sendDisabled,
|
|
529
544
|
primaryAction,
|
|
530
545
|
stop,
|
package/src/features/chat/features/conversation/hooks/use-session-conversation-input-query.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
filterNcpChatModelOptionsBySessionType,
|
|
13
13
|
} from '@/features/chat/features/ncp/utils/ncp-chat-query-derived.utils';
|
|
14
14
|
import { adaptNcpSessionSummaries } from '@/features/chat/features/session/utils/ncp-session-adapter.utils';
|
|
15
|
-
import {
|
|
15
|
+
import { resolveRecentSessionPreferenceSnapshot } from '@/features/chat/features/session/utils/chat-session-preference-governance.utils';
|
|
16
16
|
import { useChatSessionTypeState } from '@/features/chat/features/session-type/hooks/use-chat-session-type-state';
|
|
17
17
|
import { chatRecentModelsManager } from '@/features/chat/managers/chat-recent-models.manager';
|
|
18
18
|
import { useChatQueryStore } from '@/features/chat/stores/ncp-chat-query.store';
|
|
@@ -112,27 +112,17 @@ export function useSessionConversationInputQuery(params: UseSessionConversationI
|
|
|
112
112
|
const recentSessionTypeModel = chatRecentModelsManager
|
|
113
113
|
.read({ namespace: sessionTypeState.selectedSessionType })
|
|
114
114
|
.find((value) => availableModelValueSet.has(value));
|
|
115
|
-
const
|
|
115
|
+
const recentSessionPreferences = useMemo(
|
|
116
116
|
() =>
|
|
117
|
-
|
|
118
|
-
resolveRecentSessionPreferredValue<string>({
|
|
117
|
+
resolveRecentSessionPreferenceSnapshot({
|
|
119
118
|
sessions,
|
|
120
119
|
selectedSessionKey,
|
|
121
120
|
sessionType: sessionTypeState.selectedSessionType,
|
|
122
|
-
readPreference: (session) => session.preferredModel?.trim() || undefined,
|
|
123
|
-
}),
|
|
124
|
-
[recentSessionTypeModel, selectedSessionKey, sessionTypeState.selectedSessionType, sessions],
|
|
125
|
-
);
|
|
126
|
-
const fallbackPreferredThinking = useMemo(
|
|
127
|
-
() =>
|
|
128
|
-
resolveRecentSessionPreferredValue({
|
|
129
|
-
sessions,
|
|
130
|
-
selectedSessionKey,
|
|
131
|
-
sessionType: sessionTypeState.selectedSessionType,
|
|
132
|
-
readPreference: (session) => session.preferredThinking ?? undefined,
|
|
133
121
|
}),
|
|
134
122
|
[selectedSessionKey, sessionTypeState.selectedSessionType, sessions],
|
|
135
123
|
);
|
|
124
|
+
const fallbackPreferredModel = recentSessionPreferences?.model ?? recentSessionTypeModel;
|
|
125
|
+
const fallbackPreferredThinking = recentSessionPreferences?.thinking;
|
|
136
126
|
|
|
137
127
|
const defaultProjectRoot = normalizeSessionProjectRootValue(
|
|
138
128
|
config?.agents.defaults.workspace,
|