@nextclaw/ui 0.15.21 → 0.15.23

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 (476) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/assets/{abnfDiagram-VRR7QNED-M4g_AyR8.js → abnfDiagram-VRR7QNED-DvTjgVMf.js} +1 -1
  3. package/dist/assets/{aiden0z-pptx-renderer.es-Bd99vut2.js → aiden0z-pptx-renderer.es-C1nfBAXR.js} +1 -1
  4. package/dist/assets/api-Glw2OGXK.js +13 -0
  5. package/dist/assets/appearance-settings-page-Bl_W_9m7.js +1 -0
  6. package/dist/assets/{arc-IO7WNWOB.js → arc-BvhODBXv.js} +1 -1
  7. package/dist/assets/architecture-TIHT7OUA-Bjp9jf0h.js +1 -0
  8. package/dist/assets/{architectureDiagram-ZJ3FMSHR-Dc-lvdUh.js → architectureDiagram-ZJ3FMSHR-BKO9RgNS.js} +1 -1
  9. package/dist/assets/{blockDiagram-677ZJIJ3-CcsoWpIY.js → blockDiagram-677ZJIJ3-DHpDXy7D.js} +1 -1
  10. package/dist/assets/{c4Diagram-LMCZKHZV-Bsrtg6b2.js → c4Diagram-LMCZKHZV-BG_iQnDr.js} +1 -1
  11. package/dist/assets/channel-5T3pdR-s.js +1 -0
  12. package/dist/assets/channels-list-page-C7E4Sxwd.js +8 -0
  13. package/dist/assets/chat-page-BT6dNI-4.js +1 -0
  14. package/dist/assets/chunk-2Q5K7J3B-DTZ3tGfE.js +1 -0
  15. package/dist/assets/{chunk-32BRIVSS-Bt08t8oG.js → chunk-32BRIVSS-DR9M0LLx.js} +1 -1
  16. package/dist/assets/{chunk-52WLFC77-Ddx68Xzx.js → chunk-52WLFC77-698iC3BB.js} +1 -1
  17. package/dist/assets/{chunk-5FCAYU7R-B7p9_q55.js → chunk-5FCAYU7R-DikTj9Ey.js} +1 -1
  18. package/dist/assets/{chunk-5HE753X5-D52KvdTm.js → chunk-5HE753X5-ko0o7_q0.js} +1 -1
  19. package/dist/assets/{chunk-5JV3BV7I-C329h8nI.js → chunk-5JV3BV7I-CEHufhgo.js} +1 -1
  20. package/dist/assets/{chunk-5TONJI2A-BZyUBxWt.js → chunk-5TONJI2A-CGgno1cu.js} +1 -1
  21. package/dist/assets/{chunk-5VM5RSS4-BUiYXZmu.js → chunk-5VM5RSS4-DkiB4cUQ.js} +1 -1
  22. package/dist/assets/{chunk-7BUUIJ7U-B7o-mn4r.js → chunk-7BUUIJ7U-DzptcytQ.js} +1 -1
  23. package/dist/assets/{chunk-BIQX33UG-Bey9GoRP.js → chunk-BIQX33UG-DMRSQJvT.js} +1 -1
  24. package/dist/assets/{chunk-C7G6YPKG-C6tosszM.js → chunk-C7G6YPKG-Bw1BwwuY.js} +1 -1
  25. package/dist/assets/{chunk-CQNSW5MT-BNrdkgGQ.js → chunk-CQNSW5MT-C0z6eHBk.js} +1 -1
  26. package/dist/assets/{chunk-CYSBUYHQ-DHODFeyB.js → chunk-CYSBUYHQ-ByPyAouO.js} +1 -1
  27. package/dist/assets/{chunk-EMLP6XTP-BcmLjrcq.js → chunk-EMLP6XTP-CJlAXWZg.js} +1 -1
  28. package/dist/assets/{chunk-EX3LRPZG-r-_eEtJI.js → chunk-EX3LRPZG-Dqq4zKJu.js} +1 -1
  29. package/dist/assets/{chunk-FWX5IMBZ-DOObDWbf.js → chunk-FWX5IMBZ-B0SNoCmi.js} +2 -2
  30. package/dist/assets/{chunk-HOUHSVGY-Bd0FnbWZ.js → chunk-HOUHSVGY-DG_GJih_.js} +1 -1
  31. package/dist/assets/{chunk-ICXQ74PX-ntNwAm4_.js → chunk-ICXQ74PX-Drxk_6bE.js} +1 -1
  32. package/dist/assets/{chunk-JG7HCLWE-YtRpblZN.js → chunk-JG7HCLWE-DdLFPpnd.js} +1 -1
  33. package/dist/assets/{chunk-JWPE2WC7-De5-D9ta.js → chunk-JWPE2WC7-B8M4G76F.js} +1 -1
  34. package/dist/assets/{chunk-MOJQB5TN-CdvKhyp2.js → chunk-MOJQB5TN-XP6FJHY_.js} +1 -1
  35. package/dist/assets/{chunk-MOZMSUNE-DJMMN_lu.js → chunk-MOZMSUNE-C_6x18Qw.js} +1 -1
  36. package/dist/assets/{chunk-OGEWGWER-DOFWVw3O.js → chunk-OGEWGWER-N25BzCmG.js} +1 -1
  37. package/dist/assets/{chunk-OSBZ3O6U-DZkrIN9r.js → chunk-OSBZ3O6U-ASDEm7y0.js} +1 -1
  38. package/dist/assets/{chunk-PUDLZKDR-D1wma2Yf.js → chunk-PUDLZKDR-CTlvAcn7.js} +1 -1
  39. package/dist/assets/{chunk-Q4XR5HBZ-CjPy94Lz.js → chunk-Q4XR5HBZ-Da9BWfB6.js} +1 -1
  40. package/dist/assets/{chunk-QBLGF6JB-Hy1bEgS-.js → chunk-QBLGF6JB-Bi6RpTJO.js} +1 -1
  41. package/dist/assets/{chunk-R7FJI6CG-CUPIHyh-.js → chunk-R7FJI6CG-DkMsSPvK.js} +1 -1
  42. package/dist/assets/{chunk-RYQCIY6F-D5Ua4pcv.js → chunk-RYQCIY6F-DZp0eU-j.js} +1 -1
  43. package/dist/assets/{chunk-U6XO7XAA-DaUQcqb7.js → chunk-U6XO7XAA-vfinDSrj.js} +1 -1
  44. package/dist/assets/{chunk-V7JOEXUC-DX2zLfQe.js → chunk-V7JOEXUC-C9Nksccs.js} +1 -1
  45. package/dist/assets/chunk-VAUOI2AC-HLGIHQ6k.js +1 -0
  46. package/dist/assets/{chunk-VR4S4FIN-CFA-WQik.js → chunk-VR4S4FIN-Xv6zfwXs.js} +1 -1
  47. package/dist/assets/{chunk-WYO6CB5R-Bk2HeDvJ.js → chunk-WYO6CB5R-mOJNzbWl.js} +2 -2
  48. package/dist/assets/chunk-XXDRQBXY-Dl0m6Ni-.js +1 -0
  49. package/dist/assets/{chunk-YOTPTUD7-DUG4FpBN.js → chunk-YOTPTUD7-1-aKSu4o.js} +1 -1
  50. package/dist/assets/{chunk-ZGVPDNZ5-70JdIGo0.js → chunk-ZGVPDNZ5-OJ8veMpK.js} +1 -1
  51. package/dist/assets/{chunk-ZIRB5QZD-XZdgBt5L.js → chunk-ZIRB5QZD-SR_Nq_PP.js} +1 -1
  52. package/dist/assets/classDiagram-OUVF2IWQ-DcDnzGNX.js +1 -0
  53. package/dist/assets/classDiagram-v2-EOCWNBFH-BKXKMzKM.js +1 -0
  54. package/dist/assets/config-split-page-D5BgtKrm.js +1 -0
  55. package/dist/assets/confirm-dialog-Bf907Azu.js +5 -0
  56. package/dist/assets/{cose-bilkent-JH36ORCC-B-PQoGX-.js → cose-bilkent-JH36ORCC-NjVgz2Bw.js} +1 -1
  57. package/dist/assets/cynefin-VYW2F7L2-BWg7U2UD.js +1 -0
  58. package/dist/assets/{cynefinDiagram-TSTJHNR4-BoLCPEM0.js → cynefinDiagram-TSTJHNR4-B_17LDqD.js} +1 -1
  59. package/dist/assets/{dagre-D7Ie4eXr.js → dagre-CMe101gk.js} +1 -1
  60. package/dist/assets/{dagre-VKFMJZFB-CKRrwZA-.js → dagre-VKFMJZFB-qo8nLb3B.js} +1 -1
  61. package/dist/assets/desktop-update-config-BwuyBPJb.js +1 -0
  62. package/dist/assets/{diagram-FQU43EPY-CASL1J3_.js → diagram-FQU43EPY-Ba3XIklC.js} +1 -1
  63. package/dist/assets/{diagram-G47NLZAW-CvLPBIzf.js → diagram-G47NLZAW-CG45TcUx.js} +1 -1
  64. package/dist/assets/{diagram-NH7WQ7WH-Ba6fGdYP.js → diagram-NH7WQ7WH-2F2HrwV-.js} +1 -1
  65. package/dist/assets/{diagram-OA4YK3LP-DkpfgEjM.js → diagram-OA4YK3LP-CXfWH9NZ.js} +1 -1
  66. package/dist/assets/{diagram-WEI45ONY-BP5UMVsN.js → diagram-WEI45ONY-CH_4yepc.js} +1 -1
  67. package/dist/assets/dist-CtLyEJeO.js +1 -0
  68. package/dist/assets/dist-dv_SUR54.js +1 -0
  69. package/dist/assets/doc-browser-C23NDGh5.js +1 -0
  70. package/dist/assets/doc-browser-DlOOhBxI.js +1 -0
  71. package/dist/assets/doc-browser-context-BIBjkwfV.js +1 -0
  72. package/dist/assets/{docx-preview-D0tp3a9T.js → docx-preview-CoM4JK7_.js} +1 -1
  73. package/dist/assets/{ebnfDiagram-CCIWWBDH-TJKbUcmc.js → ebnfDiagram-CCIWWBDH-BwiG4Ea1.js} +1 -1
  74. package/dist/assets/{erDiagram-Q63AITRT-DzGPKkKH.js → erDiagram-Q63AITRT-BML3hpnQ.js} +1 -1
  75. package/dist/assets/eventmodeling-45OFAUF4-SB-J4tpq.js +1 -0
  76. package/dist/assets/flowDiagram-23GEKE2U-BQ_u-eLM.js +1 -0
  77. package/dist/assets/{form-actions-DvvxCpL4.js → form-actions-DAzi68Cn.js} +1 -1
  78. package/dist/assets/{ganttDiagram-NO4QXBWP-JhJzsGp2.js → ganttDiagram-NO4QXBWP-80enfWbD.js} +1 -1
  79. package/dist/assets/gitGraph-TEB2WS4Q-DPxNwsFd.js +1 -0
  80. package/dist/assets/{gitGraphDiagram-IHSO6WYX-DZ1CKMj8.js → gitGraphDiagram-IHSO6WYX-CVLqXr-b.js} +1 -1
  81. package/dist/assets/host-capabilities-D9C4CBK5.js +1 -0
  82. package/dist/assets/index-BLNg2yg0.css +1 -0
  83. package/dist/assets/index-D3EKjEyK.js +107 -0
  84. package/dist/assets/info-DKCQHKI2-DNlNGH90.js +1 -0
  85. package/dist/assets/{infoDiagram-FWYZ7A6U-BNXNKfRQ.js → infoDiagram-FWYZ7A6U-Dyvs2t0i.js} +1 -1
  86. package/dist/assets/{ishikawaDiagram-FXEZZL3T-DoiEfjNw.js → ishikawaDiagram-FXEZZL3T-sSnxDKfh.js} +1 -1
  87. package/dist/assets/{journeyDiagram-5HDEW3XC-D6oK2HSy.js → journeyDiagram-5HDEW3XC-D22V71Mf.js} +1 -1
  88. package/dist/assets/{kanban-definition-HUTT4EX6-B2UnLOH-.js → kanban-definition-HUTT4EX6-D3ds0p-g.js} +1 -1
  89. package/dist/assets/{line-BM7PoxY5.js → line-CbyFs065.js} +1 -1
  90. package/dist/assets/{linear-B3DiqMjk.js → linear-4DKtnZBS.js} +1 -1
  91. package/dist/assets/loader-circle-BSg5u0r4.js +1 -0
  92. package/dist/assets/{map-BAAdHTqR.js → map-CcZzEoz6.js} +1 -1
  93. package/dist/assets/mcp-marketplace-page-ByhvQQj5.js +9 -0
  94. package/dist/assets/mcp-marketplace-page-CytXwnUs.js +1 -0
  95. package/dist/assets/{mermaid-parser.core-BeMviefY.js → mermaid-parser.core-DPmMOjXD.js} +2 -2
  96. package/dist/assets/{mermaid.core-D0Q77r9I.js → mermaid.core-BSf-ddRa.js} +3 -3
  97. package/dist/assets/{mindmap-definition-LN4V7U3C-BQ7r7bR8.js → mindmap-definition-LN4V7U3C-BUTwWPyx.js} +1 -1
  98. package/dist/assets/model-config-page-bgEx3MjQ.js +1 -0
  99. package/dist/assets/navigation-link-CB0OQbnY.js +3 -0
  100. package/dist/assets/{ordinal-Bt2N4IkA.js → ordinal-BgLYtOZE.js} +1 -1
  101. package/dist/assets/packet-7NZHBO7P-BpeiqkBZ.js +1 -0
  102. package/dist/assets/{pegDiagram-2B236MQR-B-C9-LGz.js → pegDiagram-2B236MQR-BEu_ZBjc.js} +1 -1
  103. package/dist/assets/pie-RZYD4A2V-Cy1yxsPd.js +1 -0
  104. package/dist/assets/{pieDiagram-ENE6RG2P-CtirZLtn.js → pieDiagram-ENE6RG2P-CAwAwuBd.js} +1 -1
  105. package/dist/assets/plus-CNjWdLhZ.js +1 -0
  106. package/dist/assets/provider-scoped-model-input-8F6JMd2n.js +1 -0
  107. package/dist/assets/providers-config-page-CxpTjR81.js +1 -0
  108. package/dist/assets/{quadrantDiagram-ABIIQ3AL-DiBWAzDU.js → quadrantDiagram-ABIIQ3AL-CwE6F-_2.js} +1 -1
  109. package/dist/assets/radar-I7S5WNFK-CEcPKCj8.js +1 -0
  110. package/dist/assets/railroad-3IZDKUUU-CW38eNDs.js +1 -0
  111. package/dist/assets/railroad-abnf-AHOZXSZD-ByrylcXf.js +1 -0
  112. package/dist/assets/railroad-ebnf-EBAXGLYW-CNB6GwP-.js +1 -0
  113. package/dist/assets/railroad-peg-LSFZ7HO6-x6hnqlBG.js +1 -0
  114. package/dist/assets/{railroadDiagram-RFXS5EU6-CP6b3PBI.js → railroadDiagram-RFXS5EU6-xgLekU3B.js} +1 -1
  115. package/dist/assets/react-Bk2ydNTe.js +8 -0
  116. package/dist/assets/{refresh-cw-C2e94pmp.js → refresh-cw-Ba0LZou4.js} +1 -1
  117. package/dist/assets/remote-Dv38PuMV.js +1 -0
  118. package/dist/assets/{requirementDiagram-TGXJPOKE-D8YV0-Jz.js → requirementDiagram-TGXJPOKE-CyVUzEX5.js} +1 -1
  119. package/dist/assets/rotate-cw-IkYVk9JX.js +1 -0
  120. package/dist/assets/runtime-config-page-C0ByyUVr.js +1 -0
  121. package/dist/assets/{sankeyDiagram-HTMAVEWB-C2vA4ojE.js → sankeyDiagram-HTMAVEWB-JK6bFmYB.js} +1 -1
  122. package/dist/assets/{save-CkwDOgvt.js → save-DTOAvqzt.js} +1 -1
  123. package/dist/assets/{search-CLXM2reO.js → search-D4X5DNMe.js} +1 -1
  124. package/dist/assets/search-config-page-Cr6254K5.js +1 -0
  125. package/dist/assets/{secrets-config-page-DQ_Curaj.js → secrets-config-page-W2Hx332d.js} +2 -2
  126. package/dist/assets/security-config-1E8OF4kR.js +1 -0
  127. package/dist/assets/select-CDv0g2Pj.js +41 -0
  128. package/dist/assets/{sequenceDiagram-DBY2YBRQ-B9LBmpUo.js → sequenceDiagram-DBY2YBRQ-BR5IokXV.js} +1 -1
  129. package/dist/assets/{settings-2-B9IT2HGd.js → settings-2-CoxzNABp.js} +1 -1
  130. package/dist/assets/{sizeCapture-X5ZJPWSS-9UIfya9-.js → sizeCapture-X5ZJPWSS-SxG_YnMA.js} +1 -1
  131. package/dist/assets/skeleton-DW018fJv.js +1 -0
  132. package/dist/assets/{src-DKf77Yzo.js → src-vgz8E_-O.js} +1 -1
  133. package/dist/assets/{stateDiagram-2N3HPSRC-oTW44vds.js → stateDiagram-2N3HPSRC-DD4OoYhn.js} +1 -1
  134. package/dist/assets/stateDiagram-v2-6OUMAXLB-ZC6dFORv.js +1 -0
  135. package/dist/assets/{swimlanes-5IMT3BWC-Cf7lcsay.js → swimlanes-5IMT3BWC-BWkFeyCT.js} +2 -2
  136. package/dist/assets/swimlanesDiagram-G3AALYLV-CRkh51Im.js +8 -0
  137. package/dist/assets/{tag-chip-CDkB5smU.js → tag-chip-CjkZEoS2.js} +1 -1
  138. package/dist/assets/{timeline-definition-FHXFAJF6-DoCuWKDJ.js → timeline-definition-FHXFAJF6-VTRPy6KA.js} +1 -1
  139. package/dist/assets/treeView-QDETBFTQ-Bw0DSCH7.js +1 -0
  140. package/dist/assets/treemap-6X3UGDF4-DFWBFcfh.js +1 -0
  141. package/dist/assets/use-confirm-dialog-CavtY1vJ.js +1 -0
  142. package/dist/assets/{vennDiagram-L72KCM5P-CpU0pzGe.js → vennDiagram-L72KCM5P-Bvk42Pek.js} +1 -1
  143. package/dist/assets/wardley-OPB4EBWU-CoWturk0.js +1 -0
  144. package/dist/assets/{wardleyDiagram-EHGQE667-CkN6nn6-.js → wardleyDiagram-EHGQE667-DOFjx2s3.js} +1 -1
  145. package/dist/assets/x-PTXYTEwz.js +1 -0
  146. package/dist/assets/{xychartDiagram-FW5EYKEG-DATpapm2.js → xychartDiagram-FW5EYKEG-DY5yRTUc.js} +1 -1
  147. package/dist/index.html +30 -32
  148. package/index.html +8 -0
  149. package/package.json +8 -8
  150. package/src/app/components/layout/page-layout.tsx +15 -6
  151. package/src/app/components/layout/sidebar-rail.styles.ts +0 -2
  152. package/src/app/components/layout/sidebar.tsx +2 -2
  153. package/src/app/styles/charcoal-theme.css +85 -0
  154. package/src/app/styles/design-system.css +23 -13
  155. package/src/features/agents/components/__tests__/agents-page.test.tsx +36 -24
  156. package/src/features/agents/components/agent-advanced-config-fields.tsx +7 -7
  157. package/src/features/agents/components/agent-details-dialog.tsx +8 -8
  158. package/src/features/agents/components/agent-dialogs.tsx +7 -7
  159. package/src/features/agents/components/agents-page.tsx +42 -62
  160. package/src/features/chat/components/chat-sidebar-list-mode-switch.tsx +2 -2
  161. package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx +46 -4
  162. package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +17 -1
  163. package/src/features/chat/components/conversation/__tests__/chat-conversation-panel.test.tsx +8 -22
  164. package/src/features/chat/components/conversation/__tests__/chat-conversation-workspace-section.test.tsx +69 -3
  165. package/src/features/chat/components/conversation/chat-conversation-content.tsx +24 -11
  166. package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +30 -31
  167. package/src/features/chat/components/conversation/chat-conversation-header.tsx +1 -1
  168. package/src/features/chat/components/conversation/chat-conversation-panel.tsx +1 -8
  169. package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +16 -4
  170. package/src/features/chat/components/layout/__tests__/chat-page-shell.test.tsx +23 -0
  171. package/src/features/chat/components/layout/__tests__/chat-sidebar-read-state.test.tsx +1 -1
  172. package/src/features/chat/components/layout/__tests__/chat-sidebar-session-area.test.tsx +52 -0
  173. package/src/features/chat/components/layout/__tests__/chat-sidebar-toolbar.test.tsx +6 -0
  174. package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +11 -17
  175. package/src/features/chat/components/layout/chat-page-shell.tsx +7 -15
  176. package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +32 -42
  177. package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +16 -67
  178. package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +1 -4
  179. package/src/features/chat/components/providers/chat-presenter.provider.tsx +2 -0
  180. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +84 -12
  181. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +258 -9
  182. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +51 -34
  183. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +50 -39
  184. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +128 -1
  185. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-input-state.test.tsx +67 -1
  186. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-slash-commands.test.tsx +4 -0
  187. package/src/features/chat/features/conversation/hooks/__tests__/use-session-provider-model-catalog.test.tsx +109 -0
  188. package/src/features/chat/features/conversation/hooks/use-chat-composer-reference-intent.ts +46 -0
  189. package/src/features/chat/features/conversation/hooks/use-session-conversation-composer-nodes.ts +26 -0
  190. package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +45 -12
  191. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-attachments.ts +1 -0
  192. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-query.ts +28 -3
  193. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +91 -37
  194. package/src/features/chat/features/conversation/hooks/use-session-conversation-recovery-actions.ts +64 -0
  195. package/src/features/chat/features/conversation/hooks/use-session-conversation-slash-commands.ts +2 -0
  196. package/src/features/chat/features/conversation/hooks/use-session-provider-model-catalog.ts +84 -0
  197. package/src/features/chat/features/conversation/utils/__tests__/session-message-composer.utils.test.ts +34 -0
  198. package/src/features/chat/features/conversation/utils/session-conversation-input-toolbar.utils.ts +18 -0
  199. package/src/features/chat/features/conversation/utils/session-message-composer.utils.ts +117 -0
  200. package/src/features/chat/features/conversation/utils/session-queued-input.utils.ts +10 -97
  201. package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +2 -1
  202. package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +3 -1
  203. package/src/features/chat/features/input/utils/__tests__/chat-composer-state.utils.test.ts +22 -1
  204. package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +23 -0
  205. package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +105 -0
  206. package/src/features/chat/features/input/utils/chat-composer-state.utils.ts +27 -2
  207. package/src/features/chat/features/input/utils/chat-composer-token-protocol.utils.ts +5 -0
  208. package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +97 -32
  209. package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +1 -1
  210. package/src/features/chat/features/input/utils/chat-input-toolbar.utils.ts +64 -10
  211. package/src/features/chat/features/message/components/__tests__/chat-message-assistant-header.container.test.tsx +149 -0
  212. package/src/features/chat/features/message/components/__tests__/chat-message-inline-editor.test.tsx +59 -0
  213. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +123 -90
  214. package/src/features/chat/features/message/components/__tests__/chat-message-timeline-dividers.test.tsx +43 -0
  215. package/src/features/chat/features/message/components/chat-message-inline-editor.tsx +111 -0
  216. package/src/features/chat/features/message/components/chat-message-list.container.tsx +81 -172
  217. package/src/features/chat/features/message/components/chat-message-timeline-dividers.tsx +75 -0
  218. package/src/features/chat/features/message/hooks/use-chat-inline-token-actions.ts +125 -0
  219. package/src/features/chat/features/message/hooks/use-chat-message-actions.tsx +187 -0
  220. package/src/features/chat/features/message/utils/__tests__/chat-message-action-targets.utils.test.ts +89 -0
  221. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline-compaction.utils.test.ts +194 -0
  222. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline-continuation-compaction.utils.test.ts +167 -0
  223. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline.utils.test.ts +203 -0
  224. package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +26 -0
  225. package/src/features/chat/features/message/utils/chat-message-action-targets.utils.ts +28 -0
  226. package/src/features/chat/features/message/utils/chat-message-part.utils.ts +38 -0
  227. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +1 -0
  228. package/src/features/chat/features/message/utils/chat-message-timeline.utils.ts +204 -7
  229. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime.test.tsx +180 -1
  230. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-session-conversation.test.tsx +184 -10
  231. package/src/features/chat/features/ncp/hooks/use-hydrated-ncp-agent.integration.test.tsx +57 -0
  232. package/src/features/chat/features/ncp/hooks/use-ncp-session-conversation.ts +90 -1
  233. package/src/features/chat/features/ncp/utils/__tests__/ncp-chat-query-derived.utils.test.ts +110 -0
  234. package/src/features/chat/features/ncp/utils/ncp-chat-query-derived.utils.ts +65 -3
  235. package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +10 -10
  236. package/src/features/chat/features/session/components/chat-sidebar-session-list.tsx +2 -2
  237. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +17 -0
  238. package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +63 -54
  239. package/src/features/chat/features/session/components/session-header/chat-session-header-menu-item.tsx +1 -1
  240. package/src/features/chat/features/session/components/session-header/chat-session-project-badge.tsx +1 -1
  241. package/src/features/chat/features/session/components/session-header/chat-session-switcher-groups.tsx +1 -1
  242. package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx +1 -1
  243. package/src/features/chat/features/session/hooks/__tests__/use-selected-session-context-window-indicator.test.tsx +87 -0
  244. package/src/features/chat/features/session/utils/__tests__/chat-session-display.utils.test.ts +26 -0
  245. package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts +23 -2
  246. package/src/features/chat/features/session/utils/chat-context-window-indicator.utils.ts +44 -5
  247. package/src/features/chat/features/session/utils/chat-session-display.utils.ts +48 -12
  248. package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +23 -4
  249. package/src/features/chat/features/session/utils/ncp-session-context-metadata.utils.ts +42 -4
  250. package/src/features/chat/features/welcome/components/__tests__/chat-conversation-welcome.test.tsx +6 -0
  251. package/src/features/chat/features/welcome/components/__tests__/chat-welcome.test.tsx +27 -9
  252. package/src/features/chat/features/welcome/components/chat-welcome-agent-picker.tsx +1 -1
  253. package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +11 -17
  254. package/src/features/chat/features/welcome/components/chat-welcome-prompt-suggestions.tsx +37 -0
  255. package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +1 -1
  256. package/src/features/chat/features/welcome/components/chat-welcome.tsx +35 -33
  257. package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-project-options.utils.test.ts +22 -6
  258. package/src/features/chat/features/welcome/utils/chat-welcome-project-options.utils.ts +13 -0
  259. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +73 -3
  260. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +22 -2
  261. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-text-selection.test.tsx +75 -0
  262. package/src/features/chat/features/workspace/components/__tests__/workspace-text-selection-menu.test.tsx +102 -0
  263. package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +145 -26
  264. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +42 -18
  265. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +50 -10
  266. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +48 -22
  267. package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +13 -2
  268. package/src/features/chat/features/workspace/components/workspace-text-selection-menu.tsx +223 -0
  269. package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +80 -0
  270. package/src/features/chat/features/workspace/utils/__tests__/workspace-text-excerpt.utils.test.ts +54 -0
  271. package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +47 -0
  272. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +16 -0
  273. package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +22 -1
  274. package/src/features/chat/features/workspace/utils/workspace-text-excerpt.utils.ts +53 -0
  275. package/src/features/chat/index.ts +1 -0
  276. package/src/features/chat/managers/__tests__/chat-composer-intent.manager.test.ts +106 -0
  277. package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +53 -5
  278. package/src/features/chat/managers/__tests__/chat-thread-workspace.manager.test.ts +93 -1
  279. package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +3 -10
  280. package/src/features/chat/managers/__tests__/provider-model-catalog-notice.manager.test.ts +57 -0
  281. package/src/features/chat/managers/chat-composer-intent.manager.ts +145 -0
  282. package/src/features/chat/managers/chat-session-list.manager.ts +14 -25
  283. package/src/features/chat/managers/chat-thread.manager.ts +32 -16
  284. package/src/features/chat/managers/provider-model-catalog-notice.manager.ts +121 -0
  285. package/src/features/chat/pages/__tests__/ncp-chat-page.test.tsx +3 -9
  286. package/src/features/chat/pages/ncp-chat-page.tsx +0 -1
  287. package/src/features/chat/presenters/chat.presenter.ts +3 -0
  288. package/src/features/chat/stores/__tests__/chat-composer-draft.store.test.ts +55 -0
  289. package/src/features/chat/stores/chat-composer-draft.store.ts +92 -0
  290. package/src/features/chat/stores/chat-thread.store.ts +2 -4
  291. package/src/features/chat/types/chat-input-bar.types.ts +6 -0
  292. package/src/features/chat/types/chat-input.types.ts +5 -0
  293. package/src/features/chat/types/chat-message.types.ts +5 -0
  294. package/src/features/cron/components/__tests__/cron-config.test.tsx +100 -19
  295. package/src/features/cron/components/cron-config.tsx +19 -33
  296. package/src/features/cron/components/cron-job-detail-dialog.tsx +42 -16
  297. package/src/features/cron/components/cron-job-row.tsx +18 -1
  298. package/src/features/cron/components/cron-task-discovery.tsx +1 -1
  299. package/src/features/inbox/components/inbox-delivery-content.test.tsx +8 -2
  300. package/src/features/inbox/components/inbox-delivery-content.tsx +1 -1
  301. package/src/features/inbox/components/inbox-reader-dialog.test.tsx +10 -4
  302. package/src/features/inbox/components/inbox-reader-dialog.tsx +37 -61
  303. package/src/features/inbox/pages/inbox-page.tsx +90 -98
  304. package/src/features/marketplace/components/marketplace-page-parts.tsx +1 -1
  305. package/src/features/notifications/components/__tests__/app-notification-toast.test.tsx +31 -6
  306. package/src/features/notifications/components/app-notification-toast.tsx +54 -15
  307. package/src/features/notifications/managers/__tests__/app-notification.manager.test.ts +2 -0
  308. package/src/features/notifications/managers/app-notification.manager.ts +3 -0
  309. package/src/features/panel-apps/components/panel-app-list-item.tsx +1 -1
  310. package/src/features/panel-apps/components/panel-app-toolbar.tsx +2 -2
  311. package/src/features/panel-apps/components/panel-apps-list.tsx +4 -4
  312. package/src/features/panel-apps/utils/panel-app-doc-browser.utils.tsx +1 -0
  313. package/src/features/pwa/managers/__tests__/pwa-shell-theme.manager.test.ts +9 -0
  314. package/src/features/pwa/managers/pwa-shell-theme.manager.ts +1 -0
  315. package/src/features/service-apps/components/__tests__/service-apps-panel.test.tsx +5 -4
  316. package/src/features/service-apps/components/service-app-list-item.tsx +470 -0
  317. package/src/features/service-apps/components/service-apps-panel.tsx +76 -490
  318. package/src/features/settings/components/config/__tests__/provider-models-section.test.tsx +239 -0
  319. package/src/features/settings/components/config/provider-auth-section.tsx +90 -59
  320. package/src/features/settings/components/config/provider-form-detail-pane.tsx +307 -0
  321. package/src/features/settings/components/config/provider-form.tsx +149 -367
  322. package/src/features/settings/components/config/provider-model-list.tsx +293 -0
  323. package/src/features/settings/components/config/provider-model-suggestions-panel.tsx +151 -0
  324. package/src/features/settings/components/config/provider-models-section.tsx +178 -149
  325. package/src/features/settings/components/config/provider-status-badge.tsx +4 -2
  326. package/src/features/settings/hooks/__tests__/use-provider-connectivity.test.tsx +98 -0
  327. package/src/features/settings/hooks/__tests__/use-provider-model-suggestions.test.tsx +107 -0
  328. package/src/features/settings/hooks/use-provider-connectivity.ts +132 -0
  329. package/src/features/settings/hooks/use-provider-model-suggestions.ts +51 -0
  330. package/src/features/settings/pages/__tests__/appearance-settings-page.test.tsx +2 -2
  331. package/src/features/settings/pages/__tests__/providers-config-page.test.tsx +12 -0
  332. package/src/features/settings/pages/__tests__/search-config-page.test.tsx +48 -0
  333. package/src/features/settings/pages/providers-config-page.tsx +2 -1
  334. package/src/features/settings/pages/search-config-page.tsx +17 -8
  335. package/src/features/settings/utils/__tests__/provider-form-support.utils.test.ts +42 -0
  336. package/src/features/settings/utils/provider-form-context.utils.ts +2 -0
  337. package/src/features/settings/utils/provider-form-model.utils.ts +25 -0
  338. package/src/features/settings/utils/provider-form-support.utils.ts +21 -5
  339. package/src/index.css +1 -1
  340. package/src/platforms/desktop/types/desktop-update.types.ts +5 -0
  341. package/src/platforms/mobile/components/__tests__/mobile-app-shell.test.tsx +13 -7
  342. package/src/platforms/mobile/components/__tests__/mobile-bottom-nav.test.tsx +18 -20
  343. package/src/shared/components/actions/__tests__/navigation-link.test.tsx +14 -3
  344. package/src/shared/components/actions/navigation-link.tsx +35 -15
  345. package/src/shared/components/common/__tests__/agent-avatar.test.tsx +3 -2
  346. package/src/shared/components/common/agent-avatar.tsx +3 -3
  347. package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +66 -0
  348. package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +5 -2
  349. package/src/shared/components/doc-browser/doc-browser-renderer.types.ts +1 -0
  350. package/src/shared/components/doc-browser/doc-browser.tsx +9 -0
  351. package/src/shared/components/doc-browser/hooks/use-doc-browser-scroll-restoration.ts +126 -0
  352. package/src/shared/components/path-picker/server-path-picker-directory-list.tsx +1 -1
  353. package/src/shared/components/path-picker/server-path-picker-locations.tsx +1 -1
  354. package/src/shared/components/path-picker/server-path-picker-navigation-bar.tsx +3 -3
  355. package/src/shared/components/settings/settings-page.tsx +1 -1
  356. package/src/shared/components/ui/actions/__tests__/icon-action-button.test.tsx +1 -1
  357. package/src/shared/components/ui/actions/icon-action-button.tsx +1 -1
  358. package/src/shared/components/ui/button.tsx +4 -4
  359. package/src/shared/components/ui/context-menu/__tests__/context-menu.test.tsx +103 -0
  360. package/src/shared/components/ui/context-menu/context-menu.tsx +263 -0
  361. package/src/shared/components/ui/dialog.tsx +1 -1
  362. package/src/shared/components/ui/input.tsx +1 -1
  363. package/src/shared/components/ui/scroll-area.tsx +2 -0
  364. package/src/shared/components/ui/select.tsx +1 -1
  365. package/src/shared/components/ui/switch.tsx +1 -1
  366. package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +19 -6
  367. package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +28 -67
  368. package/src/shared/components/ui/tabs.tsx +1 -1
  369. package/src/shared/components/ui/textarea.tsx +1 -1
  370. package/src/shared/hooks/use-config.ts +33 -1
  371. package/src/shared/lib/api/__tests__/ncp-session-query-cache.utils.test.ts +26 -0
  372. package/src/shared/lib/api/ncp-session-query-cache.utils.ts +7 -10
  373. package/src/shared/lib/api/ncp-session.types.ts +7 -0
  374. package/src/shared/lib/api/types.ts +38 -1
  375. package/src/shared/lib/api/utils/config.utils.ts +14 -0
  376. package/src/shared/lib/cron/cron-job-view.utils.ts +4 -0
  377. package/src/shared/lib/host-capabilities/__tests__/host-capability.manager.test.ts +26 -0
  378. package/src/shared/lib/host-capabilities/host-capability.manager.ts +28 -0
  379. package/src/shared/lib/i18n/locales/en-US/chat.json +45 -17
  380. package/src/shared/lib/i18n/locales/en-US/core.json +26 -0
  381. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +1 -0
  382. package/src/shared/lib/i18n/locales/en-US/search.json +1 -0
  383. package/src/shared/lib/i18n/locales/zh-CN/chat.json +45 -17
  384. package/src/shared/lib/i18n/locales/zh-CN/core.json +26 -0
  385. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +1 -0
  386. package/src/shared/lib/i18n/locales/zh-CN/search.json +1 -0
  387. package/src/shared/lib/provider-models/__tests__/anonymous-provider-configuration.test.ts +40 -0
  388. package/src/shared/lib/provider-models/index.ts +4 -3
  389. package/src/shared/lib/session-project/__tests__/workspace-file-breadcrumb.test.ts +28 -1
  390. package/src/shared/lib/session-project/workspace-file-breadcrumb.utils.ts +23 -0
  391. package/src/shared/lib/theme/index.test.ts +11 -0
  392. package/src/shared/lib/theme/index.ts +2 -0
  393. package/dist/assets/api-CHo2gekJ.js +0 -13
  394. package/dist/assets/app-presenter-provider-2_DmWYT9.js +0 -108
  395. package/dist/assets/app-presenter-provider-D3BuuOyZ.css +0 -1
  396. package/dist/assets/appearance-settings-page-BEy2UE7R.js +0 -1
  397. package/dist/assets/architecture-TIHT7OUA-Cii4JyA2.js +0 -1
  398. package/dist/assets/book-open-BrZL4o9L.js +0 -1
  399. package/dist/assets/card-B5rRLu_6.js +0 -1
  400. package/dist/assets/channel-BSKkTPe8.js +0 -1
  401. package/dist/assets/channels-list-page-C1dnm68I.js +0 -8
  402. package/dist/assets/chat-page-Dz6tvZ91.js +0 -1
  403. package/dist/assets/chunk-2Q5K7J3B-CDMOR1xZ.js +0 -1
  404. package/dist/assets/chunk-VAUOI2AC-BRvqNW76.js +0 -1
  405. package/dist/assets/chunk-XXDRQBXY-CwX_mqJ3.js +0 -1
  406. package/dist/assets/classDiagram-OUVF2IWQ-RzbGxG77.js +0 -1
  407. package/dist/assets/classDiagram-v2-EOCWNBFH-C_gWB7WT.js +0 -1
  408. package/dist/assets/config-split-page-6y6W6dz4.js +0 -1
  409. package/dist/assets/confirm-dialog-q1CpEmR8.js +0 -5
  410. package/dist/assets/cynefin-VYW2F7L2-fSLIS0X3.js +0 -1
  411. package/dist/assets/desktop-update-config-DrdlwP4j.js +0 -1
  412. package/dist/assets/dist-C9EX46wM.js +0 -41
  413. package/dist/assets/doc-browser-BfttDSti.js +0 -1
  414. package/dist/assets/doc-browser-DH-3hDnI.js +0 -1
  415. package/dist/assets/doc-browser-context-TU2heon0.js +0 -1
  416. package/dist/assets/eventmodeling-45OFAUF4-XVcQGgha.js +0 -1
  417. package/dist/assets/eye-B-3dyZNA.js +0 -1
  418. package/dist/assets/flowDiagram-23GEKE2U-BiFSctqz.js +0 -1
  419. package/dist/assets/gitGraph-TEB2WS4Q-C014uXqS.js +0 -1
  420. package/dist/assets/host-capabilities-B-TtABsP.js +0 -1
  421. package/dist/assets/index-CXDPLDFD.css +0 -1
  422. package/dist/assets/index-k_eWScvN.js +0 -2
  423. package/dist/assets/info-DKCQHKI2-CQ2qjGhU.js +0 -1
  424. package/dist/assets/loader-circle-B35NlHTI.js +0 -1
  425. package/dist/assets/logo-badge-DB51c0cS.js +0 -1
  426. package/dist/assets/mcp-marketplace-page-CkPtwPBk.js +0 -1
  427. package/dist/assets/mcp-marketplace-page-DWtsIqml.js +0 -9
  428. package/dist/assets/model-config-page-D6cGNnMM.js +0 -1
  429. package/dist/assets/navigation-link-BSJEUiwD.js +0 -1
  430. package/dist/assets/packet-7NZHBO7P-6IFyydij.js +0 -1
  431. package/dist/assets/pie-RZYD4A2V-spXQC_wr.js +0 -1
  432. package/dist/assets/plus-BRVe5jSJ.js +0 -1
  433. package/dist/assets/popover-Bp9IpE_N.js +0 -1
  434. package/dist/assets/provider-models-CXUgDxGR.js +0 -1
  435. package/dist/assets/provider-scoped-model-input-dk7fz9NL.js +0 -1
  436. package/dist/assets/providers-config-page-DTP2YfxA.js +0 -1
  437. package/dist/assets/radar-I7S5WNFK-DWVA8lvY.js +0 -1
  438. package/dist/assets/railroad-3IZDKUUU-Ce51u8y1.js +0 -1
  439. package/dist/assets/railroad-abnf-AHOZXSZD-D7i7cqI_.js +0 -1
  440. package/dist/assets/railroad-ebnf-EBAXGLYW-BEg2hcPs.js +0 -1
  441. package/dist/assets/railroad-peg-LSFZ7HO6-CxROSzbi.js +0 -1
  442. package/dist/assets/react-D6ShNq95.js +0 -8
  443. package/dist/assets/remote-FllhXW3o.js +0 -1
  444. package/dist/assets/rotate-cw-CTC-Qszi.js +0 -1
  445. package/dist/assets/runtime-config-page-C57OLdPm.js +0 -1
  446. package/dist/assets/search-config-page-tnJXaaSr.js +0 -1
  447. package/dist/assets/security-config-S00DjFfO.js +0 -1
  448. package/dist/assets/select-CNFBf00l.js +0 -1
  449. package/dist/assets/skeleton-Dc06gatK.js +0 -1
  450. package/dist/assets/stateDiagram-v2-6OUMAXLB-Bui4RXQK.js +0 -1
  451. package/dist/assets/swimlanesDiagram-G3AALYLV-DTwUsCBz.js +0 -8
  452. package/dist/assets/tabs-BuNr8LSO.js +0 -1
  453. package/dist/assets/trash-2-GchaJuEM.js +0 -1
  454. package/dist/assets/treeView-QDETBFTQ-D5zsTiuL.js +0 -1
  455. package/dist/assets/treemap-6X3UGDF4-CR3dxqNs.js +0 -1
  456. package/dist/assets/use-config-C69rR15i.js +0 -1
  457. package/dist/assets/use-confirm-dialog-DF_EgPYx.js +0 -1
  458. package/dist/assets/use-viewport-layout-BACNZyMY.js +0 -1
  459. package/dist/assets/wardley-OPB4EBWU-LatRT_Lx.js +0 -1
  460. package/dist/assets/x-BXFquY9e.js +0 -1
  461. package/src/features/chat/features/welcome/components/chat-welcome-capability-grid.tsx +0 -59
  462. /package/dist/assets/{array-CAkJT3no.js → array-NADKd9aU.js} +0 -0
  463. /package/dist/assets/{chunk-KEIR6QF5-BgdDoWEH.js → chunk-KEIR6QF5-BuyuzJJL.js} +0 -0
  464. /package/dist/assets/{chunk-Y2CYZVJY-pTllD0P3.js → chunk-Y2CYZVJY-D_B2ZRNt.js} +0 -0
  465. /package/dist/assets/{config-hints-S3g-UVZb.js → config-hints-BcfVUMij.js} +0 -0
  466. /package/dist/assets/{cytoscape.esm-DmURt-5G.js → cytoscape.esm-BANTlgza.js} +0 -0
  467. /package/dist/assets/{defaultLocale-BKy_KP3O.js → defaultLocale-C4_L0aEs.js} +0 -0
  468. /package/dist/assets/{dist-IgNNB7K9.js → dist-DAPEIJbz.js} +0 -0
  469. /package/dist/assets/{graphlib-xw_TBHyH.js → graphlib-ChqXg0dI.js} +0 -0
  470. /package/dist/assets/{init-C9xlLIAu.js → init-D8wZg9xq.js} +0 -0
  471. /package/dist/assets/{jszip.min-Das2IAnb.js → jszip.min-CkbBfwAQ.js} +0 -0
  472. /package/dist/assets/{katex-DSy1eD2d.js → katex-TZJFa0J_.js} +0 -0
  473. /package/dist/assets/{middleware-Df-a4x-q.js → middleware-B3k-TSmZ.js} +0 -0
  474. /package/dist/assets/{path-DB0USYHi.js → path-DodfxcX7.js} +0 -0
  475. /package/dist/assets/{rough.esm-B1B52ZQS.js → rough.esm-CUaa1Rbe.js} +0 -0
  476. /package/dist/assets/{xlsx-DfjC_iml.js → xlsx-CxByi7bR.js} +0 -0
