@nextclaw/ui 0.19.2 → 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.
Files changed (249) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/dist/assets/{abnfDiagram-VRR7QNED-DvTjgVMf.js → abnfDiagram-VRR7QNED-DrbfiNNF.js} +1 -1
  3. package/dist/assets/{aiden0z-pptx-renderer.es-C1nfBAXR.js → aiden0z-pptx-renderer.es-DdLb2IVc.js} +1 -1
  4. package/dist/assets/{api-BH6Oslx4.js → api-CUSlBpt_.js} +3 -3
  5. package/dist/assets/{appearance-settings-page-zhgfcGgW.js → appearance-settings-page-cTuZdROP.js} +1 -1
  6. package/dist/assets/{arc-BvhODBXv.js → arc-BXlMc9B0.js} +1 -1
  7. package/dist/assets/architecture-TIHT7OUA-Bq0i9WiE.js +1 -0
  8. package/dist/assets/{architectureDiagram-ZJ3FMSHR-BKO9RgNS.js → architectureDiagram-ZJ3FMSHR-DXAJxxO0.js} +1 -1
  9. package/dist/assets/{blockDiagram-677ZJIJ3-DHpDXy7D.js → blockDiagram-677ZJIJ3-i4Kz7zU0.js} +1 -1
  10. package/dist/assets/{book-open-DGTS6oEO.js → book-open-BJ-xAIp9.js} +1 -1
  11. package/dist/assets/{c4Diagram-LMCZKHZV-BG_iQnDr.js → c4Diagram-LMCZKHZV-BDc0TkPd.js} +1 -1
  12. package/dist/assets/channel-iCaZQ87u.js +1 -0
  13. package/dist/assets/{channels-list-page-3D5lhZoT.js → channels-list-page-nRHOasWA.js} +2 -2
  14. package/dist/assets/chunk-2Q5K7J3B-DFQtlnJS.js +1 -0
  15. package/dist/assets/{chunk-32BRIVSS-DR9M0LLx.js → chunk-32BRIVSS-DuReei0Z.js} +1 -1
  16. package/dist/assets/{chunk-52WLFC77-698iC3BB.js → chunk-52WLFC77-D8APq0tm.js} +1 -1
  17. package/dist/assets/{chunk-5FCAYU7R-DikTj9Ey.js → chunk-5FCAYU7R-BsPrNXNS.js} +1 -1
  18. package/dist/assets/{chunk-5HE753X5-ko0o7_q0.js → chunk-5HE753X5-BRtkOPoJ.js} +1 -1
  19. package/dist/assets/{chunk-5JV3BV7I-CEHufhgo.js → chunk-5JV3BV7I-ChPc8e8U.js} +1 -1
  20. package/dist/assets/{chunk-5TONJI2A-CGgno1cu.js → chunk-5TONJI2A-CzTjlJGb.js} +1 -1
  21. package/dist/assets/{chunk-5VM5RSS4-DkiB4cUQ.js → chunk-5VM5RSS4-xsv_22jR.js} +1 -1
  22. package/dist/assets/{chunk-7BUUIJ7U-DzptcytQ.js → chunk-7BUUIJ7U-CwECujme.js} +1 -1
  23. package/dist/assets/{chunk-BIQX33UG-DMRSQJvT.js → chunk-BIQX33UG-DQAm173p.js} +1 -1
  24. package/dist/assets/{chunk-C7G6YPKG-Bw1BwwuY.js → chunk-C7G6YPKG-BkpajeQb.js} +1 -1
  25. package/dist/assets/{chunk-CQNSW5MT-C0z6eHBk.js → chunk-CQNSW5MT-ND65pWRA.js} +1 -1
  26. package/dist/assets/{chunk-CYSBUYHQ-ByPyAouO.js → chunk-CYSBUYHQ-vMu6ijxG.js} +1 -1
  27. package/dist/assets/{chunk-EMLP6XTP-CJlAXWZg.js → chunk-EMLP6XTP-CEaeW3gH.js} +1 -1
  28. package/dist/assets/{chunk-EX3LRPZG-Dqq4zKJu.js → chunk-EX3LRPZG-a4ER-HDr.js} +1 -1
  29. package/dist/assets/{chunk-FWX5IMBZ-B0SNoCmi.js → chunk-FWX5IMBZ-C_8fQssn.js} +2 -2
  30. package/dist/assets/{chunk-HOUHSVGY-DG_GJih_.js → chunk-HOUHSVGY-CEvw-PEd.js} +1 -1
  31. package/dist/assets/{chunk-ICXQ74PX-Drxk_6bE.js → chunk-ICXQ74PX-BEtF0fGY.js} +1 -1
  32. package/dist/assets/{chunk-JG7HCLWE-DdLFPpnd.js → chunk-JG7HCLWE-DGRrVp8-.js} +1 -1
  33. package/dist/assets/{chunk-JWPE2WC7-B8M4G76F.js → chunk-JWPE2WC7-DRqpp_nM.js} +1 -1
  34. package/dist/assets/{chunk-MOJQB5TN-XP6FJHY_.js → chunk-MOJQB5TN-BDEriOXs.js} +1 -1
  35. package/dist/assets/{chunk-MOZMSUNE-C_6x18Qw.js → chunk-MOZMSUNE-JWRNzdnw.js} +1 -1
  36. package/dist/assets/{chunk-OGEWGWER-N25BzCmG.js → chunk-OGEWGWER-DS2VTgCx.js} +1 -1
  37. package/dist/assets/{chunk-OSBZ3O6U-ASDEm7y0.js → chunk-OSBZ3O6U-3LiYTBUB.js} +1 -1
  38. package/dist/assets/{chunk-PUDLZKDR-CTlvAcn7.js → chunk-PUDLZKDR-o9M2s_SO.js} +1 -1
  39. package/dist/assets/{chunk-Q4XR5HBZ-Da9BWfB6.js → chunk-Q4XR5HBZ-DTB7Ma2N.js} +1 -1
  40. package/dist/assets/{chunk-QBLGF6JB-Bi6RpTJO.js → chunk-QBLGF6JB-DpkxZJL5.js} +1 -1
  41. package/dist/assets/{chunk-R7FJI6CG-DkMsSPvK.js → chunk-R7FJI6CG-CRejkHJU.js} +1 -1
  42. package/dist/assets/{chunk-RYQCIY6F-DZp0eU-j.js → chunk-RYQCIY6F-B1boBjLt.js} +1 -1
  43. package/dist/assets/{chunk-U6XO7XAA-vfinDSrj.js → chunk-U6XO7XAA-DgKa97bj.js} +1 -1
  44. package/dist/assets/{chunk-V7JOEXUC-C9Nksccs.js → chunk-V7JOEXUC-DE56eh0N.js} +1 -1
  45. package/dist/assets/chunk-VAUOI2AC-CF9A-TeE.js +1 -0
  46. package/dist/assets/{chunk-VR4S4FIN-Xv6zfwXs.js → chunk-VR4S4FIN-BzomPZq2.js} +1 -1
  47. package/dist/assets/{chunk-WYO6CB5R-mOJNzbWl.js → chunk-WYO6CB5R-C6gy8rSd.js} +2 -2
  48. package/dist/assets/chunk-XXDRQBXY-BRxdfVCU.js +1 -0
  49. package/dist/assets/{chunk-YOTPTUD7-1-aKSu4o.js → chunk-YOTPTUD7-BK98yfl9.js} +1 -1
  50. package/dist/assets/{chunk-ZGVPDNZ5-OJ8veMpK.js → chunk-ZGVPDNZ5-DFXAhwUT.js} +1 -1
  51. package/dist/assets/{chunk-ZIRB5QZD-SR_Nq_PP.js → chunk-ZIRB5QZD-Bbu6BiNC.js} +1 -1
  52. package/dist/assets/classDiagram-OUVF2IWQ-Bo28fPi-.js +1 -0
  53. package/dist/assets/classDiagram-v2-EOCWNBFH-BOVN9wUz.js +1 -0
  54. package/dist/assets/{config-split-page-B55XD_ZY.js → config-split-page-DO1Oyb-r.js} +1 -1
  55. package/dist/assets/{confirm-dialog-BCCreI_f.js → confirm-dialog-CCae9Fr4.js} +1 -1
  56. package/dist/assets/{cose-bilkent-JH36ORCC-NjVgz2Bw.js → cose-bilkent-JH36ORCC-BV8KR4uF.js} +1 -1
  57. package/dist/assets/cynefin-VYW2F7L2-B7rF3JRp.js +1 -0
  58. package/dist/assets/{cynefinDiagram-TSTJHNR4-B_17LDqD.js → cynefinDiagram-TSTJHNR4-Ckd6nU9t.js} +1 -1
  59. package/dist/assets/{dagre-CMe101gk.js → dagre-D_T-FW5i.js} +1 -1
  60. package/dist/assets/{dagre-VKFMJZFB-qo8nLb3B.js → dagre-VKFMJZFB-CyDaQpD4.js} +1 -1
  61. package/dist/assets/desktop-update-config-XSV-DjJW.js +1 -0
  62. package/dist/assets/{diagram-FQU43EPY-Ba3XIklC.js → diagram-FQU43EPY-CghnwS0H.js} +1 -1
  63. package/dist/assets/{diagram-G47NLZAW-CG45TcUx.js → diagram-G47NLZAW-DK4DJ3Kx.js} +1 -1
  64. package/dist/assets/{diagram-NH7WQ7WH-2F2HrwV-.js → diagram-NH7WQ7WH-CUgDkZCJ.js} +1 -1
  65. package/dist/assets/{diagram-OA4YK3LP-CXfWH9NZ.js → diagram-OA4YK3LP-vO2gyH6n.js} +1 -1
  66. package/dist/assets/{diagram-WEI45ONY-CH_4yepc.js → diagram-WEI45ONY-DbSBRDSr.js} +1 -1
  67. package/dist/assets/{dist-IRWcJpfm.js → dist-1N9UpL3c.js} +1 -1
  68. package/dist/assets/{dist-BYSUQaO2.js → dist-C-2H76Cv.js} +1 -1
  69. package/dist/assets/doc-browser-BHFCZIoL.js +1 -0
  70. package/dist/assets/{doc-browser-D8rNrWHk.js → doc-browser-CRlpqMwH.js} +1 -1
  71. package/dist/assets/{doc-browser-context-DjTbIbQc.js → doc-browser-context-C0Bns1J6.js} +1 -1
  72. package/dist/assets/{docx-preview-CoM4JK7_.js → docx-preview-DfxIm9IB.js} +1 -1
  73. package/dist/assets/{ebnfDiagram-CCIWWBDH-BwiG4Ea1.js → ebnfDiagram-CCIWWBDH-W7CAcK7r.js} +1 -1
  74. package/dist/assets/{erDiagram-Q63AITRT-BML3hpnQ.js → erDiagram-Q63AITRT-LA0JWL8-.js} +1 -1
  75. package/dist/assets/eventmodeling-45OFAUF4-DzQLXa3s.js +1 -0
  76. package/dist/assets/flowDiagram-23GEKE2U-C81eexJZ.js +1 -0
  77. package/dist/assets/{form-actions-C8hazgNP.js → form-actions-CMxhPact.js} +1 -1
  78. package/dist/assets/{ganttDiagram-NO4QXBWP-80enfWbD.js → ganttDiagram-NO4QXBWP-_PiTPavJ.js} +1 -1
  79. package/dist/assets/gitGraph-TEB2WS4Q-Dt3ZqpFD.js +1 -0
  80. package/dist/assets/{gitGraphDiagram-IHSO6WYX-CVLqXr-b.js → gitGraphDiagram-IHSO6WYX-DsRB1ZMU.js} +1 -1
  81. package/dist/assets/index-DdYOaKm7.js +109 -0
  82. package/dist/assets/index-DxCGKhAw.css +1 -0
  83. package/dist/assets/info-DKCQHKI2-CCCE7dXC.js +1 -0
  84. package/dist/assets/{infoDiagram-FWYZ7A6U-Dyvs2t0i.js → infoDiagram-FWYZ7A6U-wIRraYx5.js} +1 -1
  85. package/dist/assets/{ishikawaDiagram-FXEZZL3T-sSnxDKfh.js → ishikawaDiagram-FXEZZL3T-CNzsrcrh.js} +1 -1
  86. package/dist/assets/{journeyDiagram-5HDEW3XC-D22V71Mf.js → journeyDiagram-5HDEW3XC-Dy9huHxy.js} +1 -1
  87. package/dist/assets/{kanban-definition-HUTT4EX6-D3ds0p-g.js → kanban-definition-HUTT4EX6-BOCIN365.js} +1 -1
  88. package/dist/assets/{line-CbyFs065.js → line-BxsizOTs.js} +1 -1
  89. package/dist/assets/{linear-4DKtnZBS.js → linear-C_YHf92I.js} +1 -1
  90. package/dist/assets/loader-circle-B-UdXjl4.js +1 -0
  91. package/dist/assets/{map-CcZzEoz6.js → map-CobcjgZ8.js} +1 -1
  92. package/dist/assets/mcp-marketplace-page-BUIGtSda.js +1 -0
  93. package/dist/assets/mcp-marketplace-page-CStb8ml2.js +9 -0
  94. package/dist/assets/{mermaid-parser.core-DPmMOjXD.js → mermaid-parser.core-D7lu2RbA.js} +2 -2
  95. package/dist/assets/{mermaid.core-BSf-ddRa.js → mermaid.core-CbfW2Rk8.js} +3 -3
  96. package/dist/assets/{mindmap-definition-LN4V7U3C-BUTwWPyx.js → mindmap-definition-LN4V7U3C-DUjGXlIC.js} +1 -1
  97. package/dist/assets/model-config-page-BkfUYm95.js +1 -0
  98. package/dist/assets/{navigation-link-kGAcL2N2.js → navigation-link-v3FaWcIr.js} +1 -1
  99. package/dist/assets/{ordinal-BgLYtOZE.js → ordinal-CjoUCHto.js} +1 -1
  100. package/dist/assets/packet-7NZHBO7P-D19yoxFV.js +1 -0
  101. package/dist/assets/{pegDiagram-2B236MQR-BEu_ZBjc.js → pegDiagram-2B236MQR-BQY2DgiP.js} +1 -1
  102. package/dist/assets/pie-RZYD4A2V-hMaJbLrn.js +1 -0
  103. package/dist/assets/{pieDiagram-ENE6RG2P-CAwAwuBd.js → pieDiagram-ENE6RG2P-D18owe4D.js} +1 -1
  104. package/dist/assets/plus-Dw9QFnKr.js +1 -0
  105. package/dist/assets/{privacy-settings-page-v9LTFvJe.js → privacy-settings-page-CYM1Dxwo.js} +1 -1
  106. package/dist/assets/providers-config-page-fxL4I4bz.js +1 -0
  107. package/dist/assets/{quadrantDiagram-ABIIQ3AL-CwE6F-_2.js → quadrantDiagram-ABIIQ3AL-B8F7YoTA.js} +1 -1
  108. package/dist/assets/radar-I7S5WNFK-C8BkvScA.js +1 -0
  109. package/dist/assets/railroad-3IZDKUUU-CozZOitC.js +1 -0
  110. package/dist/assets/railroad-abnf-AHOZXSZD-BqIoFXFi.js +1 -0
  111. package/dist/assets/railroad-ebnf-EBAXGLYW-CD2e3dng.js +1 -0
  112. package/dist/assets/railroad-peg-LSFZ7HO6-DoKlRUOF.js +1 -0
  113. package/dist/assets/{railroadDiagram-RFXS5EU6-xgLekU3B.js → railroadDiagram-RFXS5EU6-RU8GpxN5.js} +1 -1
  114. package/dist/assets/{react-Dwvp5yeX.js → react-Dlz_IxoP.js} +1 -1
  115. package/dist/assets/{refresh-cw-C_oj_h_s.js → refresh-cw-CK4I5UFv.js} +1 -1
  116. package/dist/assets/remote-B0pPgtC6.js +1 -0
  117. package/dist/assets/{requirementDiagram-TGXJPOKE-CyVUzEX5.js → requirementDiagram-TGXJPOKE-ubPOmmNi.js} +1 -1
  118. package/dist/assets/rotate-cw-Z48dmh13.js +1 -0
  119. package/dist/assets/runtime-config-page-C7vksFVD.js +1 -0
  120. package/dist/assets/{sankeyDiagram-HTMAVEWB-JK6bFmYB.js → sankeyDiagram-HTMAVEWB-8L_hXJWW.js} +1 -1
  121. package/dist/assets/{save-BZE6H02N.js → save-Bo37cIFb.js} +1 -1
  122. package/dist/assets/{search-C7Tj40kC.js → search-CZiEm7YD.js} +1 -1
  123. package/dist/assets/search-config-page-DKdEf7m9.js +1 -0
  124. package/dist/assets/{secrets-config-page-B__VLAEQ.js → secrets-config-page-COlMqSfL.js} +2 -2
  125. package/dist/assets/security-config-CitLihXA.js +1 -0
  126. package/dist/assets/{select-fZ5tifar.js → select-BACzapV3.js} +1 -1
  127. package/dist/assets/{sequenceDiagram-DBY2YBRQ-BR5IokXV.js → sequenceDiagram-DBY2YBRQ-Dom6Qor5.js} +1 -1
  128. package/dist/assets/{sizeCapture-X5ZJPWSS-SxG_YnMA.js → sizeCapture-X5ZJPWSS-cZcFSpLJ.js} +1 -1
  129. package/dist/assets/skeleton-B2PCAZJG.js +1 -0
  130. package/dist/assets/{src-vgz8E_-O.js → src-BE9E1xcR.js} +1 -1
  131. package/dist/assets/{stateDiagram-2N3HPSRC-DD4OoYhn.js → stateDiagram-2N3HPSRC-CHrpxmGO.js} +1 -1
  132. package/dist/assets/stateDiagram-v2-6OUMAXLB-b1pZQlDD.js +1 -0
  133. package/dist/assets/{swimlanes-5IMT3BWC-BWkFeyCT.js → swimlanes-5IMT3BWC-D6nJS2xQ.js} +2 -2
  134. package/dist/assets/swimlanesDiagram-G3AALYLV-CowxgWVn.js +8 -0
  135. package/dist/assets/{tag-chip-CwYZcRoe.js → tag-chip-BnamTQVX.js} +1 -1
  136. package/dist/assets/{timeline-definition-FHXFAJF6-VTRPy6KA.js → timeline-definition-FHXFAJF6-DJNn-HQO.js} +1 -1
  137. package/dist/assets/treeView-QDETBFTQ-BQlYkJbz.js +1 -0
  138. package/dist/assets/treemap-6X3UGDF4-DzTOd1bd.js +1 -0
  139. package/dist/assets/{vennDiagram-L72KCM5P-Bvk42Pek.js → vennDiagram-L72KCM5P-CCEzHoXt.js} +1 -1
  140. package/dist/assets/wardley-OPB4EBWU-D80jQc0y.js +1 -0
  141. package/dist/assets/{wardleyDiagram-EHGQE667-DOFjx2s3.js → wardleyDiagram-EHGQE667-BhyVxVXt.js} +1 -1
  142. package/dist/assets/x-BzYEzkLA.js +1 -0
  143. package/dist/assets/{xychartDiagram-FW5EYKEG-DY5yRTUc.js → xychartDiagram-FW5EYKEG-CacW_246.js} +1 -1
  144. package/dist/index.html +70 -22
  145. package/index.html +49 -1
  146. package/package.json +8 -8
  147. package/src/app/index.tsx +1 -3
  148. package/src/app/managers/__tests__/viewport-layout.manager.test.ts +12 -0
  149. package/src/app/managers/viewport-layout.manager.ts +12 -0
  150. package/src/app/stores/viewport-layout.store.ts +23 -11
  151. package/src/features/apps/components/__tests__/app-marketplace-catalog.test.tsx +161 -0
  152. package/src/features/apps/components/app-marketplace-catalog.tsx +36 -7
  153. package/src/features/apps/components/app-marketplace-detail.tsx +29 -6
  154. package/src/features/apps/components/app-marketplace-dialog.tsx +71 -55
  155. package/src/features/apps/components/app-marketplace-operation.tsx +128 -16
  156. package/src/features/apps/components/app-packages-panel.tsx +1 -0
  157. package/src/features/apps/utils/app-marketplace-platform.utils.test.ts +59 -0
  158. package/src/features/apps/utils/app-marketplace-platform.utils.ts +33 -0
  159. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +6 -0
  160. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +6 -2
  161. package/src/features/chat/features/conversation/components/__tests__/session-queued-input-rows.test.tsx +46 -0
  162. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +15 -2
  163. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +5 -3
  164. package/src/features/chat/features/conversation/components/session-queued-input-rows.tsx +59 -5
  165. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +35 -1
  166. package/src/features/chat/features/conversation/hooks/__tests__/use-session-run-queue.test.tsx +25 -19
  167. package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +148 -134
  168. package/src/features/chat/features/conversation/hooks/use-session-pending-input-actions.ts +135 -0
  169. package/src/features/chat/features/conversation/hooks/use-session-run-queue.ts +19 -4
  170. package/src/features/chat/features/conversation/utils/__tests__/session-queued-input.utils.test.ts +52 -2
  171. package/src/features/chat/features/conversation/utils/session-queued-input.utils.ts +46 -5
  172. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +1 -0
  173. package/src/features/chat/features/ncp/hooks/__tests__/use-hydrated-ncp-agent.test.tsx +102 -0
  174. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-session-conversation.test.tsx +117 -1
  175. package/src/features/chat/features/ncp/hooks/use-ncp-session-conversation.ts +2 -2
  176. package/src/features/chat/features/ncp/hooks/use-ncp-session-message-history.ts +31 -81
  177. package/src/features/chat/features/ncp/hooks/use-ncp-session-seed-loader.ts +134 -0
  178. package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +1 -6
  179. package/src/features/panel-apps/components/__tests__/panel-app-list-item.test.tsx +8 -0
  180. package/src/features/panel-apps/components/__tests__/panel-app-main-sidebar-nav.test.tsx +99 -13
  181. package/src/features/panel-apps/components/panel-app-main-sidebar-menu-item.tsx +4 -0
  182. package/src/features/panel-apps/components/panel-app-main-sidebar-nav.tsx +166 -31
  183. package/src/shared/lib/api/utils/ncp-attachments.utils.ts +8 -1
  184. package/src/shared/lib/api/utils/ncp-session.utils.ts +1 -0
  185. package/src/shared/lib/i18n/locales/en-US/chat.json +3 -0
  186. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +11 -1
  187. package/src/shared/lib/i18n/locales/zh-CN/chat.json +3 -0
  188. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +11 -1
  189. package/src/vite-env.d.ts +9 -0
  190. package/dist/assets/architecture-TIHT7OUA-Bjp9jf0h.js +0 -1
  191. package/dist/assets/channel-5T3pdR-s.js +0 -1
  192. package/dist/assets/chat-page-BXu3hnOK.js +0 -1
  193. package/dist/assets/chunk-2Q5K7J3B-DTZ3tGfE.js +0 -1
  194. package/dist/assets/chunk-VAUOI2AC-HLGIHQ6k.js +0 -1
  195. package/dist/assets/chunk-XXDRQBXY-Dl0m6Ni-.js +0 -1
  196. package/dist/assets/classDiagram-OUVF2IWQ-DcDnzGNX.js +0 -1
  197. package/dist/assets/classDiagram-v2-EOCWNBFH-BKXKMzKM.js +0 -1
  198. package/dist/assets/cynefin-VYW2F7L2-BWg7U2UD.js +0 -1
  199. package/dist/assets/desktop-update-config-Blol-Umu.js +0 -1
  200. package/dist/assets/doc-browser-BExPq1Hk.js +0 -1
  201. package/dist/assets/eventmodeling-45OFAUF4-SB-J4tpq.js +0 -1
  202. package/dist/assets/flowDiagram-23GEKE2U-BQ_u-eLM.js +0 -1
  203. package/dist/assets/gitGraph-TEB2WS4Q-DPxNwsFd.js +0 -1
  204. package/dist/assets/index-Btj8yN4T.css +0 -1
  205. package/dist/assets/index-CHcGqqBe.js +0 -109
  206. package/dist/assets/info-DKCQHKI2-DNlNGH90.js +0 -1
  207. package/dist/assets/loader-circle-C75DUmmm.js +0 -1
  208. package/dist/assets/mcp-marketplace-page-CIMRYgOY.js +0 -9
  209. package/dist/assets/mcp-marketplace-page-D2NlhG1k.js +0 -1
  210. package/dist/assets/model-config-page-ChR-ENjp.js +0 -1
  211. package/dist/assets/packet-7NZHBO7P-BpeiqkBZ.js +0 -1
  212. package/dist/assets/pie-RZYD4A2V-Cy1yxsPd.js +0 -1
  213. package/dist/assets/plus-TzSIMKWW.js +0 -1
  214. package/dist/assets/provider-scoped-model-input-DiSpKSk0.js +0 -1
  215. package/dist/assets/providers-config-page-BvXa03cp.js +0 -1
  216. package/dist/assets/radar-I7S5WNFK-CEcPKCj8.js +0 -1
  217. package/dist/assets/railroad-3IZDKUUU-CW38eNDs.js +0 -1
  218. package/dist/assets/railroad-abnf-AHOZXSZD-ByrylcXf.js +0 -1
  219. package/dist/assets/railroad-ebnf-EBAXGLYW-CNB6GwP-.js +0 -1
  220. package/dist/assets/railroad-peg-LSFZ7HO6-x6hnqlBG.js +0 -1
  221. package/dist/assets/remote-BgYduCQR.js +0 -1
  222. package/dist/assets/rotate-cw-BT8HxlcB.js +0 -1
  223. package/dist/assets/runtime-config-page-BuYof2Ty.js +0 -1
  224. package/dist/assets/search-config-page-CQTJOsOf.js +0 -1
  225. package/dist/assets/security-config-CtJqaPVL.js +0 -1
  226. package/dist/assets/settings-2-BHGgNlOH.js +0 -1
  227. package/dist/assets/skeleton-CJLc1CWn.js +0 -1
  228. package/dist/assets/stateDiagram-v2-6OUMAXLB-ZC6dFORv.js +0 -1
  229. package/dist/assets/swimlanesDiagram-G3AALYLV-CRkh51Im.js +0 -8
  230. package/dist/assets/treeView-QDETBFTQ-Bw0DSCH7.js +0 -1
  231. package/dist/assets/treemap-6X3UGDF4-DFWBFcfh.js +0 -1
  232. package/dist/assets/wardley-OPB4EBWU-CoWturk0.js +0 -1
  233. package/dist/assets/x-BMl7cucN.js +0 -1
  234. /package/dist/assets/{array-NADKd9aU.js → array-COEOjKUv.js} +0 -0
  235. /package/dist/assets/{chunk-KEIR6QF5-BuyuzJJL.js → chunk-KEIR6QF5-D7oHAcry.js} +0 -0
  236. /package/dist/assets/{chunk-Y2CYZVJY-D_B2ZRNt.js → chunk-Y2CYZVJY-08IUpgTP.js} +0 -0
  237. /package/dist/assets/{config-hints-BcfVUMij.js → config-hints-BoE41XDI.js} +0 -0
  238. /package/dist/assets/{cytoscape.esm-BANTlgza.js → cytoscape.esm-HifWufkp.js} +0 -0
  239. /package/dist/assets/{defaultLocale-C4_L0aEs.js → defaultLocale-CA-_SsC1.js} +0 -0
  240. /package/dist/assets/{dist-DAPEIJbz.js → dist-C60rvfy8.js} +0 -0
  241. /package/dist/assets/{graphlib-ChqXg0dI.js → graphlib-CGYCNsoJ.js} +0 -0
  242. /package/dist/assets/{host-capabilities-D9C4CBK5.js → host-capabilities-DPNRAjIj.js} +0 -0
  243. /package/dist/assets/{init-D8wZg9xq.js → init-C3plFVsG.js} +0 -0
  244. /package/dist/assets/{jszip.min-CkbBfwAQ.js → jszip.min-CeYs17Gn.js} +0 -0
  245. /package/dist/assets/{katex-TZJFa0J_.js → katex-DRThweSG.js} +0 -0
  246. /package/dist/assets/{middleware-B3k-TSmZ.js → middleware-D05fK5Sb.js} +0 -0
  247. /package/dist/assets/{path-DodfxcX7.js → path-BKtKxQ6Y.js} +0 -0
  248. /package/dist/assets/{rough.esm-CUaa1Rbe.js → rough.esm-DurL7-u7.js} +0 -0
  249. /package/dist/assets/{xlsx-CxByi7bR.js → xlsx-Sg8fZ6nH.js} +0 -0
