@nextclaw/ui 0.19.5 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (265) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/assets/{abnfDiagram-VRR7QNED-DrbfiNNF.js → abnfDiagram-VRR7QNED-BcFJp_1X.js} +1 -1
  3. package/dist/assets/{aiden0z-pptx-renderer.es-DdLb2IVc.js → aiden0z-pptx-renderer.es-Dp2ezlQn.js} +1 -1
  4. package/dist/assets/api-C2iYhdTe.js +13 -0
  5. package/dist/assets/{appearance-settings-page-cTuZdROP.js → appearance-settings-page-BwQDQr0n.js} +1 -1
  6. package/dist/assets/{arc-BXlMc9B0.js → arc-CkgyPMyW.js} +1 -1
  7. package/dist/assets/architecture-TIHT7OUA-DO2x53FK.js +1 -0
  8. package/dist/assets/{architectureDiagram-ZJ3FMSHR-DXAJxxO0.js → architectureDiagram-ZJ3FMSHR-CMd4cIjI.js} +1 -1
  9. package/dist/assets/{blockDiagram-677ZJIJ3-i4Kz7zU0.js → blockDiagram-677ZJIJ3-Bz3tbhet.js} +1 -1
  10. package/dist/assets/book-open-C0IlT9zs.js +1 -0
  11. package/dist/assets/{c4Diagram-LMCZKHZV-BDc0TkPd.js → c4Diagram-LMCZKHZV-D9hFNAGv.js} +1 -1
  12. package/dist/assets/channel-nBvMaVyP.js +1 -0
  13. package/dist/assets/channels-list-page-B0z4bh8e.js +8 -0
  14. package/dist/assets/chunk-2Q5K7J3B-CJgqFDS4.js +1 -0
  15. package/dist/assets/{chunk-32BRIVSS-DuReei0Z.js → chunk-32BRIVSS-7NrjEITq.js} +1 -1
  16. package/dist/assets/{chunk-52WLFC77-D8APq0tm.js → chunk-52WLFC77-viIcqj9q.js} +1 -1
  17. package/dist/assets/{chunk-5FCAYU7R-BsPrNXNS.js → chunk-5FCAYU7R-Vsu4J-VM.js} +1 -1
  18. package/dist/assets/{chunk-5HE753X5-BRtkOPoJ.js → chunk-5HE753X5-C-w6CQZK.js} +1 -1
  19. package/dist/assets/{chunk-5JV3BV7I-ChPc8e8U.js → chunk-5JV3BV7I-CuKgJQ4W.js} +1 -1
  20. package/dist/assets/{chunk-5TONJI2A-CzTjlJGb.js → chunk-5TONJI2A-CYeiWZKb.js} +1 -1
  21. package/dist/assets/{chunk-5VM5RSS4-xsv_22jR.js → chunk-5VM5RSS4-DRCbfwIn.js} +1 -1
  22. package/dist/assets/{chunk-7BUUIJ7U-CwECujme.js → chunk-7BUUIJ7U-BvydOcm3.js} +1 -1
  23. package/dist/assets/{chunk-BIQX33UG-DQAm173p.js → chunk-BIQX33UG-RlppOXKo.js} +1 -1
  24. package/dist/assets/{chunk-C7G6YPKG-BkpajeQb.js → chunk-C7G6YPKG-DAizzYwr.js} +1 -1
  25. package/dist/assets/{chunk-CQNSW5MT-ND65pWRA.js → chunk-CQNSW5MT-5eChJHca.js} +1 -1
  26. package/dist/assets/{chunk-CYSBUYHQ-vMu6ijxG.js → chunk-CYSBUYHQ-YHPr53fv.js} +1 -1
  27. package/dist/assets/{chunk-EMLP6XTP-CEaeW3gH.js → chunk-EMLP6XTP-CK4D7Hz5.js} +1 -1
  28. package/dist/assets/{chunk-EX3LRPZG-a4ER-HDr.js → chunk-EX3LRPZG-BmlDFIRW.js} +1 -1
  29. package/dist/assets/{chunk-FWX5IMBZ-C_8fQssn.js → chunk-FWX5IMBZ-D0ksHZZ9.js} +2 -2
  30. package/dist/assets/{chunk-HOUHSVGY-CEvw-PEd.js → chunk-HOUHSVGY-DPkq8FCm.js} +1 -1
  31. package/dist/assets/{chunk-ICXQ74PX-BEtF0fGY.js → chunk-ICXQ74PX-FAatv5fC.js} +1 -1
  32. package/dist/assets/{chunk-JG7HCLWE-DGRrVp8-.js → chunk-JG7HCLWE-DLkIi4F7.js} +1 -1
  33. package/dist/assets/{chunk-JWPE2WC7-DRqpp_nM.js → chunk-JWPE2WC7-DawOjNE3.js} +1 -1
  34. package/dist/assets/{chunk-MOJQB5TN-BDEriOXs.js → chunk-MOJQB5TN-Di52TorW.js} +1 -1
  35. package/dist/assets/{chunk-MOZMSUNE-JWRNzdnw.js → chunk-MOZMSUNE-PfyK9w1-.js} +1 -1
  36. package/dist/assets/{chunk-OGEWGWER-DS2VTgCx.js → chunk-OGEWGWER-C3jdH3Ty.js} +1 -1
  37. package/dist/assets/{chunk-OSBZ3O6U-3LiYTBUB.js → chunk-OSBZ3O6U-CecHo-f0.js} +1 -1
  38. package/dist/assets/{chunk-PUDLZKDR-o9M2s_SO.js → chunk-PUDLZKDR-f3IJh3sN.js} +1 -1
  39. package/dist/assets/{chunk-Q4XR5HBZ-DTB7Ma2N.js → chunk-Q4XR5HBZ-DQFeUPxZ.js} +1 -1
  40. package/dist/assets/{chunk-QBLGF6JB-DpkxZJL5.js → chunk-QBLGF6JB-BhUeG0y2.js} +1 -1
  41. package/dist/assets/{chunk-R7FJI6CG-CRejkHJU.js → chunk-R7FJI6CG-C3XpZ-2u.js} +1 -1
  42. package/dist/assets/{chunk-RYQCIY6F-B1boBjLt.js → chunk-RYQCIY6F-CAqa5klQ.js} +1 -1
  43. package/dist/assets/{chunk-U6XO7XAA-DgKa97bj.js → chunk-U6XO7XAA-D_WXezeD.js} +1 -1
  44. package/dist/assets/{chunk-V7JOEXUC-DE56eh0N.js → chunk-V7JOEXUC-CZ70oOtp.js} +1 -1
  45. package/dist/assets/chunk-VAUOI2AC-BGvh3yIx.js +1 -0
  46. package/dist/assets/{chunk-VR4S4FIN-BzomPZq2.js → chunk-VR4S4FIN-B7AHEYYk.js} +1 -1
  47. package/dist/assets/{chunk-WYO6CB5R-C6gy8rSd.js → chunk-WYO6CB5R-Dn9To_OI.js} +2 -2
  48. package/dist/assets/chunk-XXDRQBXY-DaBxG5gs.js +1 -0
  49. package/dist/assets/{chunk-YOTPTUD7-BK98yfl9.js → chunk-YOTPTUD7-BYEgRBk3.js} +1 -1
  50. package/dist/assets/{chunk-ZGVPDNZ5-DFXAhwUT.js → chunk-ZGVPDNZ5-C6YtZDU0.js} +1 -1
  51. package/dist/assets/{chunk-ZIRB5QZD-Bbu6BiNC.js → chunk-ZIRB5QZD-DMuYtnHF.js} +1 -1
  52. package/dist/assets/classDiagram-OUVF2IWQ-P6N03G2R.js +1 -0
  53. package/dist/assets/classDiagram-v2-EOCWNBFH-B1_uLvMv.js +1 -0
  54. package/dist/assets/{config-split-page-DO1Oyb-r.js → config-split-page-VDTf3zZb.js} +1 -1
  55. package/dist/assets/confirm-dialog-BI_Vt2s_.js +5 -0
  56. package/dist/assets/{cose-bilkent-JH36ORCC-BV8KR4uF.js → cose-bilkent-JH36ORCC-Bc2Ex5Y3.js} +1 -1
  57. package/dist/assets/createLucideIcon-Bydi9Gc5.js +1 -0
  58. package/dist/assets/cynefin-VYW2F7L2-B-H8Qp-n.js +1 -0
  59. package/dist/assets/{cynefinDiagram-TSTJHNR4-Ckd6nU9t.js → cynefinDiagram-TSTJHNR4-DVysQoGz.js} +1 -1
  60. package/dist/assets/{dagre-D_T-FW5i.js → dagre-CMhQ1cAG.js} +1 -1
  61. package/dist/assets/{dagre-VKFMJZFB-CyDaQpD4.js → dagre-VKFMJZFB-RYyWpPJZ.js} +1 -1
  62. package/dist/assets/{desktop-update-config-XSV-DjJW.js → desktop-update-config-DVoD6NxG.js} +1 -1
  63. package/dist/assets/{diagram-FQU43EPY-CghnwS0H.js → diagram-FQU43EPY-BD6KONdF.js} +1 -1
  64. package/dist/assets/{diagram-G47NLZAW-DK4DJ3Kx.js → diagram-G47NLZAW-CGB2ptRR.js} +1 -1
  65. package/dist/assets/{diagram-NH7WQ7WH-CUgDkZCJ.js → diagram-NH7WQ7WH-DnkOI6cU.js} +1 -1
  66. package/dist/assets/{diagram-OA4YK3LP-vO2gyH6n.js → diagram-OA4YK3LP-DF82RZ7P.js} +1 -1
  67. package/dist/assets/{diagram-WEI45ONY-DbSBRDSr.js → diagram-WEI45ONY-Be2v7Ck9.js} +1 -1
  68. package/dist/assets/{dist-C-2H76Cv.js → dist-A5btQciA.js} +1 -1
  69. package/dist/assets/dist-CmHtkFbs.js +1 -0
  70. package/dist/assets/doc-browser-Cm_tMZ_C.js +1 -0
  71. package/dist/assets/doc-browser-Dto54tCf.js +1 -0
  72. package/dist/assets/{doc-browser-context-C0Bns1J6.js → doc-browser-context-Bxw0ora-.js} +1 -1
  73. package/dist/assets/{docx-preview-DfxIm9IB.js → docx-preview-BqwAjrj3.js} +1 -1
  74. package/dist/assets/{ebnfDiagram-CCIWWBDH-W7CAcK7r.js → ebnfDiagram-CCIWWBDH-CP98OYk8.js} +1 -1
  75. package/dist/assets/{erDiagram-Q63AITRT-LA0JWL8-.js → erDiagram-Q63AITRT-ZLlx8xfs.js} +1 -1
  76. package/dist/assets/eventmodeling-45OFAUF4-YVtShyFA.js +1 -0
  77. package/dist/assets/extensions-CG4voDCh.js +1 -0
  78. package/dist/assets/flowDiagram-23GEKE2U-BeKXabVO.js +1 -0
  79. package/dist/assets/{form-actions-CMxhPact.js → form-actions-lv47uGIB.js} +1 -1
  80. package/dist/assets/{ganttDiagram-NO4QXBWP-_PiTPavJ.js → ganttDiagram-NO4QXBWP-DXCDjHR2.js} +1 -1
  81. package/dist/assets/gitGraph-TEB2WS4Q-jTSiBj-p.js +1 -0
  82. package/dist/assets/{gitGraphDiagram-IHSO6WYX-DsRB1ZMU.js → gitGraphDiagram-IHSO6WYX-l2HWrTJp.js} +1 -1
  83. package/dist/assets/index-BTSwNDVi.js +109 -0
  84. package/dist/assets/index-QrdUWHfm.css +1 -0
  85. package/dist/assets/info-DKCQHKI2-BIporNWb.js +1 -0
  86. package/dist/assets/{infoDiagram-FWYZ7A6U-wIRraYx5.js → infoDiagram-FWYZ7A6U-D-DjX-bn.js} +1 -1
  87. package/dist/assets/{ishikawaDiagram-FXEZZL3T-CNzsrcrh.js → ishikawaDiagram-FXEZZL3T-y_Xy128F.js} +1 -1
  88. package/dist/assets/{journeyDiagram-5HDEW3XC-Dy9huHxy.js → journeyDiagram-5HDEW3XC-DJn5xYcQ.js} +1 -1
  89. package/dist/assets/{kanban-definition-HUTT4EX6-BOCIN365.js → kanban-definition-HUTT4EX6-DJVIOR21.js} +1 -1
  90. package/dist/assets/{line-BxsizOTs.js → line-Cos7PIOj.js} +1 -1
  91. package/dist/assets/{linear-C_YHf92I.js → linear-BtDLqtzF.js} +1 -1
  92. package/dist/assets/loader-circle-CkaqeEne.js +1 -0
  93. package/dist/assets/{map-CobcjgZ8.js → map-BKnQyvd0.js} +1 -1
  94. package/dist/assets/mcp-marketplace-page-BSk0qUOX.js +9 -0
  95. package/dist/assets/mcp-marketplace-page-BgZGc-S_.js +1 -0
  96. package/dist/assets/{mermaid-parser.core-D7lu2RbA.js → mermaid-parser.core-XUNZhr2W.js} +2 -2
  97. package/dist/assets/{mermaid.core-CbfW2Rk8.js → mermaid.core-ySrvolIe.js} +3 -3
  98. package/dist/assets/{mindmap-definition-LN4V7U3C-DUjGXlIC.js → mindmap-definition-LN4V7U3C-BypL9tei.js} +1 -1
  99. package/dist/assets/model-config-page-c8RnUGoH.js +1 -0
  100. package/dist/assets/{navigation-link-v3FaWcIr.js → navigation-link-D0agTj5t.js} +1 -1
  101. package/dist/assets/{ordinal-CjoUCHto.js → ordinal-CxxVWxYU.js} +1 -1
  102. package/dist/assets/packet-7NZHBO7P-CuFMADV0.js +1 -0
  103. package/dist/assets/{pegDiagram-2B236MQR-BQY2DgiP.js → pegDiagram-2B236MQR-D-U2KBGv.js} +1 -1
  104. package/dist/assets/pie-RZYD4A2V-XUeIXhM9.js +1 -0
  105. package/dist/assets/{pieDiagram-ENE6RG2P-D18owe4D.js → pieDiagram-ENE6RG2P-CBi3GusJ.js} +1 -1
  106. package/dist/assets/plus-BiMjkv3R.js +1 -0
  107. package/dist/assets/{privacy-settings-page-CYM1Dxwo.js → privacy-settings-page-CPkyTvEE.js} +1 -1
  108. package/dist/assets/providers-config-page-CGUEb5Dx.js +1 -0
  109. package/dist/assets/{quadrantDiagram-ABIIQ3AL-B8F7YoTA.js → quadrantDiagram-ABIIQ3AL-H0ZuvnZL.js} +1 -1
  110. package/dist/assets/radar-I7S5WNFK-D-NNGh1k.js +1 -0
  111. package/dist/assets/railroad-3IZDKUUU-C67_uDaM.js +1 -0
  112. package/dist/assets/railroad-abnf-AHOZXSZD-BZ8Yflnd.js +1 -0
  113. package/dist/assets/railroad-ebnf-EBAXGLYW-B7zO7RoK.js +1 -0
  114. package/dist/assets/railroad-peg-LSFZ7HO6-R63PTgLz.js +1 -0
  115. package/dist/assets/{railroadDiagram-RFXS5EU6-RU8GpxN5.js → railroadDiagram-RFXS5EU6-D7Y2EqSd.js} +1 -1
  116. package/dist/assets/react-BMYFkaGi.js +8 -0
  117. package/dist/assets/refresh-cw-DjgY2Eju.js +1 -0
  118. package/dist/assets/remote-1LxkpRED.js +1 -0
  119. package/dist/assets/{requirementDiagram-TGXJPOKE-ubPOmmNi.js → requirementDiagram-TGXJPOKE-BiXNX1NN.js} +1 -1
  120. package/dist/assets/rotate-cw-Dot4PVh0.js +1 -0
  121. package/dist/assets/{runtime-config-page-C7vksFVD.js → runtime-config-page-C6nzqTHg.js} +1 -1
  122. package/dist/assets/{sankeyDiagram-HTMAVEWB-8L_hXJWW.js → sankeyDiagram-HTMAVEWB-qzDeLztW.js} +1 -1
  123. package/dist/assets/save-B-jdTe_c.js +1 -0
  124. package/dist/assets/search-CEp2AIiV.js +1 -0
  125. package/dist/assets/{search-config-page-DKdEf7m9.js → search-config-page-Dp5guS5O.js} +1 -1
  126. package/dist/assets/{secrets-config-page-COlMqSfL.js → secrets-config-page-C7nRMjiv.js} +2 -2
  127. package/dist/assets/security-config-DvHUDxSg.js +1 -0
  128. package/dist/assets/{select-BACzapV3.js → select-BNA3cma8.js} +2 -2
  129. package/dist/assets/{sequenceDiagram-DBY2YBRQ-Dom6Qor5.js → sequenceDiagram-DBY2YBRQ-BtRbPQtL.js} +1 -1
  130. package/dist/assets/{sizeCapture-X5ZJPWSS-cZcFSpLJ.js → sizeCapture-X5ZJPWSS-CMYvM3SL.js} +1 -1
  131. package/dist/assets/skeleton-CbroEO8Q.js +1 -0
  132. package/dist/assets/{src-BE9E1xcR.js → src-Bw-mzbQb.js} +1 -1
  133. package/dist/assets/{stateDiagram-2N3HPSRC-CHrpxmGO.js → stateDiagram-2N3HPSRC-BJ-mFeOz.js} +1 -1
  134. package/dist/assets/stateDiagram-v2-6OUMAXLB-BE-HqxOK.js +1 -0
  135. package/dist/assets/{swimlanes-5IMT3BWC-D6nJS2xQ.js → swimlanes-5IMT3BWC-BsucVOO2.js} +2 -2
  136. package/dist/assets/swimlanesDiagram-G3AALYLV-Dr1H9LJH.js +8 -0
  137. package/dist/assets/{tag-chip-BnamTQVX.js → tag-chip-DmCNi4-9.js} +1 -1
  138. package/dist/assets/{timeline-definition-FHXFAJF6-DJNn-HQO.js → timeline-definition-FHXFAJF6-Bicewlzo.js} +1 -1
  139. package/dist/assets/treeView-QDETBFTQ-npJ5dbqS.js +1 -0
  140. package/dist/assets/treemap-6X3UGDF4-pYrBx6oa.js +1 -0
  141. package/dist/assets/{vennDiagram-L72KCM5P-CCEzHoXt.js → vennDiagram-L72KCM5P-BxOOnO9x.js} +1 -1
  142. package/dist/assets/wardley-OPB4EBWU-CmSRKLR8.js +1 -0
  143. package/dist/assets/{wardleyDiagram-EHGQE667-BhyVxVXt.js → wardleyDiagram-EHGQE667-Cl7NltMv.js} +1 -1
  144. package/dist/assets/x-BTo5-A8h.js +1 -0
  145. package/dist/assets/{xychartDiagram-FW5EYKEG-CacW_246.js → xychartDiagram-FW5EYKEG-DiMIxQ7N.js} +1 -1
  146. package/dist/index.html +23 -22
  147. package/package.json +8 -8
  148. package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +4 -0
  149. package/src/app/configs/app-navigation.config.ts +6 -0
  150. package/src/app/index.tsx +7 -0
  151. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +33 -1
  152. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +1 -0
  153. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +18 -1
  154. package/src/features/chat/features/conversation/hooks/__tests__/use-session-run-queue.test.tsx +26 -0
  155. package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +4 -1
  156. package/src/features/chat/features/conversation/hooks/use-session-run-queue.ts +8 -2
  157. package/src/features/chat/features/message/components/__tests__/chat-message-observation-event.test.tsx +46 -0
  158. package/src/features/chat/features/message/components/chat-message-list.container.tsx +11 -0
  159. package/src/features/chat/features/message/components/chat-message-observation-event.tsx +92 -0
  160. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline-compaction.utils.test.ts +38 -0
  161. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline.utils.test.ts +52 -0
  162. package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +34 -0
  163. package/src/features/chat/features/message/utils/chat-message-observation-event.utils.ts +81 -0
  164. package/src/features/chat/features/message/utils/chat-message-part.utils.ts +15 -1
  165. package/src/features/chat/features/message/utils/chat-message-timeline.utils.ts +62 -1
  166. package/src/features/chat/features/ncp/hooks/use-ncp-session-queries.ts +36 -2
  167. package/src/features/chat/features/session/components/__tests__/chat-sidebar-session-item.test.tsx +28 -0
  168. package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +10 -2
  169. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +35 -0
  170. package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +26 -47
  171. package/src/features/chat/features/session/components/session-header/chat-session-more-actions-menu.tsx +73 -0
  172. package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts +28 -0
  173. package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +3 -1
  174. package/src/features/chat/features/session/utils/ncp-session-context-metadata.utils.ts +3 -4
  175. package/src/features/chat/features/workspace/components/__tests__/chat-session-child-sessions.test.tsx +27 -1
  176. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +13 -0
  177. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +29 -0
  178. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +19 -0
  179. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +36 -2
  180. package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +4 -0
  181. package/src/features/chat/features/workspace/components/child-sessions/chat-session-child-sessions.tsx +28 -17
  182. package/src/features/chat/features/workspace/components/overview/__tests__/chat-session-continuous-attention.test.tsx +67 -0
  183. package/src/features/chat/features/workspace/components/overview/chat-session-continuous-attention.tsx +234 -0
  184. package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +9 -7
  185. package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +9 -4
  186. package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +25 -0
  187. package/src/features/chat/managers/chat-thread.manager.ts +5 -7
  188. package/src/features/chat/stores/chat-thread.store.ts +7 -4
  189. package/src/features/extensions/hooks/use-extensions.ts +11 -0
  190. package/src/features/extensions/index.ts +1 -0
  191. package/src/features/extensions/pages/__tests__/extensions-config-page.test.tsx +34 -0
  192. package/src/features/extensions/pages/extensions-config-page.tsx +136 -0
  193. package/src/shared/lib/api/ncp-session.types.ts +38 -0
  194. package/src/shared/lib/api/runtime-control.types.ts +17 -0
  195. package/src/shared/lib/api/types.ts +6 -0
  196. package/src/shared/lib/api/utils/ncp-session.utils.ts +35 -1
  197. package/src/shared/lib/api/utils/runtime-control.utils.ts +8 -0
  198. package/src/shared/lib/i18n/locales/en-US/chat.json +40 -0
  199. package/src/shared/lib/i18n/locales/en-US/core.json +27 -0
  200. package/src/shared/lib/i18n/locales/zh-CN/chat.json +40 -0
  201. package/src/shared/lib/i18n/locales/zh-CN/core.json +27 -0
  202. package/dist/assets/api-CUSlBpt_.js +0 -13
  203. package/dist/assets/architecture-TIHT7OUA-Bq0i9WiE.js +0 -1
  204. package/dist/assets/book-open-BJ-xAIp9.js +0 -1
  205. package/dist/assets/channel-iCaZQ87u.js +0 -1
  206. package/dist/assets/channels-list-page-nRHOasWA.js +0 -8
  207. package/dist/assets/chunk-2Q5K7J3B-DFQtlnJS.js +0 -1
  208. package/dist/assets/chunk-VAUOI2AC-CF9A-TeE.js +0 -1
  209. package/dist/assets/chunk-XXDRQBXY-BRxdfVCU.js +0 -1
  210. package/dist/assets/classDiagram-OUVF2IWQ-Bo28fPi-.js +0 -1
  211. package/dist/assets/classDiagram-v2-EOCWNBFH-BOVN9wUz.js +0 -1
  212. package/dist/assets/confirm-dialog-CCae9Fr4.js +0 -5
  213. package/dist/assets/cynefin-VYW2F7L2-B7rF3JRp.js +0 -1
  214. package/dist/assets/dist-1N9UpL3c.js +0 -1
  215. package/dist/assets/doc-browser-BHFCZIoL.js +0 -1
  216. package/dist/assets/doc-browser-CRlpqMwH.js +0 -1
  217. package/dist/assets/eventmodeling-45OFAUF4-DzQLXa3s.js +0 -1
  218. package/dist/assets/flowDiagram-23GEKE2U-C81eexJZ.js +0 -1
  219. package/dist/assets/gitGraph-TEB2WS4Q-Dt3ZqpFD.js +0 -1
  220. package/dist/assets/index-DdYOaKm7.js +0 -109
  221. package/dist/assets/index-DxCGKhAw.css +0 -1
  222. package/dist/assets/info-DKCQHKI2-CCCE7dXC.js +0 -1
  223. package/dist/assets/loader-circle-B-UdXjl4.js +0 -1
  224. package/dist/assets/mcp-marketplace-page-BUIGtSda.js +0 -1
  225. package/dist/assets/mcp-marketplace-page-CStb8ml2.js +0 -9
  226. package/dist/assets/model-config-page-BkfUYm95.js +0 -1
  227. package/dist/assets/packet-7NZHBO7P-D19yoxFV.js +0 -1
  228. package/dist/assets/pie-RZYD4A2V-hMaJbLrn.js +0 -1
  229. package/dist/assets/plus-Dw9QFnKr.js +0 -1
  230. package/dist/assets/providers-config-page-fxL4I4bz.js +0 -1
  231. package/dist/assets/radar-I7S5WNFK-C8BkvScA.js +0 -1
  232. package/dist/assets/railroad-3IZDKUUU-CozZOitC.js +0 -1
  233. package/dist/assets/railroad-abnf-AHOZXSZD-BqIoFXFi.js +0 -1
  234. package/dist/assets/railroad-ebnf-EBAXGLYW-CD2e3dng.js +0 -1
  235. package/dist/assets/railroad-peg-LSFZ7HO6-DoKlRUOF.js +0 -1
  236. package/dist/assets/react-Dlz_IxoP.js +0 -8
  237. package/dist/assets/refresh-cw-CK4I5UFv.js +0 -1
  238. package/dist/assets/remote-B0pPgtC6.js +0 -1
  239. package/dist/assets/rotate-cw-Z48dmh13.js +0 -1
  240. package/dist/assets/save-Bo37cIFb.js +0 -1
  241. package/dist/assets/search-CZiEm7YD.js +0 -1
  242. package/dist/assets/security-config-CitLihXA.js +0 -1
  243. package/dist/assets/skeleton-B2PCAZJG.js +0 -1
  244. package/dist/assets/stateDiagram-v2-6OUMAXLB-b1pZQlDD.js +0 -1
  245. package/dist/assets/swimlanesDiagram-G3AALYLV-CowxgWVn.js +0 -8
  246. package/dist/assets/treeView-QDETBFTQ-BQlYkJbz.js +0 -1
  247. package/dist/assets/treemap-6X3UGDF4-DzTOd1bd.js +0 -1
  248. package/dist/assets/wardley-OPB4EBWU-D80jQc0y.js +0 -1
  249. package/dist/assets/x-BzYEzkLA.js +0 -1
  250. /package/dist/assets/{array-COEOjKUv.js → array-BQVzeGRt.js} +0 -0
  251. /package/dist/assets/{chunk-KEIR6QF5-D7oHAcry.js → chunk-KEIR6QF5-f9psyCXK.js} +0 -0
  252. /package/dist/assets/{chunk-Y2CYZVJY-08IUpgTP.js → chunk-Y2CYZVJY-D1mAU_Po.js} +0 -0
  253. /package/dist/assets/{config-hints-BoE41XDI.js → config-hints-nV5HPOvq.js} +0 -0
  254. /package/dist/assets/{cytoscape.esm-HifWufkp.js → cytoscape.esm-0uDtdD0l.js} +0 -0
  255. /package/dist/assets/{defaultLocale-CA-_SsC1.js → defaultLocale-DSWXGgt0.js} +0 -0
  256. /package/dist/assets/{dist-C60rvfy8.js → dist--ta5rX12.js} +0 -0
  257. /package/dist/assets/{graphlib-CGYCNsoJ.js → graphlib-CQ6QV4yP.js} +0 -0
  258. /package/dist/assets/{host-capabilities-DPNRAjIj.js → host-capabilities-Tk038i04.js} +0 -0
  259. /package/dist/assets/{init-C3plFVsG.js → init-Y6h9jGtZ.js} +0 -0
  260. /package/dist/assets/{jszip.min-CeYs17Gn.js → jszip.min-8hyJpgpx.js} +0 -0
  261. /package/dist/assets/{katex-DRThweSG.js → katex-ARhY9PS1.js} +0 -0
  262. /package/dist/assets/{middleware-D05fK5Sb.js → middleware-DXsbf9xI.js} +0 -0
  263. /package/dist/assets/{path-BKtKxQ6Y.js → path-10BjdyLh.js} +0 -0
  264. /package/dist/assets/{rough.esm-DurL7-u7.js → rough.esm-DxgC_14r.js} +0 -0
  265. /package/dist/assets/{xlsx-Sg8fZ6nH.js → xlsx-DrDuDNAa.js} +0 -0
