@nextclaw/ui 0.15.22 → 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 (423) hide show
  1. package/CHANGELOG.md +49 -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-BgBsUmp1.js → config-split-page-D5BgtKrm.js} +1 -1
  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-CgjYZUzM.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-CkKwPztT.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-BINMWate.js → save-DTOAvqzt.js} +1 -1
  123. package/dist/assets/{search-7M38azRp.js → search-D4X5DNMe.js} +1 -1
  124. package/dist/assets/search-config-page-Cr6254K5.js +1 -0
  125. package/dist/assets/{secrets-config-page-2mVejXf2.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-DqZstREV.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-BoDaLnWt.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 +9 -9
  150. package/src/app/components/layout/page-layout.tsx +15 -6
  151. package/src/app/styles/charcoal-theme.css +85 -0
  152. package/src/app/styles/design-system.css +11 -13
  153. package/src/features/agents/components/__tests__/agents-page.test.tsx +36 -24
  154. package/src/features/agents/components/agent-advanced-config-fields.tsx +7 -7
  155. package/src/features/agents/components/agent-details-dialog.tsx +8 -8
  156. package/src/features/agents/components/agent-dialogs.tsx +7 -7
  157. package/src/features/agents/components/agents-page.tsx +42 -62
  158. package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx +27 -3
  159. package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +16 -0
  160. package/src/features/chat/components/conversation/__tests__/chat-conversation-panel.test.tsx +8 -22
  161. package/src/features/chat/components/conversation/__tests__/chat-conversation-workspace-section.test.tsx +69 -3
  162. package/src/features/chat/components/conversation/chat-conversation-content.tsx +20 -6
  163. package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +30 -31
  164. package/src/features/chat/components/conversation/chat-conversation-panel.tsx +1 -8
  165. package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +16 -4
  166. package/src/features/chat/components/layout/__tests__/chat-page-shell.test.tsx +23 -0
  167. package/src/features/chat/components/layout/__tests__/chat-sidebar-toolbar.test.tsx +6 -0
  168. package/src/features/chat/components/layout/chat-page-shell.tsx +5 -4
  169. package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +4 -4
  170. package/src/features/chat/components/providers/chat-presenter.provider.tsx +2 -0
  171. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +84 -12
  172. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +243 -5
  173. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +48 -31
  174. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +46 -37
  175. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +128 -1
  176. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-input-state.test.tsx +67 -1
  177. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-slash-commands.test.tsx +4 -0
  178. package/src/features/chat/features/conversation/hooks/__tests__/use-session-provider-model-catalog.test.tsx +109 -0
  179. package/src/features/chat/features/conversation/hooks/use-chat-composer-reference-intent.ts +46 -0
  180. package/src/features/chat/features/conversation/hooks/use-session-conversation-composer-nodes.ts +26 -0
  181. package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +45 -12
  182. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-query.ts +28 -3
  183. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +91 -37
  184. package/src/features/chat/features/conversation/hooks/use-session-conversation-recovery-actions.ts +64 -0
  185. package/src/features/chat/features/conversation/hooks/use-session-conversation-slash-commands.ts +2 -0
  186. package/src/features/chat/features/conversation/hooks/use-session-provider-model-catalog.ts +84 -0
  187. package/src/features/chat/features/conversation/utils/__tests__/session-message-composer.utils.test.ts +34 -0
  188. package/src/features/chat/features/conversation/utils/session-conversation-input-toolbar.utils.ts +17 -0
  189. package/src/features/chat/features/conversation/utils/session-message-composer.utils.ts +117 -0
  190. package/src/features/chat/features/conversation/utils/session-queued-input.utils.ts +10 -98
  191. package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +2 -1
  192. package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +3 -1
  193. package/src/features/chat/features/input/utils/__tests__/chat-composer-state.utils.test.ts +22 -1
  194. package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +23 -0
  195. package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +102 -0
  196. package/src/features/chat/features/input/utils/chat-composer-state.utils.ts +27 -2
  197. package/src/features/chat/features/input/utils/chat-composer-token-protocol.utils.ts +5 -0
  198. package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +97 -32
  199. package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +1 -0
  200. package/src/features/chat/features/input/utils/chat-input-toolbar.utils.ts +61 -10
  201. package/src/features/chat/features/message/components/__tests__/chat-message-assistant-header.container.test.tsx +149 -0
  202. package/src/features/chat/features/message/components/__tests__/chat-message-inline-editor.test.tsx +59 -0
  203. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +123 -90
  204. package/src/features/chat/features/message/components/__tests__/chat-message-timeline-dividers.test.tsx +43 -0
  205. package/src/features/chat/features/message/components/chat-message-inline-editor.tsx +111 -0
  206. package/src/features/chat/features/message/components/chat-message-list.container.tsx +81 -172
  207. package/src/features/chat/features/message/components/chat-message-timeline-dividers.tsx +75 -0
  208. package/src/features/chat/features/message/hooks/use-chat-inline-token-actions.ts +125 -0
  209. package/src/features/chat/features/message/hooks/use-chat-message-actions.tsx +187 -0
  210. package/src/features/chat/features/message/utils/__tests__/chat-message-action-targets.utils.test.ts +89 -0
  211. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline-compaction.utils.test.ts +194 -0
  212. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline-continuation-compaction.utils.test.ts +167 -0
  213. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline.utils.test.ts +175 -1
  214. package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +26 -0
  215. package/src/features/chat/features/message/utils/chat-message-action-targets.utils.ts +28 -0
  216. package/src/features/chat/features/message/utils/chat-message-part.utils.ts +38 -0
  217. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +1 -0
  218. package/src/features/chat/features/message/utils/chat-message-timeline.utils.ts +201 -7
  219. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime.test.tsx +180 -1
  220. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-session-conversation.test.tsx +184 -10
  221. package/src/features/chat/features/ncp/hooks/use-hydrated-ncp-agent.integration.test.tsx +57 -0
  222. package/src/features/chat/features/ncp/hooks/use-ncp-session-conversation.ts +90 -1
  223. package/src/features/chat/features/ncp/utils/__tests__/ncp-chat-query-derived.utils.test.ts +110 -0
  224. package/src/features/chat/features/ncp/utils/ncp-chat-query-derived.utils.ts +65 -3
  225. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +17 -0
  226. package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +63 -54
  227. package/src/features/chat/features/session/hooks/__tests__/use-selected-session-context-window-indicator.test.tsx +87 -0
  228. package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts +19 -0
  229. package/src/features/chat/features/session/utils/chat-context-window-indicator.utils.ts +44 -5
  230. package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +10 -0
  231. package/src/features/chat/features/session/utils/ncp-session-context-metadata.utils.ts +42 -4
  232. package/src/features/chat/features/welcome/components/__tests__/chat-welcome.test.tsx +17 -2
  233. package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +8 -3
  234. package/src/features/chat/features/welcome/components/chat-welcome.tsx +2 -2
  235. package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-project-options.utils.test.ts +22 -6
  236. package/src/features/chat/features/welcome/utils/chat-welcome-project-options.utils.ts +13 -0
  237. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +72 -2
  238. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +21 -1
  239. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-text-selection.test.tsx +75 -0
  240. package/src/features/chat/features/workspace/components/__tests__/workspace-text-selection-menu.test.tsx +102 -0
  241. package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +145 -26
  242. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +42 -18
  243. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +50 -10
  244. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +48 -22
  245. package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +13 -2
  246. package/src/features/chat/features/workspace/components/workspace-text-selection-menu.tsx +223 -0
  247. package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +80 -0
  248. package/src/features/chat/features/workspace/utils/__tests__/workspace-text-excerpt.utils.test.ts +54 -0
  249. package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +47 -0
  250. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +16 -0
  251. package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +22 -1
  252. package/src/features/chat/features/workspace/utils/workspace-text-excerpt.utils.ts +53 -0
  253. package/src/features/chat/index.ts +1 -0
  254. package/src/features/chat/managers/__tests__/chat-composer-intent.manager.test.ts +106 -0
  255. package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +38 -2
  256. package/src/features/chat/managers/__tests__/chat-thread-workspace.manager.test.ts +93 -1
  257. package/src/features/chat/managers/__tests__/provider-model-catalog-notice.manager.test.ts +57 -0
  258. package/src/features/chat/managers/chat-composer-intent.manager.ts +145 -0
  259. package/src/features/chat/managers/chat-session-list.manager.ts +13 -4
  260. package/src/features/chat/managers/chat-thread.manager.ts +30 -11
  261. package/src/features/chat/managers/provider-model-catalog-notice.manager.ts +121 -0
  262. package/src/features/chat/presenters/chat.presenter.ts +3 -0
  263. package/src/features/chat/stores/__tests__/chat-composer-draft.store.test.ts +55 -0
  264. package/src/features/chat/stores/chat-composer-draft.store.ts +92 -0
  265. package/src/features/chat/stores/chat-thread.store.ts +2 -4
  266. package/src/features/chat/types/chat-input-bar.types.ts +5 -0
  267. package/src/features/chat/types/chat-input.types.ts +5 -0
  268. package/src/features/chat/types/chat-message.types.ts +5 -0
  269. package/src/features/cron/components/__tests__/cron-config.test.tsx +100 -19
  270. package/src/features/cron/components/cron-config.tsx +19 -33
  271. package/src/features/cron/components/cron-job-detail-dialog.tsx +42 -16
  272. package/src/features/cron/components/cron-job-row.tsx +18 -1
  273. package/src/features/inbox/components/inbox-delivery-content.test.tsx +8 -2
  274. package/src/features/inbox/components/inbox-delivery-content.tsx +1 -1
  275. package/src/features/inbox/components/inbox-reader-dialog.test.tsx +10 -4
  276. package/src/features/inbox/components/inbox-reader-dialog.tsx +37 -61
  277. package/src/features/inbox/pages/inbox-page.tsx +90 -98
  278. package/src/features/notifications/components/__tests__/app-notification-toast.test.tsx +28 -6
  279. package/src/features/notifications/components/app-notification-toast.tsx +54 -15
  280. package/src/features/notifications/managers/__tests__/app-notification.manager.test.ts +2 -0
  281. package/src/features/notifications/managers/app-notification.manager.ts +3 -0
  282. package/src/features/panel-apps/utils/panel-app-doc-browser.utils.tsx +1 -0
  283. package/src/features/pwa/managers/__tests__/pwa-shell-theme.manager.test.ts +9 -0
  284. package/src/features/pwa/managers/pwa-shell-theme.manager.ts +1 -0
  285. package/src/features/service-apps/components/__tests__/service-apps-panel.test.tsx +5 -4
  286. package/src/features/service-apps/components/service-app-list-item.tsx +470 -0
  287. package/src/features/service-apps/components/service-apps-panel.tsx +76 -490
  288. package/src/features/settings/components/config/__tests__/provider-models-section.test.tsx +239 -0
  289. package/src/features/settings/components/config/provider-form-detail-pane.tsx +307 -0
  290. package/src/features/settings/components/config/provider-form.tsx +149 -360
  291. package/src/features/settings/components/config/provider-model-list.tsx +293 -0
  292. package/src/features/settings/components/config/provider-model-suggestions-panel.tsx +151 -0
  293. package/src/features/settings/components/config/provider-models-section.tsx +178 -149
  294. package/src/features/settings/hooks/__tests__/use-provider-connectivity.test.tsx +98 -0
  295. package/src/features/settings/hooks/__tests__/use-provider-model-suggestions.test.tsx +107 -0
  296. package/src/features/settings/hooks/use-provider-connectivity.ts +132 -0
  297. package/src/features/settings/hooks/use-provider-model-suggestions.ts +51 -0
  298. package/src/features/settings/pages/__tests__/appearance-settings-page.test.tsx +2 -2
  299. package/src/features/settings/pages/__tests__/search-config-page.test.tsx +48 -0
  300. package/src/features/settings/pages/search-config-page.tsx +17 -8
  301. package/src/features/settings/utils/__tests__/provider-form-support.utils.test.ts +42 -0
  302. package/src/features/settings/utils/provider-form-context.utils.ts +2 -0
  303. package/src/features/settings/utils/provider-form-model.utils.ts +25 -0
  304. package/src/features/settings/utils/provider-form-support.utils.ts +21 -5
  305. package/src/index.css +1 -1
  306. package/src/platforms/desktop/types/desktop-update.types.ts +5 -0
  307. package/src/shared/components/actions/__tests__/navigation-link.test.tsx +14 -3
  308. package/src/shared/components/actions/navigation-link.tsx +35 -15
  309. package/src/shared/components/common/__tests__/agent-avatar.test.tsx +3 -2
  310. package/src/shared/components/common/agent-avatar.tsx +3 -3
  311. package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +66 -0
  312. package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +4 -1
  313. package/src/shared/components/doc-browser/doc-browser-renderer.types.ts +1 -0
  314. package/src/shared/components/doc-browser/doc-browser.tsx +9 -0
  315. package/src/shared/components/doc-browser/hooks/use-doc-browser-scroll-restoration.ts +126 -0
  316. package/src/shared/components/settings/settings-page.tsx +1 -1
  317. package/src/shared/components/ui/context-menu/__tests__/context-menu.test.tsx +103 -0
  318. package/src/shared/components/ui/context-menu/context-menu.tsx +263 -0
  319. package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +19 -6
  320. package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +28 -67
  321. package/src/shared/hooks/use-config.ts +33 -1
  322. package/src/shared/lib/api/__tests__/ncp-session-query-cache.utils.test.ts +26 -0
  323. package/src/shared/lib/api/ncp-session-query-cache.utils.ts +7 -10
  324. package/src/shared/lib/api/ncp-session.types.ts +6 -0
  325. package/src/shared/lib/api/types.ts +36 -1
  326. package/src/shared/lib/api/utils/config.utils.ts +14 -0
  327. package/src/shared/lib/cron/cron-job-view.utils.ts +4 -0
  328. package/src/shared/lib/host-capabilities/__tests__/host-capability.manager.test.ts +26 -0
  329. package/src/shared/lib/host-capabilities/host-capability.manager.ts +28 -0
  330. package/src/shared/lib/i18n/locales/en-US/chat.json +33 -4
  331. package/src/shared/lib/i18n/locales/en-US/core.json +26 -0
  332. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +1 -0
  333. package/src/shared/lib/i18n/locales/en-US/search.json +1 -0
  334. package/src/shared/lib/i18n/locales/zh-CN/chat.json +33 -4
  335. package/src/shared/lib/i18n/locales/zh-CN/core.json +26 -0
  336. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +1 -0
  337. package/src/shared/lib/i18n/locales/zh-CN/search.json +1 -0
  338. package/src/shared/lib/session-project/__tests__/workspace-file-breadcrumb.test.ts +28 -1
  339. package/src/shared/lib/session-project/workspace-file-breadcrumb.utils.ts +23 -0
  340. package/src/shared/lib/theme/index.test.ts +11 -0
  341. package/src/shared/lib/theme/index.ts +2 -0
  342. package/dist/assets/api-B1yHQYs-.js +0 -13
  343. package/dist/assets/app-presenter-provider-D3BuuOyZ.css +0 -1
  344. package/dist/assets/app-presenter-provider-uP9waOzv.js +0 -108
  345. package/dist/assets/appearance-settings-page-C9Zq3hP9.js +0 -1
  346. package/dist/assets/architecture-TIHT7OUA-Cii4JyA2.js +0 -1
  347. package/dist/assets/book-open-oSmyVXea.js +0 -1
  348. package/dist/assets/card-CGvJZTZp.js +0 -1
  349. package/dist/assets/channel-BSKkTPe8.js +0 -1
  350. package/dist/assets/channels-list-page-5BTxpQHw.js +0 -8
  351. package/dist/assets/chat-page-jTvli8Jp.js +0 -1
  352. package/dist/assets/chunk-2Q5K7J3B-CDMOR1xZ.js +0 -1
  353. package/dist/assets/chunk-VAUOI2AC-BRvqNW76.js +0 -1
  354. package/dist/assets/chunk-XXDRQBXY-CwX_mqJ3.js +0 -1
  355. package/dist/assets/classDiagram-OUVF2IWQ-RzbGxG77.js +0 -1
  356. package/dist/assets/classDiagram-v2-EOCWNBFH-C_gWB7WT.js +0 -1
  357. package/dist/assets/confirm-dialog-h849QRWO.js +0 -5
  358. package/dist/assets/cynefin-VYW2F7L2-fSLIS0X3.js +0 -1
  359. package/dist/assets/desktop-update-config-BGGHxClR.js +0 -1
  360. package/dist/assets/dist-CxUhurq9.js +0 -41
  361. package/dist/assets/doc-browser-0eL8AX42.js +0 -1
  362. package/dist/assets/doc-browser-DEqE9lsh.js +0 -1
  363. package/dist/assets/doc-browser-context-DkzFIUoK.js +0 -1
  364. package/dist/assets/eventmodeling-45OFAUF4-XVcQGgha.js +0 -1
  365. package/dist/assets/eye-gOWQGt8N.js +0 -1
  366. package/dist/assets/flowDiagram-23GEKE2U-BiFSctqz.js +0 -1
  367. package/dist/assets/gitGraph-TEB2WS4Q-C014uXqS.js +0 -1
  368. package/dist/assets/host-capabilities-B-TtABsP.js +0 -1
  369. package/dist/assets/index-DSAUWCfl.js +0 -2
  370. package/dist/assets/index-G7cBryJ4.css +0 -1
  371. package/dist/assets/info-DKCQHKI2-CQ2qjGhU.js +0 -1
  372. package/dist/assets/loader-circle-BAuycGf6.js +0 -1
  373. package/dist/assets/logo-badge-BNcsDsUM.js +0 -1
  374. package/dist/assets/mcp-marketplace-page-BX39MtPa.js +0 -9
  375. package/dist/assets/mcp-marketplace-page-CkDtOEA4.js +0 -1
  376. package/dist/assets/model-config-page-7MIXg2fQ.js +0 -1
  377. package/dist/assets/navigation-link-B3iFOdxx.js +0 -1
  378. package/dist/assets/packet-7NZHBO7P-6IFyydij.js +0 -1
  379. package/dist/assets/pie-RZYD4A2V-spXQC_wr.js +0 -1
  380. package/dist/assets/plus-yvhFgfal.js +0 -1
  381. package/dist/assets/popover-Cpep_rJe.js +0 -1
  382. package/dist/assets/provider-models-CUrptZ-o.js +0 -1
  383. package/dist/assets/provider-scoped-model-input-CHmOzz7x.js +0 -1
  384. package/dist/assets/providers-config-page-A6J7Y0II.js +0 -1
  385. package/dist/assets/radar-I7S5WNFK-DWVA8lvY.js +0 -1
  386. package/dist/assets/railroad-3IZDKUUU-Ce51u8y1.js +0 -1
  387. package/dist/assets/railroad-abnf-AHOZXSZD-D7i7cqI_.js +0 -1
  388. package/dist/assets/railroad-ebnf-EBAXGLYW-BEg2hcPs.js +0 -1
  389. package/dist/assets/railroad-peg-LSFZ7HO6-CxROSzbi.js +0 -1
  390. package/dist/assets/react-BCJGCjrP.js +0 -8
  391. package/dist/assets/remote-FD-ZfSIn.js +0 -1
  392. package/dist/assets/rotate-cw-DrJMfHhp.js +0 -1
  393. package/dist/assets/runtime-config-page-DwjO-Vs6.js +0 -1
  394. package/dist/assets/search-config-page-T6tMhXbP.js +0 -1
  395. package/dist/assets/security-config-DD5RrjDe.js +0 -1
  396. package/dist/assets/select-BvidP5Oa.js +0 -1
  397. package/dist/assets/skeleton-BEQW2Dva.js +0 -1
  398. package/dist/assets/stateDiagram-v2-6OUMAXLB-Bui4RXQK.js +0 -1
  399. package/dist/assets/swimlanesDiagram-G3AALYLV-DTwUsCBz.js +0 -8
  400. package/dist/assets/tabs-BeLxVmkt.js +0 -1
  401. package/dist/assets/trash-2-CYUskMMN.js +0 -1
  402. package/dist/assets/treeView-QDETBFTQ-D5zsTiuL.js +0 -1
  403. package/dist/assets/treemap-6X3UGDF4-CR3dxqNs.js +0 -1
  404. package/dist/assets/use-config-CxKRXXCl.js +0 -1
  405. package/dist/assets/use-confirm-dialog-HASTvOP2.js +0 -1
  406. package/dist/assets/use-viewport-layout-CJxkjoRz.js +0 -1
  407. package/dist/assets/wardley-OPB4EBWU-LatRT_Lx.js +0 -1
  408. package/dist/assets/x-DdUwjre5.js +0 -1
  409. /package/dist/assets/{array-CAkJT3no.js → array-NADKd9aU.js} +0 -0
  410. /package/dist/assets/{chunk-KEIR6QF5-BgdDoWEH.js → chunk-KEIR6QF5-BuyuzJJL.js} +0 -0
  411. /package/dist/assets/{chunk-Y2CYZVJY-pTllD0P3.js → chunk-Y2CYZVJY-D_B2ZRNt.js} +0 -0
  412. /package/dist/assets/{config-hints-S3g-UVZb.js → config-hints-BcfVUMij.js} +0 -0
  413. /package/dist/assets/{cytoscape.esm-DmURt-5G.js → cytoscape.esm-BANTlgza.js} +0 -0
  414. /package/dist/assets/{defaultLocale-BKy_KP3O.js → defaultLocale-C4_L0aEs.js} +0 -0
  415. /package/dist/assets/{dist-IgNNB7K9.js → dist-DAPEIJbz.js} +0 -0
  416. /package/dist/assets/{graphlib-xw_TBHyH.js → graphlib-ChqXg0dI.js} +0 -0
  417. /package/dist/assets/{init-C9xlLIAu.js → init-D8wZg9xq.js} +0 -0
  418. /package/dist/assets/{jszip.min-Das2IAnb.js → jszip.min-CkbBfwAQ.js} +0 -0
  419. /package/dist/assets/{katex-DSy1eD2d.js → katex-TZJFa0J_.js} +0 -0
  420. /package/dist/assets/{middleware-Df-a4x-q.js → middleware-B3k-TSmZ.js} +0 -0
  421. /package/dist/assets/{path-DB0USYHi.js → path-DodfxcX7.js} +0 -0
  422. /package/dist/assets/{rough.esm-B1B52ZQS.js → rough.esm-CUaa1Rbe.js} +0 -0
  423. /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,
@@ -227,6 +212,10 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
227
212
  skillGroupLabels: labels.skillGroupLabels,
228
213
  skillScopeLabels: labels.skillScopeLabels,
229
214
  });