@@ -0,0 +1,135 @@
1
+ import { useCallback, useMemo, useState } from 'react';
2
+ import type {
3
+ UiNcpSessionPendingInputView,
4
+ UiNcpSessionQueuedInputView,
5
+ } from '@nextclaw/client-sdk';
6
+ import type { NcpAgentSendEnvelope } from '@nextclaw/ncp';
7
+
8
+ import { t } from '@/shared/lib/i18n';
9
+ import {
10
+ buildSessionQueuedInputComposerSnapshot,
11
+ buildSessionQueuedInputPresentation,
12
+ type SessionQueuedInputAttachmentPreview,
13
+ } from '@/features/chat/features/conversation/utils/session-queued-input.utils';
14
+ import type { SessionConversationInputSnapshot } from './use-session-conversation-input-state';
15
+
16
+ type ComposerDraftSnapshot = Pick<
17
+ SessionConversationInputSnapshot,
18
+ 'text' | 'nodes' | 'selectedSkills' | 'skillRecords' | 'attachments'
19
+ >;
20
+
21
+ type SessionRunQueue = {
22
+ readonly inputs: readonly UiNcpSessionQueuedInputView[];
23
+ readonly removeQueuedInput: (id: string) => Promise<UiNcpSessionQueuedInputView | null>;
24
+ readonly steerQueuedInput: (id: string) => Promise<UiNcpSessionPendingInputView | null>;
25
+ };
26
+
27
+ export type SubmittingQueuedInput = {
28
+ readonly attachments: readonly SessionQueuedInputAttachmentPreview[];
29
+ readonly id: string;
30
+ readonly preview: string;
31
+ readonly sessionId: string;
32
+ readonly userMessageId: string;
33
+ };
34
+
35
+ type ProjectedQueuedInput = {
36
+ readonly attachments: readonly SessionQueuedInputAttachmentPreview[];
37
+ readonly id: string;
38
+ readonly isSubmitting?: boolean;
39
+ readonly preview: string;
40
+ };
41
+
42
+ function buildSubmittingQueuedInput(
43
+ envelope: NcpAgentSendEnvelope,
44
+ ): SubmittingQueuedInput | null {
45
+ const sessionId = envelope.sessionId?.trim();
46
+ if (!sessionId) return null;
47
+ const input: UiNcpSessionQueuedInputView = {
48
+ id: `submitting-${envelope.message.id}`,
49
+ sessionId,
50
+ enqueuedAt: new Date().toISOString(),
51
+ message: { ...envelope.message, sessionId },
52
+ metadata: structuredClone(envelope.metadata ?? {}),
53
+ };
54
+ const presentation = buildSessionQueuedInputPresentation(input);
55
+ return {
56
+ attachments: presentation.attachments,
57
+ id: input.id,
58
+ preview: presentation.preview,
59
+ sessionId,
60
+ userMessageId: input.message.id,
61
+ };
62
+ }
63
+
64
+ export function useSubmittingQueuedInputProjection(
65
+ serverInputs: readonly UiNcpSessionQueuedInputView[],
66
+ sessionKey: string | null,
67
+ ) {
68
+ const [submittingInput, setSubmittingInput] = useState<SubmittingQueuedInput | null>(null);
69
+ const stageSubmittingInput = useCallback((envelope: NcpAgentSendEnvelope) => {
70
+ const input = buildSubmittingQueuedInput(envelope);
71
+ if (input) setSubmittingInput(input);
72
+ return input;
73
+ }, []);
74
+ const clearSubmittingInput = useCallback((input: SubmittingQueuedInput) => {
75
+ setSubmittingInput((current) =>
76
+ current?.userMessageId === input.userMessageId ? null : current
77
+ );
78
+ }, []);
79
+ const queuedInputs = useMemo(() => {
80
+ const projected: ProjectedQueuedInput[] = serverInputs.map((input) => ({
81
+ ...buildSessionQueuedInputPresentation(input),
82
+ id: input.id,
83
+ }));
84
+ if (
85
+ submittingInput?.sessionId === sessionKey &&
86
+ !serverInputs.some((input) => input.message.id === submittingInput.userMessageId)
87
+ ) {
88
+ projected.push({
89
+ attachments: submittingInput.attachments,
90
+ id: submittingInput.id,
91
+ isSubmitting: true,
92
+ preview: submittingInput.preview,
93
+ });
94
+ }
95
+ return projected;
96
+ }, [serverInputs, sessionKey, submittingInput]);
97
+ return { clearSubmittingInput, queuedInputs, stageSubmittingInput };
98
+ }
99
+
100
+ export function useQueuedInputActions(params: {
101
+ readonly availableSkills: readonly { ref: string; name: string }[];
102
+ readonly hasComposerContent: boolean;
103
+ readonly restoreComposer: (snapshot: ComposerDraftSnapshot) => void;
104
+ readonly runQueue: SessionRunQueue;
105
+ readonly setSendError: (message: string | null) => void;
106
+ }) {
107
+ const { availableSkills, hasComposerContent, restoreComposer, runQueue, setSendError } = params;
108
+ const editQueuedInput = useCallback((id: string) => {
109
+ if (hasComposerContent) {
110
+ setSendError(t('chatQueuedEditComposerNotEmpty'));
111
+ return;
112
+ }
113
+ if (!runQueue.inputs.some((item) => item.id === id)) return;
114
+ void runQueue.removeQueuedInput(id).then((removed) => {
115
+ if (!removed) return;
116
+ restoreComposer(buildSessionQueuedInputComposerSnapshot(removed, availableSkills));
117
+ setSendError(null);
118
+ }).catch((error) => {
119
+ setSendError(error instanceof Error ? error.message : String(error));
120
+ });
121
+ }, [availableSkills, hasComposerContent, restoreComposer, runQueue, setSendError]);
122
+ const deleteQueuedInput = useCallback((id: string) => {
123
+ void runQueue.removeQueuedInput(id).then(() => setSendError(null)).catch((error) => {
124
+ setSendError(error instanceof Error ? error.message : String(error));
125
+ });
126
+ }, [runQueue, setSendError]);
127
+ const steerQueuedInput = useCallback((id: string) => {
128
+ void runQueue.steerQueuedInput(id).then((steered) => {
129
+ if (steered) setSendError(null);
130
+ }).catch((error) => {
131
+ setSendError(error instanceof Error ? error.message : String(error));
132
+ });
133
+ }, [runQueue, setSendError]);
134
+ return { deleteQueuedInput, editQueuedInput, steerQueuedInput };
135
+ }
@@ -1,6 +1,10 @@
1
1
  import { useCallback, useEffect } from 'react';