@@ -1,8 +1,26 @@
1
1
  import { render, screen } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
2
3
  import { beforeEach, expect, it, vi } from 'vitest';
3
4
  import { ChatSidebarSessionItem } from '@/features/chat/features/session/components/chat-sidebar-session-item';
4
5
 
6
+ const mocks = vi.hoisted(() => ({
7
+ copyText: vi.fn(),
8
+ }));
9
+
10
+ vi.mock('@nextclaw/agent-chat-ui', () => ({
11
+ copyText: mocks.copyText,
12
+ }));
13
+
14
+ vi.mock('sonner', () => ({
15
+ toast: {
16
+ success: vi.fn(),
17
+ error: vi.fn(),
18
+ },
19
+ }));
20
+
5
21
  beforeEach(() => {
22
+ mocks.copyText.mockReset();
23
+ mocks.copyText.mockResolvedValue(true);
6
24
  render(
7
25
  <ChatSidebarSessionItem
8
26
  sessionKey="session:current"
@@ -43,6 +61,16 @@ it('shows session actions only on hover or when an action owns focus', () => {
43
61
  expect(actions?.className).not.toContain(
44
62
  'group-focus-within/session:opacity-100',
45
63
  );
64
+ expect(screen.getByRole('button', { name: 'More actions' })).toBeTruthy();
65
+ });
66
+
67
+ it('copies the sidebar session ID from the more-actions menu', async () => {
68
+ const user = userEvent.setup();
69
+
70
+ await user.click(screen.getByRole('button', { name: 'More actions' }));
71
+ await user.click(screen.getByRole('button', { name: 'Copy session ID' }));
72
+
73
+ expect(mocks.copyText).toHaveBeenCalledWith('session:current');
46
74
  });
47
75
 
48
76
  it('sizes the runtime icon to the session title text', () => {
@@ -1,6 +1,7 @@
1
1
  import { AgentAvatar } from '@/shared/components/common/agent-avatar';
2
2
  import { SessionContextIconNode } from '@/features/chat/features/session/components/session-context-icon';
3
3
  import { SessionRunBadge } from '@/features/chat/features/session/components/session-run-badge';
4
+ import { ChatSessionMoreActionsMenu } from '@/features/chat/features/session/components/session-header/chat-session-more-actions-menu';
4
5
  import { IconActionButton } from '@/shared/components/ui/actions/icon-action-button';
5
6
  import { Input } from '@/shared/components/ui/input';
6
7
  import { type SessionContextView } from '@/features/chat/features/session/utils/session-context.utils';
@@ -93,9 +94,10 @@ function ChatSidebarSessionEditingView({
93
94
  type ChatSidebarSessionDisplayViewProps = Omit<
94
95
  ChatSidebarSessionItemProps,
95
96
  'sessionKey' | 'isEditing' | 'draftLabel' | 'isSaving' | 'onDraftLabelChange' | 'onSave' | 'onCancel'
96
- >;
97
+ > & Pick<ChatSidebarSessionItemProps, 'sessionKey'>;
97
98
 
98
99
  function ChatSidebarSessionDisplayView({
100
+ sessionKey,
99
101
  active,
100
102
  showUnreadDot,
101
103
  runStatus,
@@ -113,7 +115,7 @@ function ChatSidebarSessionDisplayView({
113
115
  onStartEditing,
114
116
  onTogglePinned
115
117
  }: ChatSidebarSessionDisplayViewProps) {
116
- const trailingControlsClassName = childSessionCount > 0 && onOpenChildSessions ? 'pr-20' : 'pr-12';
118
+ const trailingControlsClassName = childSessionCount > 0 && onOpenChildSessions ? 'pr-32' : 'pr-20';
117
119
 
118
120
  return (
119
121
  <div className="group/session relative">
@@ -212,6 +214,11 @@ function ChatSidebarSessionDisplayView({
212
214
  onStartEditing();
213
215
  }}
214
216
  />
217
+ <ChatSessionMoreActionsMenu
218
+ sessionKey={sessionKey}
219
+ triggerSize="sm"
220
+ triggerTone="strong"
221
+ />
215
222
  </div>
216
223
  </div>
217
224
  );
@@ -262,6 +269,7 @@ export function ChatSidebarSessionItem({
262
269
  />
263
270
  ) : (
264
271
  <ChatSidebarSessionDisplayView
272
+ sessionKey={sessionKey}
265
273
  active={active}
266
274
  showUnreadDot={showUnreadDot}
267
275
  runStatus={runStatus}
@@ -4,11 +4,23 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
4
4
  import { ChatSessionHeaderActions } from '@/features/chat/features/session/components/session-header/chat-session-header-actions';
5
5
 
6
6
  const mocks = vi.hoisted(() => ({
7
+ copyText: vi.fn(),
7
8
  updateSessionProject: vi.fn(),
8
9
  onDeleteSession: vi.fn(),
9
10
  onToggleWorkspace: vi.fn(),
10
11
  }));
11
12
 
13
+ vi.mock('@nextclaw/agent-chat-ui', () => ({
14
+ copyText: mocks.copyText,
15
+ }));
16
+
17
+ vi.mock('sonner', () => ({
18
+ toast: {
19
+ success: vi.fn(),
20
+ error: vi.fn(),
21
+ },
22
+ }));
23
+
12
24
  vi.mock('@/features/chat/features/session/hooks/use-chat-session-project', () => ({
13
25
  useChatSessionProject: () => mocks.updateSessionProject,
14
26
  }));
@@ -19,11 +31,34 @@ vi.mock('@/features/chat/features/session/components/session-header/chat-session
19
31
 
20
32
  describe('ChatSessionHeaderActions', () => {
21
33
  beforeEach(() => {
34
+ mocks.copyText.mockReset();
22
35
  mocks.updateSessionProject.mockReset();
23
36
  mocks.onDeleteSession.mockReset();
24
37
  mocks.onToggleWorkspace.mockReset();
25
38
  });
26
39
 
40
+ it('copies the current session ID from the more-actions menu', async () => {
41
+ const user = userEvent.setup();
42
+ mocks.copyText.mockResolvedValue(true);
43
+
44
+ render(
45
+ <ChatSessionHeaderActions
46
+ sessionKey="session-copy-me"
47
+ canDeleteSession
48
+ isDeletePending={false}
49
+ currentPath={null}
50
+ isWorkspaceOpen={false}
51
+ onToggleWorkspace={mocks.onToggleWorkspace}
52
+ onDeleteSession={mocks.onDeleteSession}
53
+ />
54
+ );
55
+
56
+ await user.click(screen.getByRole('button', { name: 'More actions' }));
57
+ await user.click(screen.getByRole('button', { name: 'Copy session ID' }));
58
+
59
+ expect(mocks.copyText).toHaveBeenCalledWith('session-copy-me');
60
+ });
61
+
27
62
  it('keeps only the set-project action in the more-actions menu when a project is already attached', async () => {
28
63
  const user = userEvent.setup();
29
64
 
@@ -1,10 +1,9 @@
1
1
  import { useState } from 'react';
2
- import { Braces, FolderOpen, MoreVertical, PanelRightClose, PanelRightOpen, Trash2 } from 'lucide-react';
2
+ import { Braces, FolderOpen, PanelRightClose, PanelRightOpen, Trash2 } from 'lucide-react';
3
3
  import { IconActionButton } from '@/shared/components/ui/actions/icon-action-button';
4
- import { Popover, PopoverTrigger } from '@/shared/components/ui/popover';
5
- import { ChatPopoverContent } from '@/features/chat/components/chat-popover-content';
6
4
  import { useChatSessionProject } from '@/features/chat/features/session/hooks/use-chat-session-project';
7
5
  import { ChatSessionHeaderMenuItem } from './chat-session-header-menu-item';
6
+ import { ChatSessionMoreActionsMenu } from './chat-session-more-actions-menu';
8
7
  import { ChatSessionMetadataDialog } from './chat-session-metadata-dialog';
9
8
  import { ChatSessionProjectDialog } from './chat-session-project-dialog';
10
9
  import { t } from '@/shared/lib/i18n';
@@ -35,7 +34,6 @@ export function ChatSessionHeaderActions({
35
34
  onDeleteSession,
36
35
  }: ChatSessionHeaderActionsProps) {
37
36
  const updateSessionProject = useChatSessionProject();
38
- const [isMenuOpen, setIsMenuOpen] = useState(false);
39
37
  const [isDialogOpen, setIsDialogOpen] = useState(false);
40
38
  const [isMetadataDialogOpen, setIsMetadataDialogOpen] = useState(false);
41
39
  const [isProjectPending, setIsProjectPending] = useState(false);
@@ -54,7 +52,6 @@ export function ChatSessionHeaderActions({
54
52
  persistToServer,
55
53
  });
56
54
  setIsDialogOpen(false);
57
- setIsMenuOpen(false);
58
55
  } finally {
59
56
  setIsProjectPending(false);
60
57
  }
@@ -63,48 +60,30 @@ export function ChatSessionHeaderActions({
63
60
  return (
64
61
  <div className={SESSION_HEADER_ACTION_GROUP_CLASS}>
65
62
  {sessionKey ? (
66
- <Popover open={isMenuOpen} onOpenChange={setIsMenuOpen}>
67
- <PopoverTrigger asChild>
68
- <IconActionButton
69
- icon={<MoreVertical className="h-4 w-4" />}
70
- label={t('chatSessionMoreActions')}
71
- tooltip={false}
72
- disabled={isBusy}
73
- />
74
- </PopoverTrigger>
75
- <ChatPopoverContent align="end" className="w-56 p-2">
76
- <div className="space-y-1">
77
- <ChatSessionHeaderMenuItem
78
- icon={FolderOpen}
79
- label={t('chatSessionSetProject')}
80
- onClick={() => {
81
- setIsMenuOpen(false);
82
- setIsDialogOpen(true);
83
- }}
84
- disabled={isBusy}
85
- />
86
- <ChatSessionHeaderMenuItem
87
- icon={Braces}
88
- label={t('chatSessionViewMetadata')}
89
- onClick={() => {
90
- setIsMenuOpen(false);
91
- setIsMetadataDialogOpen(true);
92
- }}
93
- disabled={isBusy}
94
- />
95
- <ChatSessionHeaderMenuItem
96
- icon={Trash2}
97
- label={t('chatDeleteSession')}
98
- onClick={() => {
99
- setIsMenuOpen(false);
100
- onDeleteSession();
101
- }}
102
- disabled={!canDeleteSession || isBusy}
103
- destructive
104
- />
105
- </div>
106
- </ChatPopoverContent>
107
- </Popover>
63
+ <ChatSessionMoreActionsMenu
64
+ sessionKey={sessionKey}
65
+ disabled={isBusy}
66
+ >
67
+ <ChatSessionHeaderMenuItem
68
+ icon={FolderOpen}
69
+ label={t('chatSessionSetProject')}
70
+ onClick={() => setIsDialogOpen(true)}
71
+ disabled={isBusy}
72
+ />
73
+ <ChatSessionHeaderMenuItem
74
+ icon={Braces}
75
+ label={t('chatSessionViewMetadata')}
76
+ onClick={() => setIsMetadataDialogOpen(true)}
77
+ disabled={isBusy}
78
+ />
79
+ <ChatSessionHeaderMenuItem
80
+ icon={Trash2}
81
+ label={t('chatDeleteSession')}
82
+ onClick={onDeleteSession}
83
+ disabled={!canDeleteSession || isBusy}
84
+ destructive
85
+ />
86
+ </ChatSessionMoreActionsMenu>
108
87
  ) : null}
109
88
  <IconActionButton
110
89
  icon={
@@ -0,0 +1,73 @@
1
+ import { useState, type ReactNode } from 'react';
2
+ import { copyText } from '@nextclaw/agent-chat-ui';
3
+ import { Copy, MoreVertical } from 'lucide-react';
4
+ import { toast } from 'sonner';
5
+ import { ChatPopoverContent } from '@/features/chat/components/chat-popover-content';
6
+ import {
7
+ IconActionButton,
8
+ type IconActionButtonSize,
9
+ type IconActionButtonTone,
10
+ } from '@/shared/components/ui/actions/icon-action-button';
11
+ import { Popover, PopoverTrigger } from '@/shared/components/ui/popover';
12
+ import { t } from '@/shared/lib/i18n';
13
+ import { ChatSessionHeaderMenuItem } from './chat-session-header-menu-item';
14
+
15
+ type ChatSessionMoreActionsMenuProps = {
16
+ sessionKey: string;
17
+ children?: ReactNode;
18
+ disabled?: boolean;
19
+ triggerSize?: IconActionButtonSize;
20
+ triggerTone?: IconActionButtonTone;
21
+ className?: string;
22
+ };
23
+
24
+ export async function copySessionId(sessionKey: string) {
25
+ const copied = await copyText(sessionKey);
26
+ toast[copied ? 'success' : 'error'](
27
+ t(copied ? 'chatSessionCopyIdSuccess' : 'chatSessionCopyIdFailed'),
28
+ );
29
+ }
30
+
31
+ export function ChatSessionMoreActionsMenu({
32
+ sessionKey,
33
+ children,
34
+ disabled = false,
35
+ triggerSize = 'md',
36
+ triggerTone = 'default',
37
+ className,
38
+ }: ChatSessionMoreActionsMenuProps) {
39
+ const [isOpen, setIsOpen] = useState(false);
40
+
41
+ const handleCopySessionId = () => {
42
+ setIsOpen(false);
43
+ void copySessionId(sessionKey);
44
+ };
45
+
46
+ return (
47
+ <Popover open={isOpen} onOpenChange={setIsOpen}>
48
+ <PopoverTrigger asChild>
49
+ <IconActionButton
50
+ icon={<MoreVertical className="h-4 w-4" />}
51
+ label={t('chatSessionMoreActions')}
52
+ tooltip={false}
53
+ size={triggerSize}
54
+ tone={triggerTone}
55
+ className={className}
56
+ disabled={disabled}
57
+ onClick={(event) => event.stopPropagation()}
58
+ />
59
+ </PopoverTrigger>
60
+ <ChatPopoverContent align="end" className="w-56 p-2">
61
+ <div className="space-y-1" onClick={() => setIsOpen(false)}>
62
+ <ChatSessionHeaderMenuItem
63
+ icon={Copy}
64
+ label={t('chatSessionCopyId')}
65
+ onClick={handleCopySessionId}
66
+ disabled={disabled}
67
+ />
68
+ {children}
69
+ </div>
70
+ </ChatPopoverContent>
71
+ </Popover>
72
+ );
73
+ }
@@ -35,6 +35,34 @@ it('preserves NCP extension parts for the chat presentation adapter', () => {
35
35
  ]);
36
36
  });
37
37
 
38
+ it('keeps deferred tool part slots out of the summary card view', () => {
39
+ const adapted = adaptNcpMessagePartsForChat([
40
+ {
41
+ type: 'tool-invocation',
42
+ toolCallId: 'tool-visible',
43
+ toolName: 'exec',
44
+ state: 'result',
45
+ args: undefined,
46
+ result: undefined,
47
+ },
48
+ {
49
+ type: 'tool-invocation',
50
+ toolCallId: 'tool-deferred',
51
+ toolName: 'read_file',
52
+ state: 'result',
53
+ payloadDeferred: true,
54
+ args: undefined,
55
+ result: undefined,
56
+ },
57
+ { type: 'text', text: 'done' },
58
+ ]);
59
+
60
+ expect(adapted.map((part) => part.type)).toEqual([
61
+ 'tool-invocation',
62
+ 'text',
63
+ ]);
64
+ });
65
+
38
66
  it('preserves standard tool execution timing without reading opaque result timing', () => {
39
67
  const adapted = adaptNcpMessageToUiMessage({
40
68
  id: 'ncp-message-timing-1',
@@ -194,7 +194,9 @@ function mapToolStatus(part: Extract<NcpMessagePart, { type: 'tool-invocation' }
194
194
 
195
195
  function toUiParts(parts: NcpMessagePart[]): UIMessage['parts'] {
196
196
  const uiParts: UIMessage['parts'] = [];
197
- for (const part of parts) {
197
+ for (const part of parts.filter((candidate) =>
198
+ candidate.type !== 'tool-invocation' || !candidate.payloadDeferred,
199
+ )) {
198
200
  if (part.type === 'text') {
199
201
  uiParts.push({ type: 'text', text: part.text });
200
202
  continue;
@@ -80,7 +80,7 @@ export type ContextCompactionTimelineView = {
80
80
  phase?: 'pre-run' | 'mid-run';
81
81
  continuationMessageId?: string;
82
82
  continuationMessageCoveredPartCount?: number;
83
- summary: string;
83
+ summary?: string;
84
84
  coveredMessageCount: number;
85
85
  coveredSessionMessageCount: number;
86
86
  originalEstimatedTokens: number;
@@ -120,7 +120,7 @@ export function readContextCompactionTimeline(message: Pick<NcpMessageView, 'met
120
120
  const continuationMessageCoveredPartCount = readNonNegativeInteger(
121
121
  rawCheckpoint.continuationMessageCoveredPartCount,
122
122
  );
123
- const summary = typeof rawCheckpoint.summary === 'string' ? rawCheckpoint.summary : null;
123
+ const summary = typeof rawCheckpoint.summary === 'string' ? rawCheckpoint.summary : undefined;
124
124
  const coveredMessageCount = readNonNegativeInteger(rawCheckpoint.coveredMessageCount);
125
125
  const coveredSessionMessageCount = readNonNegativeInteger(rawCheckpoint.coveredSessionMessageCount);
126
126
  const originalEstimatedTokens = readNonNegativeInteger(rawCheckpoint.originalEstimatedTokens);
@@ -130,7 +130,6 @@ export function readContextCompactionTimeline(message: Pick<NcpMessageView, 'met
130
130
  if (
131
131
  !id ||
132
132
  !status ||
133
- summary === null ||
134
133
  coveredMessageCount === null ||
135
134
  coveredSessionMessageCount === null ||
136
135
  originalEstimatedTokens === null ||
@@ -148,7 +147,7 @@ export function readContextCompactionTimeline(message: Pick<NcpMessageView, 'met
148
147
  ...(continuationMessageCoveredPartCount !== null
149
148
  ? { continuationMessageCoveredPartCount }
150
149
  : {}),
151
- summary,
150
+ ...(summary !== undefined ? { summary } : {}),
152
151
  coveredMessageCount,
153
152
  coveredSessionMessageCount,
154
153
  originalEstimatedTokens,
@@ -5,15 +5,30 @@ import { ChatSessionChildSessions } from '@/features/chat/features/workspace/com
5
5
  import type { ResolvedChildSessionTab } from '@/features/chat/features/ncp/hooks/use-ncp-child-session-tabs-view';
6
6
 
7
7
  const mocks = vi.hoisted(() => ({
8
+ copyText: vi.fn(),
8
9
  openSideChatDraft: vi.fn(),
9
10
  selectChildSessionDetail: vi.fn(),
10
11
  }));
11
12
 
13
+ vi.mock('@nextclaw/agent-chat-ui', () => ({
14
+ copyText: mocks.copyText,
15
+ }));
16
+
17
+ vi.mock('sonner', () => ({
18
+ toast: {
19
+ success: vi.fn(),
20
+ error: vi.fn(),
21
+ },
22
+ }));
23
+
12
24
  vi.mock('@/features/chat/components/providers/chat-presenter.provider', () => ({
13
25
  usePresenter: () => ({ chatThreadManager: mocks }),
14
26
  }));
15
27
 
16
- beforeEach(() => vi.clearAllMocks());
28
+ beforeEach(() => {
29
+ vi.clearAllMocks();
30
+ mocks.copyText.mockResolvedValue(true);
31
+ });
17
32
 
18
33
  function createChildSession(): ResolvedChildSessionTab {
19
34
  return {
@@ -49,3 +64,14 @@ it('keeps existing child sessions selectable beside the create action', async ()
49
64
  await user.click(screen.getByRole('button', { name: /Research branch/ }));
50
65
  expect(mocks.selectChildSessionDetail).toHaveBeenCalledWith('child-1');
51
66
  });
67
+
68
+ it('copies a child session ID from its own more-actions menu', async () => {
69
+ const user = userEvent.setup();
70
+ render(<ChatSessionChildSessions childSessionTabs={[createChildSession()]} sessionKey="parent-1" />);
71
+
72
+ await user.click(screen.getByRole('button', { name: 'More actions' }));
73
+ await user.click(screen.getByRole('button', { name: 'Copy session ID' }));
74
+
75
+ expect(mocks.copyText).toHaveBeenCalledWith('child-1');
76
+ expect(mocks.selectChildSessionDetail).not.toHaveBeenCalled();
77
+ });
@@ -37,6 +37,7 @@ const mocks = vi.hoisted(() => ({
37
37
  openFilePreview: vi.fn(),
38
38
  openProjectFiles: vi.fn(),
39
39
  openSessionCronPanel: vi.fn(),
40
+ openContinuousAttention: vi.fn(),
40
41
  removeWorkspacePath: vi.fn(),
41
42
  renameWorkspacePath: vi.fn(),
42
43
  selectChildSessionDetail: vi.fn(),
@@ -95,6 +96,18 @@ vi.mock('@/features/chat/features/conversation/components/session-conversation-a
95
96
  SessionConversationArea: () => <div data-testid="session-conversation-area" />,
96
97
  }));
97
98
 
99
+ vi.mock('@/features/chat/features/ncp/hooks/use-ncp-session-queries', () => ({
100
+ useNcpSessionObservations: () => ({
101
+ data: {
102
+ counts: { total: 0, context: 0, events: 0, needsAttention: 0 },
103
+ bindings: [],
104
+ subscriptions: [],
105
+ },
106
+ isPending: false,
107
+ isError: false,
108
+ }),
109
+ }));
110
+
98
111
  vi.mock('@/shared/hooks/use-server-path-browse', () => ({
99
112
  useServerPathBrowse: ({ path }: { path?: string | null }) => ({
100
113
  isLoading: false,
@@ -2,6 +2,7 @@ import { render, screen } from "@testing-library/react";
2
2
  import userEvent from "@testing-library/user-event";
3
3
  import { beforeEach, describe, expect, it, vi } from "vitest";
4
4
  import { ChatSessionWorkspacePanel } from "@/features/chat/features/workspace/components/chat-session-workspace-panel";
5
+ import { WorkspaceTabsBar } from "@/features/chat/features/workspace/components/chat-session-workspace-panel-nav";
5
6
  import type { ChatWorkspaceFileTab } from "@/features/chat/stores/chat-thread.store";
6
7
  import type * as ReactQuery from "@tanstack/react-query";
7
8
 
@@ -112,6 +113,34 @@ describe("ChatSessionWorkspacePanel", () => {
112
113
  ).toBe("1");
113
114
  });
114
115
 
116
+ it("shows more actions for a child-session workspace tab", async () => {
117
+ const user = userEvent.setup();
118
+ render(
119
+ <WorkspaceTabsBar
120
+ canGoBack={false}
121
+ canGoForward={false}
122
+ tabs={[
123
+ {
124
+ key: "child:child-1",
125
+ kind: "child-session",
126
+ title: "Child session",
127
+ tooltip: "Child session",
128
+ active: true,
129
+ sessionKey: "child-1",
130
+ onSelect: vi.fn(),
131
+ },
132
+ ]}
133
+ onClose={vi.fn()}
134
+ onGoBack={vi.fn()}
135
+ onGoForward={vi.fn()}
136
+ />,
137
+ );
138
+
139
+ await user.click(screen.getByRole("button", { name: "More actions" }));
140
+
141
+ expect(screen.getByRole("menuitem", { name: "Copy session ID" })).toBeTruthy();
142
+ });
143
+
115
144
  it("adds an opened project file to the active chat from its action menu", async () => {
116
145
  const user = userEvent.setup();
117
146
  renderPanel();
@@ -3,6 +3,7 @@ import type { ChatFileOpenActionViewModel } from '@nextclaw/agent-chat-ui';
3
3
  import {
4
4
  AlarmClock,
5
5
  ChevronRight,
6
+ Eye,
6
7
  FolderTree,
7
8
  GitBranch,
8
9
  MessageSquarePlus,
@@ -17,6 +18,8 @@ import { ChatSessionWorkspaceDirectoryBrowser } from '@/features/chat/features/w
17
18
  import { SessionCronJobContent } from '@/features/chat/features/workspace/components/session-cron-job-content';
18
19
  import { ChatSessionChildSessions } from '@/features/chat/features/workspace/components/child-sessions/chat-session-child-sessions';
19
20
  import { ChatSessionTokenUsage } from '@/features/chat/features/workspace/components/overview/chat-session-token-usage';
21
+ import { ChatSessionContinuousAttention } from '@/features/chat/features/workspace/components/overview/chat-session-continuous-attention';
22
+ import { useNcpSessionObservations } from '@/features/chat/features/ncp/hooks/use-ncp-session-queries';
20
23
  import type { ResolvedChildSessionTab } from '@/features/chat/features/ncp/hooks/use-ncp-child-session-tabs-view';
21
24
  import type { WorkspaceSelection } from '@/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils';
22
25
  import { useServerPathBrowse } from '@/shared/hooks/use-server-path-browse';
@@ -88,6 +91,7 @@ function WorkspaceOverview({
88
91
  sessionKey: string | null;
89
92
  }) {
90
93
  const presenter = usePresenter();
94
+ const observationsQuery = useNcpSessionObservations(sessionKey);
91
95
 
92
96
  return (
93
97
  <div className="h-full overflow-auto bg-gray-50/45 px-4 py-5 custom-scrollbar">
@@ -95,6 +99,17 @@ function WorkspaceOverview({
95
99
  <h2 className="text-base font-semibold text-gray-900">{t('chatWorkspaceOverview')}</h2>
96
100
  <p className="mt-1 text-xs leading-5 text-gray-500">{t('chatWorkspaceOverviewDescription')}</p>
97
101
  <div className="mt-4 space-y-2">
102
+ <WorkspaceOverviewEntry
103
+ count={observationsQuery.data?.counts.total ?? 0}
104
+ description={t('chatWorkspaceContinuousAttentionDescription')}
105
+ icon={<Eye className="h-4 w-4" />}
106
+ title={t('chatWorkspaceContinuousAttention')}
107
+ onClick={() => {
108
+ if (sessionKey) {
109
+ presenter.chatThreadManager.openContinuousAttention(sessionKey);
110
+ }
111
+ }}
112
+ />
98
113
  <WorkspaceOverviewEntry
99
114
  count={childSessionTabs.length}
100
115
  description={t('chatWorkspaceChildSessionsDescription')}
@@ -261,6 +276,10 @@ function WorkspaceSelectedContent({
261
276
  return <ChatSessionChildSessions childSessionTabs={childSessionTabs} sessionKey={sessionKey} />;
262
277
  }
263
278
 
279
+ if (activeSelection.kind === 'continuous-attention') {
280
+ return <ChatSessionContinuousAttention sessionKey={sessionKey} />;
281
+ }
282
+
264
283
  if (activeSelection.kind === 'side-chat-draft') {
265
284
  return (
266
285
  <>
@@ -3,6 +3,7 @@ import {
3
3
  ArrowLeft,
4
4
  ArrowRight,
5
5
  Code2,
6
+ Copy,
6
7
  Eye,
7
8
  FolderTree,
8
9
  GitBranch,
@@ -15,6 +16,7 @@ import {
15
16
  X,
16
17
  } from "lucide-react";
17
18
  import type { WorkspaceTabViewModel } from "@/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils";
19
+ import { copySessionId } from "@/features/chat/features/session/components/session-header/chat-session-more-actions-menu";
18
20
  import { AgentIdentityAvatar } from "@/shared/components/common/agent-identity";
19
21
  import { FileTypeIcon } from "@/shared/components/file-type-icon";
20
22
  import {
@@ -49,6 +51,10 @@ function WorkspaceTabIcon({
49
51
  return <AlarmClock className="h-3.5 w-3.5 shrink-0 text-gray-400" />;
50
52
  }
51
53
 
54
+ if (kind === "continuous-attention") {
55
+ return <Eye className="h-3.5 w-3.5 shrink-0 text-gray-400" />;
56
+ }
57
+
52
58
  if (kind === "file") {
53
59
  return <FileTypeIcon fileName={fileName ?? ""} size="compact" />;
54
60
  }
@@ -109,6 +115,28 @@ function buildWorkspaceFileMenuGroups(
109
115
  ];
110
116
  }
111
117
 
118
+ function buildWorkspaceSessionMenuGroups(
119
+ tab: WorkspaceTabViewModel,
120
+ ): ContextMenuGroup[] | undefined {
121
+ if (tab.kind !== "child-session" || !tab.sessionKey) {
122
+ return undefined;
123
+ }
124
+
125
+ return [
126
+ {
127
+ key: "session",
128
+ items: [
129
+ {
130
+ key: "copy-session-id",
131
+ label: t("chatSessionCopyId"),
132
+ icon: <Copy className="h-4 w-4" />,
133
+ onSelect: () => void copySessionId(tab.sessionKey!),
134
+ },
135
+ ],
136
+ },
137
+ ];
138
+ }
139
+
112
140
  function buildCompactWorkspaceTabs(
113
141
  tabs: readonly WorkspaceTabViewModel[],
114
142
  ): CompactTabStripTab[] {
@@ -139,8 +167,14 @@ function buildCompactWorkspaceTabs(
139
167
  closePlacement: "leading-hover",
140
168
  onSelect: tab.onSelect,
141
169
  onClose: tab.onClose,
142
- menuLabel: t("chatWorkspaceFileMoreActions"),
143
- menuGroups: buildWorkspaceFileMenuGroups(tab),
170
+ menuLabel:
171
+ tab.kind === "child-session"
172
+ ? t("chatSessionMoreActions")
173
+ : t("chatWorkspaceFileMoreActions"),
174
+ menuGroups:
175
+ tab.menuGroups ??
176
+ buildWorkspaceSessionMenuGroups(tab) ??
177
+ buildWorkspaceFileMenuGroups(tab),
144
178
  }));
145
179
  }
146
180
 
@@ -129,6 +129,10 @@ export function ChatSessionWorkspacePanel({
129
129
  if (sessionKey)
130
130
  presenter.chatThreadManager.openSessionCronPanel(sessionKey);
131
131
  },
132
+ onSelectContinuousAttention: () => {
133
+ if (sessionKey)
134
+ presenter.chatThreadManager.openContinuousAttention(sessionKey);
135
+ },
132
136
  }),
133
137
  [
134
138
  activeSelection,