@@ -21,17 +21,12 @@ import {
21
21
  useChatInputSurfaceState,
22
22
  } from '@/features/chat/features/input/hooks/use-chat-input-surface-state';
23
23
  import { useChatModelFavorites } from '@/features/chat/features/input/hooks/use-chat-model-favorites';
24
- import {
25
- deriveChatComposerDraft,
26
- deriveSelectedSkillsFromComposer,
27
- pruneComposerAttachments,
28
- syncComposerSkills,
29
- } from '@/features/chat/features/input/utils/chat-composer-state.utils';
24
+ import { syncComposerSkills } from '@/features/chat/features/input/utils/chat-composer-state.utils';
30
25
  import {
31
26
  buildModelStateHint,
32
- type ChatModelRecord,
33
27
  type ChatSkillRecord,
34
28
  type ChatThinkingLevel,
29
+ toChatModelRecords,
35
30
  } from '@/features/chat/features/input/utils/chat-input-bar.utils';
36
31
  import {
37
32
  hasNcpChatModelOptions,
@@ -49,6 +44,8 @@ import {
49
44
  } from '@/features/chat/managers/chat-recent-skills.manager';
50
45
 
51
46
  import { useSessionConversationInputAttachments } from '@/features/chat/features/conversation/hooks/use-session-conversation-input-attachments';
47
+ import { useChatComposerReferenceIntent } from '@/features/chat/features/conversation/hooks/use-chat-composer-reference-intent';
48
+ import { useSessionConversationComposerNodes } from '@/features/chat/features/conversation/hooks/use-session-conversation-composer-nodes';
52
49
  import { useSessionConversationSlashCommands } from '@/features/chat/features/conversation/hooks/use-session-conversation-slash-commands';
53
50
  import { ChatConversationTrack } from '@/features/chat/components/conversation/chat-conversation-track';
54
51
  import { useChatMessageLayoutStore } from '@/features/chat/stores/chat-message-layout.store';
@@ -66,6 +63,7 @@ import {
66
63
  resolveThinkingForConversationModel,
67
64
  } from '@/features/chat/features/conversation/utils/session-conversation-input-toolbar.utils';
68
65
  import { SessionQueuedInputRows } from './session-queued-input-rows';
66
+ import { toast } from 'sonner';
69
67
 
70
68
  type SessionConversationInputQuery = ReturnType<typeof useSessionConversationInputQuery>;
71
69
  type SkillSource = SessionSkillEntryView['source'];
@@ -76,6 +74,7 @@ export type SessionConversationInputController = {
76
74
  readonly editQueuedInput: (id: string) => void;
77
75
  readonly isSending: boolean;
78
76
  readonly queuedInputs: readonly SessionConversationQueuedInput[];
77
+ readonly primaryAction: 'continue' | 'send';
79
78
  readonly sendDisabled: boolean;
80
79
  readonly stopDisabled: boolean;
81
80
  readonly send: () => Promise<void> | void;
@@ -99,20 +98,6 @@ function toSkillRecords(
99
98
  }));