2
2
  import { useQuery, useQueryClient } from '@tanstack/react-query';
3
- import { eventKeys, type UiNcpSessionQueuedInputView } from '@nextclaw/client-sdk';
3
+ import {
4
+ eventKeys,
5
+ type UiNcpSessionPendingInputView,
6
+ type UiNcpSessionQueuedInputView,
7
+ } from '@nextclaw/client-sdk';
4
8
 
5
9
  import { nextclawClient } from '@/shared/lib/api';
6
10
 
@@ -11,7 +15,7 @@ export function useSessionRunQueue(sessionKey: string | null) {
11
15
  const normalizedSessionKey = sessionKey?.trim() || null;
12
16
  const query = useQuery({
13
17
  queryKey: [SESSION_RUN_QUEUE_QUERY_KEY, normalizedSessionKey],
14
- queryFn: () => nextclawClient.sessions.listQueuedInputs(normalizedSessionKey as string),
18
+ queryFn: () => nextclawClient.sessions.listPendingInputs(normalizedSessionKey as string),
15
19
  enabled: Boolean(normalizedSessionKey),
16
20
  retry: false,
17
21
  staleTime: 5_000,
@@ -49,13 +53,24 @@ export function useSessionRunQueue(sessionKey: string | null) {
49
53
  return [];
50
54
  }
51
55
  const result = await refetch();
52
- return result.data?.inputs ?? [];
56
+ return result.data?.inputs.filter(({ placement }) => placement === 'queued') ?? [];
53
57
  }, [normalizedSessionKey, refetch]);
54
58
 
59
+ const steerQueuedInput = useCallback(async (
60
+ queuedInputId: string,
61
+ ): Promise<UiNcpSessionPendingInputView | null> => {
62
+ if (!normalizedSessionKey) return null;
63
+ return await nextclawClient.sessions.steerQueuedInput(normalizedSessionKey, queuedInputId);
64
+ }, [normalizedSessionKey]);
65
+
66
+ const inputs = data?.inputs ?? [];
67
+
55
68
  return {
56
- inputs: data?.inputs ?? [],
69
+ inputs: inputs.filter(({ placement }) => placement === 'queued'),
70
+ pendingInputs: inputs,
57
71
  isLoading,
58
72
  refreshQueuedInputs,
59
73
  removeQueuedInput,
74
+ steerQueuedInput,
60
75
  };
61
76
  }