215
+ const discoveredModelRecords = useMemo(
216
+ () => toChatModelRecords(inputQuery.discoveredModelOptions),
217
+ [inputQuery.discoveredModelOptions],
218
+ );
230
219
  const slashCommands = useSessionConversationSlashCommands({
231
220
  language,
232
221
  onContextCompactingChange,
@@ -296,19 +285,18 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
296
285
  }
297
286
  void updateNcpSession(inputQuery.selectedSessionKey, patch).catch(() => undefined);
298
287
  }, [inputQuery.selectedSession, inputQuery.selectedSessionKey]);
299
- const handleNodesChange = useCallback((nodes: SessionConversationInputSnapshot['nodes']) => {
300
- const nextNodes = [...nodes];
301
- inputActions.update((current) => ({
302
- nodes: nextNodes,
303
- attachments: pruneComposerAttachments(nextNodes, current.attachments),
304
- text: deriveChatComposerDraft(nextNodes),
305
- selectedSkills: deriveSelectedSkillsFromComposer(nextNodes),
306
- sendError: null,
307
- }));
308
- }, [inputActions]);
288
+ const handleNodesChange = useSessionConversationComposerNodes(inputActions);
289
+
290
+ useChatComposerReferenceIntent({
291
+ inputBarRef,
292
+ intentManager: presenter.chatComposerIntentManager,
293
+ selectedSessionKey: inputQuery.selectedSessionKey,
294
+ });
295
+
309
296
  const handleModelChange = useCallback((value: string) => {
310
297
  const nextThinkingLevel = resolveThinkingForConversationModel(
311
- modelRecords.find((option) => option.value === value),
298
+ modelRecords.find((option) => option.value === value) ??
299
+ discoveredModelRecords.find((option) => option.value === value),
312
300
  selectedThinkingLevel,
313
301
  );
314
302
  inputActions.update({
@@ -328,11 +316,22 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
328
316
  });
329
317
  }, [
330
318
  inputActions,
319
+ discoveredModelRecords,
331
320
  inputQuery.sessionTypeState.selectedSessionType,
332
321
  modelRecords,
333
322
  selectedThinkingLevel,
334
323
  syncSessionPreferences,
335
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
+ ]);
336
335
  const handleThinkingChange = useCallback((value: ChatThinkingLevel | null) => {
337
336
  inputActions.setSelectedThinkingLevel(value);
338
337
  syncSessionPreferences({ preferredThinking: value });
@@ -372,10 +371,16 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
372
371
  favoriteModelsLabel: labels.favoriteModelsLabel,
373
372
  hasModelOptions,
374
373
  isModelOptionsLoading,
374
+ discoveredModelRecords,
375
375
  modelRecords,
376
376
  modelSearchEmptyLabel: labels.modelSearchEmptyLabel,
377
377
  modelSearchPlaceholder: labels.modelSearchPlaceholder,
378
378
  onFavoriteModelToggle: setModelFavorite,
379
+ onDiscoveredModelSelect: handleDiscoveredModelSelect,
380
+ onDiscoveredModelsDismiss: inputQuery.dismissDiscoveredModels,
381
+ onModelSelectOpen: () => {
382
+ void inputQuery.refreshProviderModelCatalog();
383
+ },
379
384
  onModelChange: handleModelChange,
380
385
  onThinkingChange: handleThinkingChange,
381
386
  recentModelValues,
@@ -403,14 +408,14 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
403
408
  <ChatInputBar
404
409
  ref={inputBarRef}
405
410
  surface={useReadingTrack ? 'embedded' : surface}
406
- sendError={inputSnapshot.sendError}
407
- sendErrorDetailsLabel={t('chatErrorDetails', language)}
408
411
  topSlot={controller.queuedInputs.length > 0
409
412
  ? <SessionQueuedInputRows controller={controller} />
410
413
  : null}
411
414
  composer={{
412
415
  nodes: composerNodes,
413
416
  placeholder: textareaPlaceholder,
417
+ excerptCharacterCountTemplate: t('chatWorkspaceExcerptCharacterCount'),
418
+ removeTokenLabel: t('chatInputRemoveReference'),
414
419
  disabled: inputDisabled,
415
420
  onNodesChange: handleNodesChange,
416
421
  onFilesAdd: handleFilesAdd,
@@ -419,7 +424,6 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
419
424
  }}
420
425
  inputSurface={inputSurfaceState.panel ?? undefined}
421
426
  hint={buildModelStateHint({
422
- isModelOptionsLoading,
423
427
  isModelOptionsEmpty,
424
428
  onGoToProviders: presenter.chatUiManager.goToProviders,
425
429
  texts: {
@@ -447,7 +451,12 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
447
451
  sendDisabled: controller.sendDisabled,
448
452
  stopDisabled: controller.stopDisabled,
449
453
  stopHint: t('chatStopUnavailable'),
450
- 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,
451
460
  stopButtonLabel: t('chatStop'),
452
461
  contextWindow,
453
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
+ }
@@ -0,0 +1,26 @@
1
+ import { useCallback } from 'react';
2
+
3
+ import type {
4
+ SessionConversationInputActions,
5
+ SessionConversationInputSnapshot,
6
+ } from '@/features/chat/features/conversation/hooks/use-session-conversation-input-state';
7
+ import {
8
+ deriveChatComposerDraft,
9
+ deriveSelectedSkillsFromComposer,
10
+ pruneComposerAttachments,
11
+ } from '@/features/chat/features/input/utils/chat-composer-state.utils';
12
+
13
+ export function useSessionConversationComposerNodes(
14
+ inputActions: SessionConversationInputActions,
15
+ ) {
16
+ return useCallback((nodes: SessionConversationInputSnapshot['nodes']) => {
17
+ const nextNodes = [...nodes];
18
+ inputActions.update((current) => ({
19
+ nodes: nextNodes,
20
+ attachments: pruneComposerAttachments(nextNodes, current.attachments),
21
+ text: deriveChatComposerDraft(nextNodes),
22
+ selectedSkills: deriveSelectedSkillsFromComposer(nextNodes),
23
+ sendError: null,
24
+ }));
25
+ }, [inputActions]);
26
+ }