100
99
  }
101
100
 
102
- function toModelRecords(snapshotModels: SessionConversationInputQuery['modelOptions']): ChatModelRecord[] {
103
- return snapshotModels.map((model) => ({
104
- value: model.value,
105
- modelLabel: model.modelLabel,
106
- providerLabel: model.providerLabel,
107
- thinkingCapability: model.thinkingCapability
108
- ? {
109
- supported: model.thinkingCapability.supported as ChatThinkingLevel[],
110
- default: (model.thinkingCapability.default as ChatThinkingLevel | null | undefined) ?? null,
111
- }
112
- : null,
113
- }));
114
- }
115
-
116
101
  function useSessionConversationInputLabels(language: string) {
117
102
  const { skillGroupLabels, skillScopeLabels } = useMemo(() => {
118
103
  void language;
@@ -167,7 +152,7 @@ function useSessionConversationInputCollections(params: {
167
152
  () => toSkillRecords(sourceSkillRecords, skillScopeLabels, skillGroupLabels),
168
153
  [skillGroupLabels, skillScopeLabels, sourceSkillRecords],
169
154
  );
170
- const modelRecords = useMemo(() => toModelRecords(modelOptions), [modelOptions]);
155
+ const modelRecords = useMemo(() => toChatModelRecords(modelOptions), [modelOptions]);
171
156
  return {
172
157
  skillRecords,
173
158
  modelRecords,
@@ -193,6 +178,7 @@ type SessionConversationInputProps = {
193
178
  readonly inputQuery: SessionConversationInputQuery;
194
179
  readonly inputSnapshot: SessionConversationInputSnapshot;
195
180
  readonly onContextCompactingChange?: (sessionId: string, isCompacting: boolean) => void;
181
+ readonly placeholder?: string;
196
182
  readonly surface?: 'default' | 'embedded';
197
183
  };
198
184
 
@@ -204,6 +190,7 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
204
190
  inputQuery,
205
191
  inputSnapshot,
206
192
  onContextCompactingChange,
193
+ placeholder,
207
194
  surface = 'default',
208
195
  } = props;
209
196
  const presenter = usePresenter();
@@ -225,6 +212,10 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
225
212
  skillGroupLabels: labels.skillGroupLabels,
226
213
  skillScopeLabels: labels.skillScopeLabels,
227
214
  });
215
+ const discoveredModelRecords = useMemo(
216
+ () => toChatModelRecords(inputQuery.discoveredModelOptions),
217
+ [inputQuery.discoveredModelOptions],
218
+ );
228
219
  const slashCommands = useSessionConversationSlashCommands({
229
220
  language,
230
221
  onContextCompactingChange,
@@ -277,7 +268,7 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
277
268
  const attachmentSupported = true;
278
269
  const textareaPlaceholder = isModelOptionsEmpty
279
270
  ? t('chatModelNoOptions')
280
- : t(isMobile ? 'chatInputPlaceholderCompact' : 'chatInputPlaceholder');
271
+ : placeholder ?? t(isMobile ? 'chatInputPlaceholderCompact' : 'chatInputPlaceholder');
281
272
  const selectedModelOption = modelRecords.find((option) => option.value === selectedModel);
282
273
  const thinkingSupportedLevels = selectedModelOption?.thinkingCapability?.supported ?? [];
283
274
  const { handleFilesAdd, handleFileInputChange } = useSessionConversationInputAttachments({
@@ -294,19 +285,18 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
294
285
  }
295
286
  void updateNcpSession(inputQuery.selectedSessionKey, patch).catch(() => undefined);
296
287
  }, [inputQuery.selectedSession, inputQuery.selectedSessionKey]);
297
- const handleNodesChange = useCallback((nodes: SessionConversationInputSnapshot['nodes']) => {
298
- const nextNodes = [...nodes];
299
- inputActions.update((current) => ({
300
- nodes: nextNodes,
301
- attachments: pruneComposerAttachments(nextNodes, current.attachments),
302
- text: deriveChatComposerDraft(nextNodes),
303
- selectedSkills: deriveSelectedSkillsFromComposer(nextNodes),
304
- sendError: null,
305
- }));
306
- }, [inputActions]);
288
+ const handleNodesChange = useSessionConversationComposerNodes(inputActions);
289
+
290
+ useChatComposerReferenceIntent({
291
+ inputBarRef,
292
+ intentManager: presenter.chatComposerIntentManager,
293
+ selectedSessionKey: inputQuery.selectedSessionKey,
294
+ });
295
+
307
296
  const handleModelChange = useCallback((value: string) => {
308
297
  const nextThinkingLevel = resolveThinkingForConversationModel(
309
- modelRecords.find((option) => option.value === value),
298
+ modelRecords.find((option) => option.value === value) ??
299
+ discoveredModelRecords.find((option) => option.value === value),
310
300
  selectedThinkingLevel,
311
301
  );
312
302
  inputActions.update({
@@ -326,11 +316,22 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
326
316
  });
327
317
  }, [
328
318
  inputActions,
319
+ discoveredModelRecords,
329
320
  inputQuery.sessionTypeState.selectedSessionType,
330
321
  modelRecords,
331
322
  selectedThinkingLevel,
332
323
  syncSessionPreferences,
333
324
  ]);
325
+ const handleDiscoveredModelSelect = useCallback(async (value: string) => {
326
+ const option = await inputQuery.addDiscoveredModel(value);
327
+ if (!option) {
328
+ toast.error(t('chatDiscoveredModelUnavailable'));
329
+ throw new Error('Discovered provider model is no longer available.');
330
+ }
331
+ toast.success(t('chatDiscoveredModelAdded').replace('{model}', option.modelLabel));
332
+ }, [
333
+ inputQuery,
334
+ ]);
334
335
  const handleThinkingChange = useCallback((value: ChatThinkingLevel | null) => {
335
336
  inputActions.setSelectedThinkingLevel(value);
336
337
  syncSessionPreferences({ preferredThinking: value });
@@ -370,10 +371,16 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
370
371
  favoriteModelsLabel: labels.favoriteModelsLabel,
371
372
  hasModelOptions,
372
373
  isModelOptionsLoading,
374
+ discoveredModelRecords,
373
375
  modelRecords,
374
376
  modelSearchEmptyLabel: labels.modelSearchEmptyLabel,
375
377
  modelSearchPlaceholder: labels.modelSearchPlaceholder,
376
378
  onFavoriteModelToggle: setModelFavorite,
379
+ onDiscoveredModelSelect: handleDiscoveredModelSelect,
380
+ onDiscoveredModelsDismiss: inputQuery.dismissDiscoveredModels,
381
+ onModelSelectOpen: () => {
382
+ void inputQuery.refreshProviderModelCatalog();
383
+ },
377
384
  onModelChange: handleModelChange,
378
385
  onThinkingChange: handleThinkingChange,
379
386
  recentModelValues,
@@ -401,14 +408,14 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
401
408
  <ChatInputBar
402
409
  ref={inputBarRef}
403
410
  surface={useReadingTrack ? 'embedded' : surface}
404
- sendError={inputSnapshot.sendError}
405
- sendErrorDetailsLabel={t('chatErrorDetails', language)}
406
411
  topSlot={controller.queuedInputs.length > 0
407
412
  ? <SessionQueuedInputRows controller={controller} />
408
413
  : null}
409
414
  composer={{
410
415
  nodes: composerNodes,
411
416
  placeholder: textareaPlaceholder,
417
+ excerptCharacterCountTemplate: t('chatWorkspaceExcerptCharacterCount'),
418
+ removeTokenLabel: t('chatInputRemoveReference'),
412
419
  disabled: inputDisabled,
413
420
  onNodesChange: handleNodesChange,
414
421
  onFilesAdd: handleFilesAdd,
@@ -417,7 +424,6 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
417
424
  }}
418
425
  inputSurface={inputSurfaceState.panel ?? undefined}
419
426
  hint={buildModelStateHint({
420
- isModelOptionsLoading,
421
427
  isModelOptionsEmpty,
422
428
  onGoToProviders: presenter.chatUiManager.goToProviders,
423
429
  texts: {
@@ -426,6 +432,7 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
426
432
  },
427
433
  })}
428
434
  toolbar={{
435
+ addMenuLabel: t('chatInputAdd'),
429
436
  selects: [],
430
437
  trailingSelects: toolbarSelects,
431
438
  accessories: [
@@ -433,7 +440,6 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
433
440
  key: 'attach',
434
441
  label: t('chatInputAttach'),
435
442
  icon: 'paperclip' as const,
436
- iconOnly: true,
437
443
  disabled: !attachmentSupported || inputDisabled,
438
444
  onClick: () => fileInputRef.current?.click(),
439
445
  },
@@ -445,7 +451,12 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
445
451
  sendDisabled: controller.sendDisabled,
446
452
  stopDisabled: controller.stopDisabled,
447
453
  stopHint: t('chatStopUnavailable'),
448
- sendButtonLabel: controller.isSending ? t('chatQueueSend') : t('chatSend'),
454
+ sendButtonLabel: controller.primaryAction === 'continue'
455
+ ? t('chatContinueRun')
456
+ : controller.isSending
457
+ ? t('chatQueueSend')
458
+ : t('chatSend'),
459
+ sendIcon: controller.primaryAction,
449
460
  stopButtonLabel: t('chatStop'),
450
461
  contextWindow,
451
462
  onSend: controller.send,
@@ -1,7 +1,7 @@
1
1
  import { act, renderHook, waitFor } from '@testing-library/react';
2
2
  import { describe, expect, it, vi } from 'vitest';
3
3
  import type { UiNcpSessionQueuedInputView } from '@nextclaw/client-sdk';
4
- import type { NcpAgentSendEnvelope, NcpRunHandle } from '@nextclaw/ncp';
4
+ import type { NcpAgentSendEnvelope, NcpMessage, NcpRunHandle } from '@nextclaw/ncp';
5
5
 
6
6
  import { useSessionConversationController } from '@/features/chat/features/conversation/hooks/use-session-conversation-controller';
7
7
 
@@ -54,12 +54,16 @@ function createControllerParams(params: {
54
54
  }) {
55
55
  const { isRunning, queuedInputs = [], send: requestedSend } = params;
56
56
  const send = requestedSend ?? createSendMock();
57
+ const continueRun = vi.fn(async () => createRunHandle());
58
+ const editMessage = vi.fn(async () => createRunHandle({ sessionId: 'edited-session' }));
57
59
  const removeQueuedInput = vi.fn(async (id: string) =>
58
60
  queuedInputs.find((item) => item.id === id) ?? null,
59
61
  );
60
62
  return {
61
63
  agent: {
62
64
  abort: vi.fn(),
65
+ continueRun,
66
+ editMessage,
63
67
  isHydrating: false,
64
68
  isRunning,
65
69
  isSending: false,
@@ -70,13 +74,18 @@ function createControllerParams(params: {
70
74
  visibleMessages: [],
71
75
  },
72
76
  inputQuery: {
77
+ addDiscoveredModel: vi.fn(async () => null),
73
78
  defaultModel: 'test-model',
74
79
  defaultProjectRoot: null,
80
+ dismissDiscoveredModels: vi.fn(),
81
+ discoveredModelOptions: [],
75
82
  fallbackPreferredModel: undefined,
76
83
  fallbackPreferredThinking: undefined,
77
84
  isProviderStateResolved: true,
78
85
  isSkillsLoading: false,
79
86
  modelOptions: [],
87
+ providersView: null,
88
+ refreshProviderModelCatalog: vi.fn(),
80
89
  selectedSession: null,
81
90
  selectedSessionKey: 'session-1',
82
91
  sessionTypeState: {
@@ -113,11 +122,43 @@ function createControllerParams(params: {
113
122
  sessionKey: 'session-1',
114
123
  resetComposer: vi.fn(),
115
124
  restoreComposer: vi.fn(),
125
+ onSessionMaterialized: vi.fn(),
116
126
  setSendError: vi.fn(),
117
127
  };
118
128
  }
119
129
 
120
130
  describe('useSessionConversationController backend run queue', () => {
131
+ it('preallocates one stable session identity for the first root message', async () => {
132
+ const send = vi.fn<TestAgentSend>(async (envelope) => {
133
+ if (!envelope.sessionId) {
134
+ throw new Error('Expected a preallocated session id.');
135
+ }
136
+ return createRunHandle({
137
+ sessionId: envelope.sessionId,
138
+ userMessageId: envelope.message.id,
139
+ });
140
+ });
141
+ const params = createControllerParams({ isRunning: false, send });
142
+ const draftParams = {
143
+ ...params,
144
+ inputQuery: {
145
+ ...params.inputQuery,
146
+ selectedSessionKey: null,
147
+ },
148
+ sessionKey: null,
149
+ } as unknown as Parameters<typeof useSessionConversationController>[0];
150
+ const { result } = renderHook(() => useSessionConversationController(draftParams));
151
+
152
+ await act(async () => {
153
+ await result.current.send();
154
+ });
155
+
156
+ const envelope = send.mock.calls[0]?.[0];
157
+ expect(envelope?.sessionId).toMatch(/^ncp-/);
158
+ expect(envelope?.message.sessionId).toBe(envelope?.sessionId);
159
+ expect(params.onSessionMaterialized).toHaveBeenCalledWith(envelope?.sessionId);
160
+ });
161
+
121
162
  it('submits immediately while the session is running so the backend can enqueue it', async () => {
122
163
  const send = createSendMock(createRunHandle({ runId: null }));
123
164
  const params = createControllerParams({ isRunning: true, send });
@@ -137,6 +178,29 @@ describe('useSessionConversationController backend run queue', () => {
137
178
  expect(params.resetComposer).toHaveBeenCalledTimes(1);
138
179
  });
139
180
 
181
+ it('keeps an existing session bound to its agent instead of the global draft selection', async () => {
182
+ const send = createSendMock();
183
+ const params = createControllerParams({ isRunning: false, send });
184
+ const controllerParams = {
185
+ ...params,
186
+ inputQuery: {
187
+ ...params.inputQuery,
188
+ selectedSession: { agentId: 'researcher' },
189
+ },
190
+ selectedAgentId: 'main',
191
+ } as unknown as Parameters<typeof useSessionConversationController>[0];
192
+ const { result } = renderHook(() => useSessionConversationController(controllerParams));
193
+
194
+ await act(async () => {
195
+ await result.current.send();
196
+ });
197
+
198
+ expect(send.mock.calls[0]?.[0].message.metadata).toMatchObject({
199
+ agentId: 'researcher',
200
+ agent_id: 'researcher',
201
+ });
202
+ });
203
+
140
204
  it('projects the backend queue and returns a removed item to the composer for editing', async () => {
141
205
  const queuedInput = createQueuedInput();
142
206
  const params = createControllerParams({ isRunning: true, queuedInputs: [queuedInput] });
@@ -170,4 +234,67 @@ describe('useSessionConversationController backend run queue', () => {
170
234
  await waitFor(() => expect(params.runQueue.removeQueuedInput).toHaveBeenCalledWith(queuedInput.id));
171
235
  expect(params.restoreComposer).not.toHaveBeenCalled();
172
236
  });
237
+
238
+ it('continues a cancelled session from the primary action when the composer is empty', async () => {
239
+ const params = createControllerParams({ isRunning: false });
240
+ params.inputSnapshot.nodes = [];
241
+ params.inputSnapshot.text = '';
242
+ const controllerParams = {
243
+ ...params,
244
+ inputQuery: {
245
+ ...params.inputQuery,
246
+ selectedSession: { activityPreview: { state: 'cancelled' } },
247
+ },
248
+ } as unknown as Parameters<typeof useSessionConversationController>[0];
249
+ const { result } = renderHook(() => useSessionConversationController(controllerParams));
250
+
251
+ expect(result.current.primaryAction).toBe('continue');
252
+ expect(result.current.canContinue).toBe(true);
253
+ await act(async () => {
254
+ await result.current.send();
255
+ });
256
+
257
+ expect(params.agent.continueRun).toHaveBeenCalledWith({ sessionId: 'session-1' });
258
+ expect(params.agent.send).not.toHaveBeenCalled();
259
+ expect(params.resetComposer).not.toHaveBeenCalled();
260
+ });
261
+
262
+ it('keeps continuation available but restores send as primary when the user types', () => {
263
+ const params = createControllerParams({ isRunning: false });
264
+ const controllerParams = {
265
+ ...params,
266
+ inputQuery: {
267
+ ...params.inputQuery,
268
+ selectedSession: { activityPreview: { state: 'failed' } },
269
+ },
270
+ } as unknown as Parameters<typeof useSessionConversationController>[0];
271
+ const { result } = renderHook(() => useSessionConversationController(controllerParams));
272
+
273
+ expect(result.current.canContinue).toBe(true);
274
+ expect(result.current.primaryAction).toBe('send');
275
+ });
276
+
277
+ it('edits inside the current session without materializing or navigating to another session', async () => {
278
+ const params = createControllerParams({ isRunning: false });
279
+ const message = {
280
+ id: 'edited-user-message',
281
+ sessionId: 'session-1',
282
+ role: 'user',
283
+ status: 'final',
284
+ timestamp: '2026-08-08T10:00:00.000Z',
285
+ parts: [{ type: 'text', text: 'updated request' }],
286
+ } satisfies NcpMessage;
287
+ const { result } = renderHook(() => useSessionConversationController(params));
288
+
289
+ await act(async () => {
290
+ await result.current.editMessage({ messageId: 'user-message-1', message });
291
+ });
292
+
293
+ expect(params.agent.editMessage).toHaveBeenCalledWith({
294
+ message,
295
+ messageId: 'user-message-1',
296
+ sessionId: 'session-1',
297
+ });
298
+ expect(params.onSessionMaterialized).not.toHaveBeenCalled();
299
+ });
173
300
  });
@@ -1,7 +1,9 @@
1
1
  import { act, renderHook } from '@testing-library/react';
2
- import { describe, expect, it } from 'vitest';
2
+ import { beforeEach, describe, expect, it } from 'vitest';
3
3
 
4
4
  import { useSessionConversationInputState } from '@/features/chat/features/conversation/hooks/use-session-conversation-input-state';
5
+ import { useChatComposerDraftStore } from '@/features/chat/stores/chat-composer-draft.store';
6
+ import { useChatThreadStore } from '@/features/chat/stores/chat-thread.store';
5
7
  import type { ChatModelOption } from '@/features/chat/types/chat-input.types';
6
8
 
7
9
  const MODEL_OPTIONS: ChatModelOption[] = [
@@ -20,6 +22,12 @@ const MODEL_OPTIONS: ChatModelOption[] = [
20
22
  ];
21
23
 
22
24
  describe('useSessionConversationInputState session preferences', () => {
25
+ beforeEach(() => {
26
+ window.localStorage.clear();
27
+ useChatComposerDraftStore.setState({ drafts: {} });
28
+ useChatThreadStore.getState().setSnapshot({ draftProjectRoot: null });
29
+ });
30
+
23
31
  it('owns the initial routed prompt before the composer first renders', () => {
24
32
  const { result } = renderHook(() => useSessionConversationInputState(' 每天整理项目风险 '));
25
33
 
@@ -101,4 +109,62 @@ describe('useSessionConversationInputState session preferences', () => {
101
109
  });
102
110
  expect(result.current.inputSnapshot.selectedModel).toBe('deepseek/deepseek-v4-flash');
103
111
  });
112
+
113
+ it('shares the selected draft project with the thread workspace owner', () => {
114
+ const { result } = renderHook(() => useSessionConversationInputState());
115
+
116
+ act(() => {
117
+ result.current.inputActions.setPendingProjectRoot('/tmp/project-alpha');
118
+ });
119
+
120
+ expect(result.current.inputSnapshot.pendingProjectRoot).toBe('/tmp/project-alpha');
121
+ expect(useChatThreadStore.getState().snapshot.draftProjectRoot).toBe('/tmp/project-alpha');
122
+ });
123
+
124
+ it('keeps composer drafts isolated by session and restores them when switching back', () => {
125
+ const { result, rerender } = renderHook(
126
+ ({ sessionKey }: { sessionKey: string | null }) =>
127
+ useSessionConversationInputState(null, sessionKey),
128
+ { initialProps: { sessionKey: 'session-a' as string | null } },
129
+ );
130
+
131
+ act(() => {
132
+ result.current.inputActions.syncComposer({
133
+ text: '会话 A 草稿',
134
+ nodes: [],
135
+ selectedSkills: [],
136
+ skillRecords: [],
137
+ });
138
+ });
139
+ rerender({ sessionKey: 'session-b' });
140
+ expect(result.current.inputSnapshot.text).toBe('');
141
+
142
+ act(() => {
143
+ result.current.inputActions.syncComposer({
144
+ text: '会话 B 草稿',
145
+ nodes: [],
146
+ selectedSkills: [],
147
+ skillRecords: [],
148
+ });
149
+ });
150
+ rerender({ sessionKey: null });
151
+ expect(result.current.inputSnapshot.text).toBe('');
152
+
153
+ act(() => {
154
+ result.current.inputActions.syncComposer({
155
+ text: '新会话草稿',
156
+ nodes: [],
157
+ selectedSkills: [],
158
+ skillRecords: [],
159
+ });
160
+ });
161
+ rerender({ sessionKey: 'session-a' });
162
+ expect(result.current.inputSnapshot.text).toBe('会话 A 草稿');
163
+
164
+ rerender({ sessionKey: 'session-b' });
165
+ expect(result.current.inputSnapshot.text).toBe('会话 B 草稿');
166
+
167
+ rerender({ sessionKey: null });
168
+ expect(result.current.inputSnapshot.text).toBe('新会话草稿');
169
+ });
104
170
  });
@@ -39,6 +39,10 @@ describe('useSessionConversationSlashCommands', () => {
39
39
  }));
40
40
  const command = result.current.find((entry) => entry.key === 'compact-context');
41
41
 
42
+ expect(result.current.map(({ key, icon }) => ({ key, icon }))).toEqual([
43
+ { key: 'side-chat', icon: 'message-square-plus' },
44
+ { key: 'compact-context', icon: 'list-collapse' },
45
+ ]);
42
46
  act(() => command?.onSelect());
43
47
 
44
48
  await waitFor(() => expect(compactNcpSessionContext).toHaveBeenCalledWith('session-1'));
@@ -0,0 +1,109 @@
1
+ import { act, renderHook } from '@testing-library/react';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+ import { useSessionProviderModelCatalog } from '@/features/chat/features/conversation/hooks/use-session-provider-model-catalog';
4
+
5
+ const mocks = vi.hoisted(() => ({
6
+ refetch: vi.fn(),
7
+ updateProvider: vi.fn(),
8
+ }));
9
+
10
+ vi.mock('@/shared/hooks/use-config', () => ({
11
+ useProviderModelCatalog: () => ({
12
+ data: {
13
+ refreshIntervalMs: 43_200_000,
14
+ refreshing: false,
15
+ lastRefreshStartedAt: '2026-08-07T00:00:00.000Z',
16
+ lastRefreshCompletedAt: '2026-08-07T00:00:01.000Z',
17
+ providers: {
18
+ opencode: {
19
+ providerId: 'opencode',
20
+ models: ['big-pickle', 'deepseek-v4-flash-free'],
21
+ source: 'catalog',
22
+ fetchedAt: '2026-08-07T00:00:00.500Z',
23
+ lastError: null,
24
+ },
25
+ },
26
+ },
27
+ refetch: mocks.refetch,
28
+ }),
29
+ useUpdateProvider: () => ({ mutateAsync: mocks.updateProvider }),
30
+ }));
31
+
32
+ const providersView = {
33
+ providers: {
34
+ opencode: {
35
+ providerId: 'opencode',
36
+ providerType: 'opencode',
37
+ isBuiltInType: true,
38
+ isCustom: false,
39
+ enabled: true,
40
+ displayName: 'OpenCode Zen Free Trial',
41
+ apiKeyRequired: false,
42
+ apiKeySet: false,
43
+ models: ['big-pickle'],
44
+ },
45
+ },
46
+ };
47
+
48
+ const templatesView = {
49
+ providerTemplates: [{
50
+ id: 'opencode',
51
+ providerType: 'opencode',
52
+ displayName: 'OpenCode Zen Free Trial',
53
+ modelPrefix: 'opencode',
54
+ keywords: [],
55
+ envKey: 'OPENCODE_API_KEY',
56
+ apiKeyRequired: false,
57
+ }],
58
+ };
59
+
60
+ describe('useSessionProviderModelCatalog', () => {
61
+ beforeEach(() => {
62
+ window.localStorage.clear();
63
+ mocks.refetch.mockReset();
64
+ mocks.updateProvider.mockReset().mockResolvedValue(providersView.providers.opencode);
65
+ });
66
+
67
+ it('adds exactly the selected discovered model through the existing provider update path', async () => {
68
+ const { result } = renderHook(() => useSessionProviderModelCatalog({
69
+ config: null,
70
+ providersView,
71
+ templatesView,
72
+ modelSelectionMode: 'nextclaw',
73
+ }));
74
+
75
+ expect(result.current.discoveredModelOptions.map((option) => option.value)).toEqual([
76
+ 'opencode/deepseek-v4-flash-free',
77
+ ]);
78
+ let addedModel: Awaited<ReturnType<typeof result.current.addDiscoveredModel>> = null;
79
+ await act(async () => {
80
+ addedModel = await result.current.addDiscoveredModel('opencode/deepseek-v4-flash-free');
81
+ });
82
+
83
+ expect(mocks.updateProvider).toHaveBeenCalledWith({
84
+ provider: 'opencode',
85
+ data: { models: ['big-pickle', 'deepseek-v4-flash-free'] },
86
+ silentSuccess: true,
87
+ });
88
+ expect(addedModel).toMatchObject({
89
+ value: 'opencode/deepseek-v4-flash-free',
90
+ providerId: 'opencode',
91
+ providerModel: 'deepseek-v4-flash-free',
92
+ });
93
+ expect(result.current.refreshProviderModelCatalog).toBe(mocks.refetch);
94
+ });
95
+
96
+ it('lets the user mark the current discovery batch as seen without adding it', () => {
97
+ const { result } = renderHook(() => useSessionProviderModelCatalog({
98
+ config: null,
99
+ providersView,
100
+ templatesView,
101
+ modelSelectionMode: 'nextclaw',
102
+ }));
103
+
104
+ expect(result.current.discoveredModelOptions).toHaveLength(1);
105
+ act(() => result.current.dismissDiscoveredModels());
106
+ expect(result.current.discoveredModelOptions).toEqual([]);
107
+ expect(mocks.updateProvider).not.toHaveBeenCalled();
108
+ });
109
+ });
@@ -0,0 +1,46 @@
1
+ import { useEffect, type RefObject } from 'react';
2
+ import { type ChatInputBarHandle } from '@nextclaw/agent-chat-ui';
3
+ import { CHAT_WORKSPACE_EXCERPT_TOKEN_KIND } from '@nextclaw/shared';
4
+
5
+ import type {
6
+ ChatComposerIntentManager,
7
+ ChatComposerReferenceIntent,
8
+ } from '@/features/chat/managers/chat-composer-intent.manager';
9
+
10
+ export function useChatComposerReferenceIntent(params: {
11
+ inputBarRef: RefObject<ChatInputBarHandle | null>;
12
+ intentManager: ChatComposerIntentManager;
13
+ selectedSessionKey: string | null | undefined;
14
+ }): void {
15
+ const { inputBarRef, intentManager, selectedSessionKey } = params;
16
+
17
+ useEffect(() => {
18
+ const targetSessionKey = selectedSessionKey?.trim() || null;
19
+ const applyIntent = (intent: ChatComposerReferenceIntent) => {
20
+ const inputBar = inputBarRef.current;
21
+ if (!inputBar) {
22
+ return;
23
+ }
24
+ inputBar.insertToken({
25
+ tokenKind: intent.kind,
26
+ tokenKey: intent.tokenKey,
27
+ label: intent.label,
28
+ data: intent.kind === CHAT_WORKSPACE_EXCERPT_TOKEN_KIND
29
+ ? {
30
+ path: intent.path,
31
+ excerpt: intent.excerpt,
32
+ startLine: intent.startLine,
33
+ endLine: intent.endLine,
34
+ }
35
+ : undefined,
36
+ });
37
+ intentManager.markConsumed(intent.id);
38
+ };
39
+ const unsubscribe = intentManager.subscribe(targetSessionKey, applyIntent);
40
+ const pendingIntent = intentManager.consumePending(targetSessionKey);
41
+ if (pendingIntent) {
42
+ applyIntent(pendingIntent);
43
+ }
44
+ return unsubscribe;
45
+ }, [inputBarRef, intentManager, selectedSessionKey]);
46
+ }