@@ -3,7 +3,7 @@ import type { UiNcpSessionQueuedInputView } from '@nextclaw/client-sdk';
3
3
 
4
4
  import {
5
5
  buildSessionQueuedInputComposerSnapshot,
6
- buildSessionQueuedInputPreview,
6
+ buildSessionQueuedInputPresentation,
7
7
  } from '@/features/chat/features/conversation/utils/session-queued-input.utils';
8
8
 
9
9
  describe('session queued input utils', () => {
@@ -58,6 +58,56 @@ describe('session queued input utils', () => {
58
58
  mimeType: 'text/markdown',
59
59
  assetUri: 'asset://spec',
60
60
  }]);
61
- expect(buildSessionQueuedInputPreview(input)).toBe('please');
61
+ expect(buildSessionQueuedInputPresentation(input)).toEqual({
62
+ attachments: [{
63
+ mimeType: 'text/markdown',
64
+ name: 'spec.md',
65
+ }],
66
+ preview: 'please',
67
+ });
68
+ });
69
+
70
+ it('keeps image content available to the compact queue presentation', () => {
71
+ const input: UiNcpSessionQueuedInputView = {
72
+ id: 'queued-image',
73
+ sessionId: 'session-1',
74
+ enqueuedAt: '2026-07-22T10:00:00.000Z',
75
+ metadata: {},
76
+ message: {
77
+ id: 'message-image',
78
+ sessionId: 'session-1',
79
+ role: 'user',
80
+ status: 'final',
81
+ timestamp: '2026-07-22T10:00:00.000Z',
82
+ parts: [
83
+ {
84
+ type: 'file',
85
+ name: 'inline.png',
86
+ mimeType: 'image/png',
87
+ contentBase64: 'aW1hZ2U=',
88
+ },
89
+ {
90
+ type: 'file',
91
+ name: 'stored.png',
92
+ mimeType: 'image/png',
93
+ assetUri: 'asset://store/stored.png',
94
+ },
95
+ ],
96
+ },
97
+ };
98
+
99
+ const presentation = buildSessionQueuedInputPresentation(input);
100
+
101
+ expect(presentation.preview).toBe('');
102
+ expect(presentation.attachments[0]).toEqual({
103
+ mimeType: 'image/png',
104
+ name: 'inline.png',
105
+ previewUrl: 'data:image/png;base64,aW1hZ2U=',
106
+ });
107
+ expect(presentation.attachments[1]).toMatchObject({
108
+ mimeType: 'image/png',
109
+ name: 'stored.png',
110
+ previewUrl: expect.stringContaining('/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Fstored.png'),
111
+ });
62
112
  });
63
113
  });
@@ -1,5 +1,7 @@
1
1
  import type { UiNcpSessionQueuedInputView } from '@nextclaw/client-sdk';
2
+ import type { NcpMessagePart } from '@nextclaw/ncp';
2
3
 
4
+ import { buildNcpAssetContentUrl } from '@/shared/lib/api';
3
5
  import {
4
6
  readInlineTokensFromMetadata,
5
7
  } from '@/features/chat/features/input/utils/chat-inline-token.utils';
@@ -10,6 +12,17 @@ import {
10
12
 
11
13
  export type SessionQueuedInputComposerSnapshot = SessionMessageComposerSnapshot;
12
14
 
15
+ export type SessionQueuedInputAttachmentPreview = {
16
+ readonly mimeType: string;
17
+ readonly name: string;
18
+ readonly previewUrl?: string;
19
+ };
20
+
21
+ export type SessionQueuedInputPresentation = {
22
+ readonly attachments: readonly SessionQueuedInputAttachmentPreview[];
23
+ readonly preview: string;
24
+ };
25
+
13
26
  function readInputMetadata(input: UiNcpSessionQueuedInputView): Record<string, unknown> {
14
27
  return {
15
28
  ...input.metadata,
@@ -17,7 +30,7 @@ function readInputMetadata(input: UiNcpSessionQueuedInputView): Record<string, u
17
30
  };
18
31
  }
19
32
 
20
- export function buildSessionQueuedInputPreview(input: UiNcpSessionQueuedInputView): string {
33
+ function buildSessionQueuedInputPreview(input: UiNcpSessionQueuedInputView): string {
21
34
  const inlineTokens = readInlineTokensFromMetadata(readInputMetadata(input));
22
35
  const text = input.message.parts.flatMap((part) =>
23
36
  part.type === 'text' || part.type === 'rich-text' || part.type === 'reasoning'
@@ -28,10 +41,38 @@ export function buildSessionQueuedInputPreview(input: UiNcpSessionQueuedInputVie
28
41
  (value, token) => value.split(token.rawText).join(''),
29
42
  text,
30
43
  ).replace(/\s+/g, ' ').trim();
31
- if (plainText) {
32
- return plainText;
33
- }
34
- return input.message.parts.flatMap((part) => part.type === 'file' && part.name ? [part.name] : []).join(', ');
44
+ return plainText;
45
+ }
46
+
47
+ function buildImagePreviewUrl(
48
+ part: Extract<NcpMessagePart, { type: 'file' }>,
49
+ ): string | undefined {
50
+ if (!part.mimeType?.startsWith('image/')) return undefined;
51
+ const url = part.url?.trim();
52
+ if (url) return url;
53
+ const contentBase64 = part.contentBase64?.trim();
54
+ if (contentBase64) return `data:${part.mimeType};base64,${contentBase64}`;
55
+ const assetUri = part.assetUri?.trim();
56
+ return assetUri ? buildNcpAssetContentUrl(assetUri) : undefined;
57
+ }
58
+
59
+ export function buildSessionQueuedInputPresentation(
60
+ input: UiNcpSessionQueuedInputView,
61
+ ): SessionQueuedInputPresentation {
62
+ const attachments = input.message.parts.flatMap((part, index) => {
63
+ if (part.type !== 'file') return [];
64
+ const mimeType = part.mimeType ?? 'application/octet-stream';
65
+ const previewUrl = buildImagePreviewUrl(part);
66
+ return [{
67
+ mimeType,
68
+ name: part.name?.trim() || `attachment-${index + 1}`,
69
+ ...(previewUrl ? { previewUrl } : {}),
70
+ }];
71
+ });
72
+ return {
73
+ attachments,
74
+ preview: buildSessionQueuedInputPreview(input),
75
+ };
35
76
  }
36
77
 
37
78
  export function buildSessionQueuedInputComposerSnapshot(
@@ -160,6 +160,7 @@ export function buildChatMessageTexts(
160
160
  previewZoomOutLabel: t("chatPreviewZoomOut"),
161
161
  previewResetZoomLabel: t("chatPreviewResetZoom"),
162
162
  typingLabel: t("chatTyping"),
163
+ pendingInputLabel: t("chatSteeringPending"),
163
164
  reasoningCharacterCountTemplates: {
164
165
  inProgress: t("chatReasoningInProgressCharacterCount"),
165
166
  completed: t("chatReasoningCompletedCharacterCount"),
@@ -329,6 +329,108 @@ describe("useHydratedNcpAgent", () => {
329
329
  });
330
330
 
331
331
  describe("useHydratedNcpAgent stream reconciliation", () => {
332
+ it("keeps the rendered message snapshot stable when reconciliation data is unchanged", async () => {
333
+ let resolveReconcile: ((seed: { messages: readonly NcpMessage[]; status: "idle" }) => void) | null = null;
334
+ const reconcileSeed = new Promise<{ messages: readonly NcpMessage[]; status: "idle" }>((resolve) => {
335
+ resolveReconcile = resolve;
336
+ });
337
+ const client = {
338
+ stop: mocks.stop.mockResolvedValue(undefined),
339
+ stream: mocks.stream.mockImplementation((_payload, observer) => {
340
+ observer?.onOpen?.();
341
+ return new Promise<void>(() => {});
342
+ }),
343
+ subscribe: vi.fn(() => () => {}),
344
+ } as unknown as NcpAgentClientEndpoint;
345
+ const historyMessage: NcpMessage = {
346
+ id: "message-stable",
347
+ sessionId: "session-stable",
348
+ role: "assistant",
349
+ status: "final",
350
+ parts: [{ type: "text", text: "Already rendered" }],
351
+ timestamp: "2026-08-20T00:00:00.000Z",
352
+ };
353
+ const loadSeed = vi
354
+ .fn()
355
+ .mockResolvedValueOnce({ messages: [historyMessage], status: "idle" })
356
+ .mockImplementationOnce(() => reconcileSeed);
357
+ const { result } = renderHook(() =>
358
+ useHydratedNcpAgent({
359
+ sessionId: "session-stable",
360
+ client,
361
+ loadSeed,
362
+ }),
363
+ );
364
+
365
+ await waitFor(() => expect(loadSeed).toHaveBeenCalledTimes(2));
366
+ await waitFor(() => expect(result.current.visibleMessages).toEqual([historyMessage]));
367
+ const renderedMessages = result.current.visibleMessages;
368
+
369
+ await act(async () => {
370
+ resolveReconcile?.({ messages: [{ ...historyMessage }], status: "idle" });
371
+ await Promise.resolve();
372
+ await Promise.resolve();
373
+ });
374
+
375
+ expect(result.current.visibleMessages).toBe(renderedMessages);
376
+ });
377
+
378
+ it("rehydrates when reconciliation changes the content of an existing message", async () => {
379
+ let resolveReconcile: ((seed: { messages: readonly NcpMessage[]; status: "idle" }) => void) | null = null;
380
+ const reconcileSeed = new Promise<{ messages: readonly NcpMessage[]; status: "idle" }>((resolve) => {
381
+ resolveReconcile = resolve;
382
+ });
383
+ const client = {
384
+ stop: mocks.stop.mockResolvedValue(undefined),
385
+ stream: mocks.stream.mockImplementation((_payload, observer) => {
386
+ observer?.onOpen?.();
387
+ return new Promise<void>(() => {});
388
+ }),
389
+ subscribe: vi.fn(() => () => {}),
390
+ } as unknown as NcpAgentClientEndpoint;
391
+ const historyMessage: NcpMessage = {
392
+ id: "message-changed",
393
+ sessionId: "session-changed",
394
+ role: "assistant",
395
+ status: "final",
396
+ parts: [{ type: "text", text: "Before reconciliation" }],
397
+ timestamp: "2026-08-20T00:00:00.000Z",
398
+ };
399
+ const loadSeed = vi
400
+ .fn()
401
+ .mockResolvedValueOnce({ messages: [historyMessage], status: "idle" })
402
+ .mockImplementationOnce(() => reconcileSeed);
403
+ const { result } = renderHook(() =>
404
+ useHydratedNcpAgent({
405
+ sessionId: "session-changed",
406
+ client,
407
+ loadSeed,
408
+ }),
409
+ );
410
+
411
+ await waitFor(() => expect(loadSeed).toHaveBeenCalledTimes(2));
412
+ await waitFor(() => expect(result.current.visibleMessages).toEqual([historyMessage]));
413
+ const renderedMessages = result.current.visibleMessages;
414
+
415
+ await act(async () => {
416
+ resolveReconcile?.({
417
+ messages: [{
418
+ ...historyMessage,
419
+ parts: [{ type: "text", text: "After reconciliation" }],
420
+ }],
421
+ status: "idle",
422
+ });
423
+ await Promise.resolve();
424
+ });
425
+
426
+ await waitFor(() => {
427
+ expect(result.current.visibleMessages).not.toBe(renderedMessages);
428
+ expect(result.current.visibleMessages[0]?.parts).toEqual([
429
+ { type: "text", text: "After reconciliation" },
430
+ ]);
431
+ });
432
+ });
433
+
332
434
  it("reconciles history after the stream opens without overwriting newer live completion", async () => {
333
435
  let subscriber: NcpEndpointSubscriber | null = null;
334
436
  let resolveReconcile: ((seed: { messages: readonly NcpMessage[]; status: "running" }) => void) | null = null;
@@ -102,6 +102,7 @@ function resetConversationMocks(): void {
102
102
  mocks.runtimeAvailability.phase = "cold-starting";
103
103
  mocks.runtimeAvailability.lastReadyAt = null;
104
104
  mocks.visibleMessages.length = 0;
105
+ delete window.__NEXTCLAW_INITIAL_SESSION_MESSAGES_PREFETCH__;
105
106
  }
106
107
 
107
108
  describe("useNcpSessionConversation", () => {
@@ -138,6 +139,7 @@ describe("useNcpSessionConversation", () => {
138
139
  expect(mocks.fetchNcpSessionMessages).toHaveBeenCalledWith("session-1", {
139
140
  limit: 300,
140
141
  toolPayload: "summary",
142
+ initialPayload: "compact",
141
143
  signal: expect.any(AbortSignal),
142
144
  });
143
145
  expect(result).toEqual({
@@ -162,6 +164,79 @@ describe("useNcpSessionConversation", () => {
162
164
  });
163
165
  });
164
166
 
167
+ it("consumes a matching HTML history prefetch without repeating the compact request", async () => {
168
+ window.__NEXTCLAW_INITIAL_SESSION_MESSAGES_PREFETCH__ = {
169
+ consumed: false,
170
+ limit: 20,
171
+ sessionId: "session-1",
172
+ promise: Promise.resolve({
173
+ responseOk: true,
174
+ payload: {
175
+ ok: true,
176
+ data: {
177
+ sessionId: "session-1",
178
+ status: "idle",
179
+ total: 1,
180
+ pageInfo: { startCursor: "cursor-1", hasPreviousPage: false },
181
+ messages: [{ id: "message-1" }],
182
+ },
183
+ },
184
+ }),
185
+ };
186
+
187
+ await expect(fetchNcpSessionConversationSeed(
188
+ "session-1",
189
+ new AbortController().signal,
190
+ 20,
191
+ )).resolves.toMatchObject({
192
+ messages: [{ id: "message-1" }],
193
+ total: 1,
194
+ });
195
+
196
+ expect(mocks.fetchNcpSessionMessages).not.toHaveBeenCalled();
197
+ expect(window.__NEXTCLAW_INITIAL_SESSION_MESSAGES_PREFETCH__?.consumed).toBe(true);
198
+ });
199
+
200
+ it("falls back to the canonical request when the HTML history prefetch is invalid", async () => {
201
+ window.__NEXTCLAW_INITIAL_SESSION_MESSAGES_PREFETCH__ = {
202
+ consumed: false,
203
+ limit: 20,
204
+ sessionId: "session-1",
205
+ promise: Promise.resolve({ responseOk: true, payload: { ok: true } }),
206
+ };
207
+ mocks.fetchNcpSessionMessages.mockResolvedValue({
208
+ sessionId: "session-1",
209
+ status: "idle",
210
+ total: 1,
211
+ pageInfo: { startCursor: "cursor-1", hasPreviousPage: false },
212
+ messages: [{ id: "message-1" }],
213
+ });
214
+
215
+ await fetchNcpSessionConversationSeed(
216
+ "session-1",
217
+ new AbortController().signal,
218
+ 20,
219
+ );
220
+
221
+ expect(mocks.fetchNcpSessionMessages).toHaveBeenCalledTimes(1);
222
+ });
223
+
224
+ it("does not revive an aborted session through the history prefetch fallback", async () => {
225
+ window.__NEXTCLAW_INITIAL_SESSION_MESSAGES_PREFETCH__ = {
226
+ consumed: false,
227
+ limit: 20,
228
+ sessionId: "session-1",
229
+ promise: new Promise(() => undefined),
230
+ };
231
+ const controller = new AbortController();
232
+ const seed = fetchNcpSessionConversationSeed("session-1", controller.signal, 20);
233
+
234
+ controller.abort();
235
+
236
+ await expect(seed).rejects.toMatchObject({ name: "AbortError" });
237
+ expect(mocks.fetchNcpSessionMessages).not.toHaveBeenCalled();
238
+ });
239
+
165
240
  it("treats a missing session as an empty idle draft seed", async () => {
166
241
  mocks.fetchNcpSessionMessages.mockRejectedValue(
167
242
  new Error("ncp session not found: draft-session"),
@@ -437,7 +512,7 @@ describe("useNcpSessionConversation history and hydration", () => {
437
512
  expect(mocks.fetchNcpSessionMessages).toHaveBeenLastCalledWith(
438
513
  "session-1",
439
514
  {
440
- limit: 40,
515
+ limit: 20,
441
516
  cursor: "cursor-2",
442
517
  toolPayload: "summary",
443
518
  signal: expect.any(AbortSignal),
@@ -455,6 +530,47 @@ describe("useNcpSessionConversation history and hydration", () => {
455
530
  expect(result.current.messageTotal).toBe(5);
456
531
  });
457
532
 
533
+ it("uses the compact payload only for the first seed and restores the full recent window during reconciliation", async () => {
534
+ mocks.fetchNcpSessionMessages
535
+ .mockResolvedValueOnce({
536
+ sessionId: "session-1",
537
+ status: "idle",
538
+ total: 20,
539
+ pageInfo: { startCursor: "cursor-16", hasPreviousPage: true },
540
+ messages: Array.from({ length: 5 }, (_, index) => ({ id: `message-${index + 16}` })),
541
+ })
542
+ .mockResolvedValueOnce({
543
+ sessionId: "session-1",
544
+ status: "idle",
545
+ total: 20,
546
+ pageInfo: { startCursor: "cursor-1", hasPreviousPage: false },
547
+ messages: Array.from({ length: 20 }, (_, index) => ({ id: `message-${index + 1}` })),
548
+ });
549
+ renderHook(() => useNcpSessionConversation("session-1"));
550
+ const loadSeed = mocks.hydratedCalls[0]?.loadSeed as (
551
+ sessionId: string,
552
+ signal: AbortSignal,
553
+ ) => Promise<{ messages: unknown[]; status: string }>;
554
+
555
+ await act(async () => {
556
+ await loadSeed("session-1", new AbortController().signal);
557
+ await loadSeed("session-1", new AbortController().signal);
558
+ });
559
+
560
+ expect(mocks.fetchNcpSessionMessages).toHaveBeenNthCalledWith(1, "session-1", {
561
+ limit: 20,
562
+ toolPayload: "summary",
563
+ initialPayload: "compact",
564
+ signal: expect.any(AbortSignal),
565
+ });
566
+ expect(mocks.fetchNcpSessionMessages).toHaveBeenNthCalledWith(2, "session-1", {
567
+ limit: 20,
568
+ toolPayload: "summary",
569
+ initialPayload: undefined,
570
+ signal: expect.any(AbortSignal),
571
+ });
572
+ });
573
+
458
574
  it("loads and caches one complete message when deferred tool details are requested", async () => {
459
575
  const summaryMessage: NcpMessage = {
460
576
  id: "assistant-large",
@@ -12,9 +12,9 @@ import type {
12
12
  import { API_BASE } from "@/shared/lib/api";
13
13
  import { createNcpAppClientFetch } from "@/features/chat/features/runtime/utils/ncp-app-client-fetch.utils";
14
14
  import {
15
- DEFAULT_NCP_SESSION_MESSAGE_LIMIT,
16
15
  useNcpSessionMessageHistory,
17
16
  } from "@/features/chat/features/ncp/hooks/use-ncp-session-message-history";
17
+ import { DEFAULT_NCP_SESSION_MESSAGE_LIMIT } from "@/features/chat/features/ncp/hooks/use-ncp-session-seed-loader";
18
18
  import { useSystemStatus } from "@/features/system-status";
19
19
  import { nextclawClient } from "@/shared/lib/api";
20
20
 
@@ -23,7 +23,7 @@ const NCP_AGENT_UNAVAILABLE_DURING_STARTUP =
23
23
  const NCP_AGENT_STREAM_OPEN_TIMEOUT_MS = 15_000;
24
24
  const NCP_AGENT_STREAM_IDLE_TIMEOUT_MS = 70_000;
25
25
 
26
- export { fetchNcpSessionConversationSeed } from "@/features/chat/features/ncp/hooks/use-ncp-session-message-history";
26
+ export { fetchNcpSessionConversationSeed } from "@/features/chat/features/ncp/hooks/use-ncp-session-seed-loader";
27
27
 
28
28
  type UseNcpSessionConversationOptions = {
29
29
  messageLimit?: number;