@nextclaw/ui 0.15.22 → 0.15.24

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 (428) hide show
  1. package/CHANGELOG.md +66 -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-BxPNDp30.js +13 -0
  5. package/dist/assets/appearance-settings-page-DpVJjvkB.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-DRaNWAxC.js +8 -0
  13. package/dist/assets/chat-page-BuFDmG5_.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-BVWVDbvG.js +1 -0
  55. package/dist/assets/confirm-dialog-mA2iVzxh.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-DyQb1LXr.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-B4Pf5X3Q.js +1 -0
  68. package/dist/assets/dist-DHEWkGWX.js +1 -0
  69. package/dist/assets/doc-browser-CtO_HtRy.js +1 -0
  70. package/dist/assets/doc-browser-context-CuGI6PQc.js +1 -0
  71. package/dist/assets/doc-browser-eMUagyBV.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-BOfIyG3O.js +1 -0
  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-BjBdWHV1.js +107 -0
  83. package/dist/assets/index-DmpmjAGQ.css +1 -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-Df8wF2Pr.js +1 -0
  92. package/dist/assets/{map-BAAdHTqR.js → map-CcZzEoz6.js} +1 -1
  93. package/dist/assets/mcp-marketplace-page-DI49IKxp.js +1 -0
  94. package/dist/assets/mcp-marketplace-page-ofDqbOIF.js +9 -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-D73Tu3jW.js +1 -0
  99. package/dist/assets/navigation-link-CAFOcXNg.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-DVU5RLCS.js +1 -0
  106. package/dist/assets/provider-scoped-model-input-BaJJJYMX.js +1 -0
  107. package/dist/assets/providers-config-page-BL6IRVBm.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-BC_orpAj.js +8 -0
  116. package/dist/assets/{refresh-cw-CkKwPztT.js → refresh-cw-CWDp_JnN.js} +1 -1
  117. package/dist/assets/remote-T24O58lH.js +1 -0
  118. package/dist/assets/{requirementDiagram-TGXJPOKE-D8YV0-Jz.js → requirementDiagram-TGXJPOKE-CyVUzEX5.js} +1 -1
  119. package/dist/assets/rotate-cw-BqhwSV5p.js +1 -0
  120. package/dist/assets/runtime-config-page-CXUx017t.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-Cnr71JON.js} +1 -1
  123. package/dist/assets/{search-7M38azRp.js → search-CCX11X2m.js} +1 -1
  124. package/dist/assets/search-config-page-BB6d5gT7.js +1 -0
  125. package/dist/assets/{secrets-config-page-2mVejXf2.js → secrets-config-page-DYsDeT9K.js} +2 -2
  126. package/dist/assets/security-config-CVPgqL7G.js +1 -0
  127. package/dist/assets/select-DvwBu99K.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-Dmv6zMne.js} +1 -1
  130. package/dist/assets/{sizeCapture-X5ZJPWSS-9UIfya9-.js → sizeCapture-X5ZJPWSS-SxG_YnMA.js} +1 -1
  131. package/dist/assets/skeleton-C5tWata3.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-gMPvx7n_.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-D_3-QpZU.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-BEWSqeux.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 +161 -0
  179. package/src/features/chat/features/conversation/hooks/use-chat-composer-reference-intent.ts +55 -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 +159 -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 +23 -1
  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 +52 -0
  195. package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +111 -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 +10 -0
  198. package/src/features/chat/features/input/utils/chat-conversation-excerpt-token.utils.ts +59 -0
  199. package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +123 -32
  200. package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +1 -0
  201. package/src/features/chat/features/input/utils/chat-input-toolbar.utils.ts +97 -10
  202. package/src/features/chat/features/message/components/__tests__/chat-message-assistant-header.container.test.tsx +149 -0
  203. package/src/features/chat/features/message/components/__tests__/chat-message-inline-editor.test.tsx +59 -0
  204. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +123 -90
  205. package/src/features/chat/features/message/components/__tests__/chat-message-timeline-dividers.test.tsx +43 -0
  206. package/src/features/chat/features/message/components/chat-message-inline-editor.tsx +111 -0
  207. package/src/features/chat/features/message/components/chat-message-list.container.tsx +120 -171
  208. package/src/features/chat/features/message/components/chat-message-timeline-dividers.tsx +75 -0
  209. package/src/features/chat/features/message/hooks/use-chat-inline-token-actions.ts +125 -0
  210. package/src/features/chat/features/message/hooks/use-chat-message-actions.tsx +187 -0
  211. package/src/features/chat/features/message/utils/__tests__/chat-message-action-targets.utils.test.ts +89 -0
  212. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline-compaction.utils.test.ts +194 -0
  213. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline-continuation-compaction.utils.test.ts +167 -0
  214. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline.utils.test.ts +175 -1
  215. package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +26 -0
  216. package/src/features/chat/features/message/utils/chat-message-action-targets.utils.ts +28 -0
  217. package/src/features/chat/features/message/utils/chat-message-part.utils.ts +38 -0
  218. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +6 -0
  219. package/src/features/chat/features/message/utils/chat-message-timeline.utils.ts +201 -7
  220. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime.test.tsx +180 -1
  221. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-session-conversation.test.tsx +184 -10
  222. package/src/features/chat/features/ncp/hooks/use-hydrated-ncp-agent.integration.test.tsx +57 -0
  223. package/src/features/chat/features/ncp/hooks/use-ncp-session-conversation.ts +90 -1
  224. package/src/features/chat/features/ncp/utils/__tests__/ncp-chat-query-derived.utils.test.ts +110 -0
  225. package/src/features/chat/features/ncp/utils/ncp-chat-query-derived.utils.ts +65 -3
  226. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +17 -0
  227. package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +63 -54
  228. package/src/features/chat/features/session/hooks/__tests__/use-selected-session-context-window-indicator.test.tsx +87 -0
  229. package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts +19 -0
  230. package/src/features/chat/features/session/utils/chat-context-window-indicator.utils.ts +44 -5
  231. package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +10 -0
  232. package/src/features/chat/features/session/utils/ncp-session-context-metadata.utils.ts +42 -4
  233. package/src/features/chat/features/welcome/components/__tests__/chat-welcome.test.tsx +17 -2
  234. package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +8 -3
  235. package/src/features/chat/features/welcome/components/chat-welcome.tsx +2 -2
  236. package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-project-options.utils.test.ts +22 -6
  237. package/src/features/chat/features/welcome/utils/chat-welcome-project-options.utils.ts +13 -0
  238. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +72 -2
  239. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +21 -1
  240. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-text-selection.test.tsx +75 -0
  241. package/src/features/chat/features/workspace/components/__tests__/workspace-text-selection-menu.test.tsx +109 -0
  242. package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +145 -26
  243. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +42 -18
  244. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +50 -10
  245. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +48 -22
  246. package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +13 -2
  247. package/src/features/chat/features/workspace/components/workspace-text-selection-menu.tsx +45 -0
  248. package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +80 -0
  249. package/src/features/chat/features/workspace/utils/__tests__/workspace-text-excerpt.utils.test.ts +54 -0
  250. package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +47 -0
  251. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +16 -0
  252. package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +22 -1
  253. package/src/features/chat/features/workspace/utils/workspace-text-excerpt.utils.ts +53 -0
  254. package/src/features/chat/index.ts +1 -0
  255. package/src/features/chat/managers/__tests__/chat-composer-intent.manager.test.ts +129 -0
  256. package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +38 -2
  257. package/src/features/chat/managers/__tests__/chat-thread-workspace.manager.test.ts +93 -1
  258. package/src/features/chat/managers/__tests__/provider-model-catalog-notice.manager.test.ts +57 -0
  259. package/src/features/chat/managers/chat-composer-intent.manager.ts +190 -0
  260. package/src/features/chat/managers/chat-session-list.manager.ts +13 -4
  261. package/src/features/chat/managers/chat-thread.manager.ts +30 -11
  262. package/src/features/chat/managers/provider-model-catalog-notice.manager.ts +121 -0
  263. package/src/features/chat/presenters/chat.presenter.ts +3 -0
  264. package/src/features/chat/stores/__tests__/chat-composer-draft.store.test.ts +55 -0
  265. package/src/features/chat/stores/chat-composer-draft.store.ts +92 -0
  266. package/src/features/chat/stores/chat-thread.store.ts +2 -4
  267. package/src/features/chat/types/chat-input-bar.types.ts +8 -0
  268. package/src/features/chat/types/chat-input.types.ts +5 -0
  269. package/src/features/chat/types/chat-message.types.ts +5 -0
  270. package/src/features/cron/components/__tests__/cron-config.test.tsx +100 -19
  271. package/src/features/cron/components/cron-config.tsx +19 -33
  272. package/src/features/cron/components/cron-job-detail-dialog.tsx +42 -16
  273. package/src/features/cron/components/cron-job-row.tsx +18 -1
  274. package/src/features/inbox/components/inbox-delivery-content.test.tsx +8 -2
  275. package/src/features/inbox/components/inbox-delivery-content.tsx +1 -1
  276. package/src/features/inbox/components/inbox-reader-dialog.test.tsx +10 -4
  277. package/src/features/inbox/components/inbox-reader-dialog.tsx +37 -61
  278. package/src/features/inbox/pages/inbox-page.tsx +90 -98
  279. package/src/features/notifications/components/__tests__/app-notification-toast.test.tsx +28 -6
  280. package/src/features/notifications/components/app-notification-toast.tsx +54 -15
  281. package/src/features/notifications/managers/__tests__/app-notification.manager.test.ts +2 -0
  282. package/src/features/notifications/managers/app-notification.manager.ts +3 -0
  283. package/src/features/panel-apps/utils/panel-app-doc-browser.utils.tsx +1 -0
  284. package/src/features/pwa/managers/__tests__/pwa-shell-theme.manager.test.ts +9 -0
  285. package/src/features/pwa/managers/pwa-shell-theme.manager.ts +1 -0
  286. package/src/features/service-apps/components/__tests__/service-apps-panel.test.tsx +5 -4
  287. package/src/features/service-apps/components/service-app-list-item.tsx +470 -0
  288. package/src/features/service-apps/components/service-apps-panel.tsx +76 -490
  289. package/src/features/settings/components/config/__tests__/provider-models-section.test.tsx +239 -0
  290. package/src/features/settings/components/config/provider-form-detail-pane.tsx +307 -0
  291. package/src/features/settings/components/config/provider-form.tsx +149 -360
  292. package/src/features/settings/components/config/provider-model-list.tsx +293 -0
  293. package/src/features/settings/components/config/provider-model-suggestions-panel.tsx +151 -0
  294. package/src/features/settings/components/config/provider-models-section.tsx +178 -149
  295. package/src/features/settings/hooks/__tests__/use-provider-connectivity.test.tsx +98 -0
  296. package/src/features/settings/hooks/__tests__/use-provider-model-suggestions.test.tsx +107 -0
  297. package/src/features/settings/hooks/use-provider-connectivity.ts +132 -0
  298. package/src/features/settings/hooks/use-provider-model-suggestions.ts +51 -0
  299. package/src/features/settings/pages/__tests__/appearance-settings-page.test.tsx +2 -2
  300. package/src/features/settings/pages/__tests__/search-config-page.test.tsx +48 -0
  301. package/src/features/settings/pages/search-config-page.tsx +17 -8
  302. package/src/features/settings/utils/__tests__/provider-form-support.utils.test.ts +42 -0
  303. package/src/features/settings/utils/provider-form-context.utils.ts +2 -0
  304. package/src/features/settings/utils/provider-form-model.utils.ts +25 -0
  305. package/src/features/settings/utils/provider-form-support.utils.ts +21 -5
  306. package/src/features/side-dock/components/__tests__/side-dock.test.tsx +1 -0
  307. package/src/features/side-dock/configs/side-dock-built-in-items.config.ts +0 -9
  308. package/src/index.css +1 -1
  309. package/src/platforms/desktop/types/desktop-update.types.ts +5 -0
  310. package/src/shared/components/actions/__tests__/navigation-link.test.tsx +14 -3
  311. package/src/shared/components/actions/navigation-link.tsx +35 -15
  312. package/src/shared/components/common/__tests__/agent-avatar.test.tsx +3 -2
  313. package/src/shared/components/common/agent-avatar.tsx +3 -3
  314. package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +66 -0
  315. package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +4 -1
  316. package/src/shared/components/doc-browser/doc-browser-renderer.types.ts +1 -0
  317. package/src/shared/components/doc-browser/doc-browser.tsx +9 -0
  318. package/src/shared/components/doc-browser/hooks/use-doc-browser-scroll-restoration.ts +126 -0
  319. package/src/shared/components/settings/settings-page.tsx +1 -1
  320. package/src/shared/components/ui/context-menu/__tests__/context-menu.test.tsx +103 -0
  321. package/src/shared/components/ui/context-menu/context-menu.tsx +263 -0
  322. package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +19 -6
  323. package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +28 -67
  324. package/src/shared/hooks/use-config.ts +33 -1
  325. package/src/shared/lib/api/__tests__/ncp-session-query-cache.utils.test.ts +26 -0
  326. package/src/shared/lib/api/ncp-session-query-cache.utils.ts +7 -10
  327. package/src/shared/lib/api/ncp-session.types.ts +6 -0
  328. package/src/shared/lib/api/types.ts +36 -1
  329. package/src/shared/lib/api/utils/config.utils.ts +14 -0
  330. package/src/shared/lib/cron/cron-job-view.utils.ts +4 -0
  331. package/src/shared/lib/host-capabilities/__tests__/host-capability.manager.test.ts +26 -0
  332. package/src/shared/lib/host-capabilities/host-capability.manager.ts +28 -0
  333. package/src/shared/lib/i18n/locales/en-US/chat.json +39 -4
  334. package/src/shared/lib/i18n/locales/en-US/core.json +26 -0
  335. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +1 -0
  336. package/src/shared/lib/i18n/locales/en-US/search.json +1 -0
  337. package/src/shared/lib/i18n/locales/zh-CN/chat.json +39 -4
  338. package/src/shared/lib/i18n/locales/zh-CN/core.json +26 -0
  339. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +1 -0
  340. package/src/shared/lib/i18n/locales/zh-CN/search.json +1 -0
  341. package/src/shared/lib/session-project/__tests__/workspace-file-breadcrumb.test.ts +28 -1
  342. package/src/shared/lib/session-project/workspace-file-breadcrumb.utils.ts +23 -0
  343. package/src/shared/lib/theme/index.test.ts +11 -0
  344. package/src/shared/lib/theme/index.ts +2 -0
  345. package/dist/assets/api-B1yHQYs-.js +0 -13
  346. package/dist/assets/app-presenter-provider-D3BuuOyZ.css +0 -1
  347. package/dist/assets/app-presenter-provider-uP9waOzv.js +0 -108
  348. package/dist/assets/appearance-settings-page-C9Zq3hP9.js +0 -1
  349. package/dist/assets/architecture-TIHT7OUA-Cii4JyA2.js +0 -1
  350. package/dist/assets/book-open-oSmyVXea.js +0 -1
  351. package/dist/assets/card-CGvJZTZp.js +0 -1
  352. package/dist/assets/channel-BSKkTPe8.js +0 -1
  353. package/dist/assets/channels-list-page-5BTxpQHw.js +0 -8
  354. package/dist/assets/chat-page-jTvli8Jp.js +0 -1
  355. package/dist/assets/chunk-2Q5K7J3B-CDMOR1xZ.js +0 -1
  356. package/dist/assets/chunk-VAUOI2AC-BRvqNW76.js +0 -1
  357. package/dist/assets/chunk-XXDRQBXY-CwX_mqJ3.js +0 -1
  358. package/dist/assets/classDiagram-OUVF2IWQ-RzbGxG77.js +0 -1
  359. package/dist/assets/classDiagram-v2-EOCWNBFH-C_gWB7WT.js +0 -1
  360. package/dist/assets/config-split-page-BgBsUmp1.js +0 -1
  361. package/dist/assets/confirm-dialog-h849QRWO.js +0 -5
  362. package/dist/assets/cynefin-VYW2F7L2-fSLIS0X3.js +0 -1
  363. package/dist/assets/desktop-update-config-BGGHxClR.js +0 -1
  364. package/dist/assets/dist-CxUhurq9.js +0 -41
  365. package/dist/assets/doc-browser-0eL8AX42.js +0 -1
  366. package/dist/assets/doc-browser-DEqE9lsh.js +0 -1
  367. package/dist/assets/doc-browser-context-DkzFIUoK.js +0 -1
  368. package/dist/assets/eventmodeling-45OFAUF4-XVcQGgha.js +0 -1
  369. package/dist/assets/eye-gOWQGt8N.js +0 -1
  370. package/dist/assets/flowDiagram-23GEKE2U-BiFSctqz.js +0 -1
  371. package/dist/assets/form-actions-CgjYZUzM.js +0 -1
  372. package/dist/assets/gitGraph-TEB2WS4Q-C014uXqS.js +0 -1
  373. package/dist/assets/host-capabilities-B-TtABsP.js +0 -1
  374. package/dist/assets/index-DSAUWCfl.js +0 -2
  375. package/dist/assets/index-G7cBryJ4.css +0 -1
  376. package/dist/assets/info-DKCQHKI2-CQ2qjGhU.js +0 -1
  377. package/dist/assets/loader-circle-BAuycGf6.js +0 -1
  378. package/dist/assets/logo-badge-BNcsDsUM.js +0 -1
  379. package/dist/assets/mcp-marketplace-page-BX39MtPa.js +0 -9
  380. package/dist/assets/mcp-marketplace-page-CkDtOEA4.js +0 -1
  381. package/dist/assets/model-config-page-7MIXg2fQ.js +0 -1
  382. package/dist/assets/navigation-link-B3iFOdxx.js +0 -1
  383. package/dist/assets/packet-7NZHBO7P-6IFyydij.js +0 -1
  384. package/dist/assets/pie-RZYD4A2V-spXQC_wr.js +0 -1
  385. package/dist/assets/plus-yvhFgfal.js +0 -1
  386. package/dist/assets/popover-Cpep_rJe.js +0 -1
  387. package/dist/assets/provider-models-CUrptZ-o.js +0 -1
  388. package/dist/assets/provider-scoped-model-input-CHmOzz7x.js +0 -1
  389. package/dist/assets/providers-config-page-A6J7Y0II.js +0 -1
  390. package/dist/assets/radar-I7S5WNFK-DWVA8lvY.js +0 -1
  391. package/dist/assets/railroad-3IZDKUUU-Ce51u8y1.js +0 -1
  392. package/dist/assets/railroad-abnf-AHOZXSZD-D7i7cqI_.js +0 -1
  393. package/dist/assets/railroad-ebnf-EBAXGLYW-BEg2hcPs.js +0 -1
  394. package/dist/assets/railroad-peg-LSFZ7HO6-CxROSzbi.js +0 -1
  395. package/dist/assets/react-BCJGCjrP.js +0 -8
  396. package/dist/assets/remote-FD-ZfSIn.js +0 -1
  397. package/dist/assets/rotate-cw-DrJMfHhp.js +0 -1
  398. package/dist/assets/runtime-config-page-DwjO-Vs6.js +0 -1
  399. package/dist/assets/search-config-page-T6tMhXbP.js +0 -1
  400. package/dist/assets/security-config-DD5RrjDe.js +0 -1
  401. package/dist/assets/select-BvidP5Oa.js +0 -1
  402. package/dist/assets/skeleton-BEQW2Dva.js +0 -1
  403. package/dist/assets/stateDiagram-v2-6OUMAXLB-Bui4RXQK.js +0 -1
  404. package/dist/assets/swimlanesDiagram-G3AALYLV-DTwUsCBz.js +0 -8
  405. package/dist/assets/tabs-BeLxVmkt.js +0 -1
  406. package/dist/assets/trash-2-CYUskMMN.js +0 -1
  407. package/dist/assets/treeView-QDETBFTQ-D5zsTiuL.js +0 -1
  408. package/dist/assets/treemap-6X3UGDF4-CR3dxqNs.js +0 -1
  409. package/dist/assets/use-config-CxKRXXCl.js +0 -1
  410. package/dist/assets/use-confirm-dialog-HASTvOP2.js +0 -1
  411. package/dist/assets/use-viewport-layout-CJxkjoRz.js +0 -1
  412. package/dist/assets/wardley-OPB4EBWU-LatRT_Lx.js +0 -1
  413. package/dist/assets/x-DdUwjre5.js +0 -1
  414. /package/dist/assets/{array-CAkJT3no.js → array-NADKd9aU.js} +0 -0
  415. /package/dist/assets/{chunk-KEIR6QF5-BgdDoWEH.js → chunk-KEIR6QF5-BuyuzJJL.js} +0 -0
  416. /package/dist/assets/{chunk-Y2CYZVJY-pTllD0P3.js → chunk-Y2CYZVJY-D_B2ZRNt.js} +0 -0
  417. /package/dist/assets/{config-hints-S3g-UVZb.js → config-hints-BcfVUMij.js} +0 -0
  418. /package/dist/assets/{cytoscape.esm-DmURt-5G.js → cytoscape.esm-BANTlgza.js} +0 -0
  419. /package/dist/assets/{defaultLocale-BKy_KP3O.js → defaultLocale-C4_L0aEs.js} +0 -0
  420. /package/dist/assets/{dist-IgNNB7K9.js → dist-DAPEIJbz.js} +0 -0
  421. /package/dist/assets/{graphlib-xw_TBHyH.js → graphlib-ChqXg0dI.js} +0 -0
  422. /package/dist/assets/{init-C9xlLIAu.js → init-D8wZg9xq.js} +0 -0
  423. /package/dist/assets/{jszip.min-Das2IAnb.js → jszip.min-CkbBfwAQ.js} +0 -0
  424. /package/dist/assets/{katex-DSy1eD2d.js → katex-TZJFa0J_.js} +0 -0
  425. /package/dist/assets/{middleware-Df-a4x-q.js → middleware-B3k-TSmZ.js} +0 -0
  426. /package/dist/assets/{path-DB0USYHi.js → path-DodfxcX7.js} +0 -0
  427. /package/dist/assets/{rough.esm-B1B52ZQS.js → rough.esm-CUaa1Rbe.js} +0 -0
  428. /package/dist/assets/{xlsx-DfjC_iml.js → xlsx-CxByi7bR.js} +0 -0
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  buildChatSlashItems,
3
+ buildModelStateHint,
3
4
  buildModelToolbarSelect,
4
5
  buildSkillPickerModel
5
6
  } from '@/features/chat/features/input/utils/chat-input-bar.utils';
@@ -23,11 +24,49 @@ function createModelTexts() {
23
24
  favoriteModelLabel: 'Favorite model',
24
25
  unfavoriteModelLabel: 'Remove favorite',
25
26
  manageModelsLabel: 'Manage models and providers',
27
+ discoveredModelsSummaryLabel: '{count} new models available',
28
+ discoveredModelsViewLabel: 'View',
29
+ discoveredModelsGroupLabel: 'New models',
30
+ discoveredModelAddLabel: 'Add',
31
+ discoveredModelAddedLabel: 'Added',
32
+ discoveredModelsDismissLabel: 'Dismiss this batch',
33
+ discoveredModelsDoneLabel: 'Done',
34
+ discoveredModelsCloseLabel: 'Close new models',
26
35
  recentModelsLabel: 'Recent',
27
36
  allModelsLabel: 'All models'
28
37
  };
29
38
  }
30
39
 
40
+ describe('buildModelStateHint', () => {
41
+ it('keeps the input surface clear while model options are unresolved', () => {
42
+ expect(buildModelStateHint({
43
+ isModelOptionsEmpty: false,
44
+ onGoToProviders: vi.fn(),
45
+ texts: {
46
+ noModelOptionsLabel: 'No models',
47
+ configureProviderLabel: 'Configure provider'
48
+ }
49
+ })).toBeNull();
50
+ });
51
+
52
+ it('keeps the actionable warning when model loading resolves empty', () => {
53
+ const onGoToProviders = vi.fn();
54
+
55
+ expect(buildModelStateHint({
56
+ isModelOptionsEmpty: true,
57
+ onGoToProviders,
58
+ texts: {
59
+ noModelOptionsLabel: 'No models',
60
+ configureProviderLabel: 'Configure provider'
61
+ }
62
+ })).toEqual({
63
+ tone: 'warning',
64
+ text: 'No models',
65
+ actionLabel: 'Configure provider',
66
+ onAction: onGoToProviders
67
+ });
68
+ });
69
+ });
31
70
  describe('buildChatSlashItems', () => {
32
71
  const texts = {
33
72
  slashSkillSubtitle: 'Skill',
@@ -239,6 +278,78 @@ describe('buildSkillPickerModel', () => {
239
278
  });
240
279
 
241
280
  describe('buildModelToolbarSelect', () => {
281
+ it('exposes discovered models through an expanded-panel contract without changing the selected label', () => {
282
+ const onDiscoveredModelsDismiss = vi.fn();
283
+ const onDiscoveredModelSelect = vi.fn();
284
+ const onOpen = vi.fn();
285
+ const select = buildModelToolbarSelect({
286
+ modelOptions: [{
287
+ value: 'opencode/big-pickle',
288
+ modelLabel: 'big-pickle',
289
+ providerLabel: 'OpenCode',
290
+ }],
291
+ discoveredModelOptions: [
292
+ {
293
+ value: 'opencode/deepseek-v4-flash-free',
294
+ modelLabel: 'deepseek-v4-flash-free',
295
+ providerLabel: 'OpenCode',
296
+ },
297
+ {
298
+ value: 'openrouter/inclusionai/ling-3.0-tiny:free',
299
+ modelLabel: 'inclusionai/ling-3.0-tiny:free',
300
+ providerLabel: 'OpenRouter',
301
+ },
302
+ ],
303
+ recentModelValues: [],
304
+ selectedModel: 'opencode/big-pickle',
305
+ isModelOptionsLoading: false,
306
+ hasModelOptions: true,
307
+ onDiscoveredModelSelect,
308
+ onDiscoveredModelsDismiss,
309
+ onOpen,
310
+ onValueChange: vi.fn(),
311
+ texts: createModelTexts(),
312
+ });
313
+
314
+ expect(select.selectedLabel).toBe('OpenCode/big-pickle');
315
+ expect(select.discovery).toEqual({
316
+ summaryLabel: '2 new models available',
317
+ viewLabel: 'View',
318
+ groupLabel: 'New models',
319
+ allGroupLabel: 'All models',
320
+ actionLabel: 'Add',
321
+ addedLabel: 'Added',
322
+ dismissLabel: 'Dismiss this batch',
323
+ doneLabel: 'Done',
324
+ closeLabel: 'Close new models',
325
+ searchPlaceholder: 'Search models',
326
+ searchEmptyLabel: 'No matching models',
327
+ groups: [
328
+ {
329
+ key: 'opencode',
330
+ label: 'OpenCode',
331
+ options: [{
332
+ value: 'opencode/deepseek-v4-flash-free',
333
+ label: 'deepseek-v4-flash-free',
334
+ }],
335
+ },
336
+ {
337
+ key: 'openrouter',
338
+ label: 'OpenRouter',
339
+ options: [{
340
+ value: 'openrouter/inclusionai/ling-3.0-tiny:free',
341
+ label: 'inclusionai/ling-3.0-tiny:free',
342
+ }],
343
+ },
344
+ ],
345
+ onDismiss: onDiscoveredModelsDismiss,
346
+ onSelect: onDiscoveredModelSelect,
347
+ });
348
+ expect(select.onOpen).toBe(onOpen);
349
+ });
350
+ });
351
+
352
+ describe('buildModelToolbarSelect selection', () => {
242
353
  it('falls back to the first available option when the selected model is missing', () => {
243
354
  const onValueChange = vi.fn();
244
355
  const select = buildModelToolbarSelect({
@@ -29,6 +29,27 @@ function appendTextPart(parts: NcpMessagePart[], text: string): NcpMessagePart[]
29
29
  ];
30
30
  }
31
31
 
32
+ function hasSerializedTokenText(node: ChatComposerNode | undefined): boolean {
33
+ return node?.type === 'token' && Boolean(serializeChatComposerTokenText(node));
34
+ }
35
+
36
+ function serializeTokenWithTextBoundaries(params: {
37
+ index: number;
38
+ nodes: readonly ChatComposerNode[];
39
+ tokenText: string;
40
+ }): string {
41
+ const { index, nodes, tokenText } = params;
42
+ const previousNode = nodes[index - 1];
43
+ const nextNode = nodes[index + 1];
44
+ const needsLeadingSpace = (
45
+ previousNode?.type === 'text' && /\S$/.test(previousNode.text)
46
+ ) || hasSerializedTokenText(previousNode);
47
+ const needsTrailingSpace = (
48
+ nextNode?.type === 'text' && /^\S/.test(nextNode.text)
49
+ ) || hasSerializedTokenText(nextNode);
50
+ return `${needsLeadingSpace ? ' ' : ''}${tokenText}${needsTrailingSpace ? ' ' : ''}`;
51
+ }
52
+
32
53
  export function createInitialChatComposerNodes(): ChatComposerNode[] {
33
54
  return createEmptyChatComposerNodes();
34
55
  }
@@ -102,7 +123,7 @@ export function deriveNcpMessagePartsFromComposer(
102
123
  const attachmentById = new Map(attachments.map((attachment) => [attachment.id, attachment]));
103
124
  let parts: NcpMessagePart[] = [];
104
125
 
105
- for (const node of nodes) {
126
+ for (const [index, node] of nodes.entries()) {
106
127
  if (node.type === 'text') {
107
128
  parts = appendTextPart(parts, node.text);
108
129
  continue;
@@ -110,7 +131,11 @@ export function deriveNcpMessagePartsFromComposer(
110
131
 
111
132
  const tokenText = serializeChatComposerTokenText(node);
112
133
  if (tokenText) {
113
- parts = appendTextPart(parts, tokenText);
134
+ parts = appendTextPart(parts, serializeTokenWithTextBoundaries({
135
+ index,
136
+ nodes,
137
+ tokenText,
138
+ }));
114
139
  continue;
115
140
  }
116
141
 
@@ -1,6 +1,8 @@
1
1
  import {
2
+ CHAT_CONVERSATION_EXCERPT_TOKEN_KIND,
2
3
  CHAT_PROJECT_TOKEN_KIND,
3
4
  CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND,
5
+ CHAT_WORKSPACE_EXCERPT_TOKEN_KIND,
4
6
  CHAT_WORKSPACE_FILE_TOKEN_KIND,
5
7
  } from '@nextclaw/shared';
6
8
 
@@ -9,6 +11,8 @@ const CHAT_PANEL_APP_TOKEN_PREFIX = '@panel-app:';
9
11
  const CHAT_PROJECT_TOKEN_PREFIX = '@project:';
10
12
  const CHAT_WORKSPACE_FILE_TOKEN_PREFIX = '@file:';
11
13
  const CHAT_WORKSPACE_DIRECTORY_TOKEN_PREFIX = '@folder:';
14
+ const CHAT_WORKSPACE_EXCERPT_TOKEN_PREFIX = '@excerpt:';
15
+ const CHAT_CONVERSATION_EXCERPT_TOKEN_PREFIX = '@message-excerpt:';
12
16
 
13
17
  export function serializeChatComposerTokenText(params: {
14
18
  label?: string;
@@ -31,5 +35,11 @@ export function serializeChatComposerTokenText(params: {
31
35
  if (tokenKind === CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND) {
32
36
  return `${CHAT_WORKSPACE_DIRECTORY_TOKEN_PREFIX}${encodeURIComponent(tokenKey)}`;
33
37
  }
38
+ if (tokenKind === CHAT_WORKSPACE_EXCERPT_TOKEN_KIND) {
39
+ return `${CHAT_WORKSPACE_EXCERPT_TOKEN_PREFIX}${encodeURIComponent(tokenKey)}`;
40
+ }
41
+ if (tokenKind === CHAT_CONVERSATION_EXCERPT_TOKEN_KIND) {
42
+ return `${CHAT_CONVERSATION_EXCERPT_TOKEN_PREFIX}${encodeURIComponent(tokenKey)}`;
43
+ }
34
44
  return null;
35
45
  }
@@ -0,0 +1,59 @@
1
+ import type { ChatComposerNode } from '@nextclaw/agent-chat-ui';
2
+ import {
3
+ CHAT_CONVERSATION_EXCERPT_TOKEN_KIND,
4
+ type ChatConversationExcerptInlineTokenMetadata,
5
+ } from '@nextclaw/shared';
6
+
7
+ type ComposerTokenNode = Extract<ChatComposerNode, { type: 'token' }>;
8
+
9
+ function readRequiredString(value: unknown): string | null {
10
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
11
+ }
12
+
13
+ function readConversationExcerptFields(source: Record<string, unknown>): {
14
+ messageId: string;
15
+ role: 'assistant' | 'user';
16
+ excerpt: string;
17
+ } | null {
18
+ const messageId = readRequiredString(source.messageId);
19
+ const excerpt = readRequiredString(source.excerpt);
20
+ const { role } = source;
21
+ return messageId && excerpt && (role === 'assistant' || role === 'user')
22
+ ? { messageId, role, excerpt }
23
+ : null;
24
+ }
25
+
26
+ export function buildConversationExcerptInlineToken(
27
+ node: ComposerTokenNode,
28
+ rawText: string,
29
+ ): ChatConversationExcerptInlineTokenMetadata | null {
30
+ const fields = readConversationExcerptFields(node.data ?? {});
31
+ return fields
32
+ ? {
33
+ kind: CHAT_CONVERSATION_EXCERPT_TOKEN_KIND,
34
+ key: node.tokenKey,
35
+ label: node.label,
36
+ rawText,
37
+ ...fields,
38
+ }
39
+ : null;
40
+ }
41
+
42
+ export function readConversationExcerptInlineToken(params: {
43
+ entry: Record<string, unknown>;
44
+ label: string;
45
+ rawText: string;
46
+ }): ChatConversationExcerptInlineTokenMetadata | null {
47
+ const { entry, label, rawText } = params;
48
+ const key = readRequiredString(entry.key);
49
+ const fields = readConversationExcerptFields(entry);
50
+ return key && fields
51
+ ? {
52
+ kind: CHAT_CONVERSATION_EXCERPT_TOKEN_KIND,
53
+ key,
54
+ label,
55
+ rawText,
56
+ ...fields,
57
+ }
58
+ : null;
59
+ }
@@ -1,15 +1,18 @@
1
1
  import type { ChatComposerNode } from '@nextclaw/agent-chat-ui';
2
2
  import {
3
+ CHAT_CONVERSATION_EXCERPT_TOKEN_KIND,
3
4
  CHAT_INLINE_TOKENS_METADATA_KEY,
4
5
  CHAT_INLINE_TOKENS_SCHEMA_VERSION,
5
6
  CHAT_PROJECT_TOKEN_KIND,
6
7
  CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND,
8
+ CHAT_WORKSPACE_EXCERPT_TOKEN_KIND,
7
9
  CHAT_WORKSPACE_FILE_TOKEN_KIND,
8
10
  type ChatInlineTokenMetadata,
9
11
  type ChatInlineTokensMetadata,
10
12
  type ChatSkillSource,
11
13
  } from '@nextclaw/shared';
12
14
  import { serializeChatComposerTokenText } from './chat-composer-token-protocol.utils';
15
+ import { buildConversationExcerptInlineToken, readConversationExcerptInlineToken } from './chat-conversation-excerpt-token.utils';
13
16
 
14
17
  export { CHAT_INLINE_TOKENS_METADATA_KEY };
15
18
  const CHAT_PANEL_APP_TOKEN_PREFIX = '@panel-app:';
@@ -35,6 +38,25 @@ export type ChatInlineTokenSource =
35
38
  label: string;
36
39
  rawText: string;
37
40
  }
41
+ | {
42
+ kind: typeof CHAT_CONVERSATION_EXCERPT_TOKEN_KIND;
43
+ key: string;
44
+ messageId: string;
45
+ role: 'assistant' | 'user';
46
+ label: string;
47
+ excerpt: string;
48
+ rawText: string;
49
+ }
50
+ | {
51
+ kind: typeof CHAT_WORKSPACE_EXCERPT_TOKEN_KIND;
52
+ key: string;
53
+ path: string;
54
+ label: string;
55
+ excerpt: string;
56
+ startLine: number | null;
57
+ endLine: number | null;
58
+ rawText: string;
59
+ }
38
60
  | {
39
61
  kind: string;
40
62
  key: string;
@@ -71,6 +93,12 @@ function readOptionalString(value: unknown): string | null {
71
93
  return trimmed.length > 0 ? trimmed : null;
72
94
  }
73
95
 
96
+ function readOptionalLine(value: unknown): number | null {
97
+ return typeof value === 'number' && Number.isInteger(value) && value > 0
98
+ ? value
99
+ : null;
100
+ }
101
+
74
102
  function dedupeInlineTokens<T extends ChatInlineTokenSource>(tokens: readonly T[]): T[] {
75
103
  const seen = new Set<string>();
76
104
  const output: T[] = [];
@@ -86,6 +114,26 @@ function dedupeInlineTokens<T extends ChatInlineTokenSource>(tokens: readonly T[
86
114
  return output;
87
115
  }
88
116
 
117
+ function appendWorkspaceExcerptToken(
118
+ tokens: ChatInlineTokenMetadata[],
119
+ node: Extract<ChatComposerNode, { type: 'token' }>,
120
+ rawText: string,
121
+ ): void {
122
+ const path = readOptionalString(node.data?.path);
123
+ const excerpt = readOptionalString(node.data?.excerpt);
124
+ if (!path || !excerpt) return;
125
+ tokens.push({
126
+ kind: CHAT_WORKSPACE_EXCERPT_TOKEN_KIND,
127
+ key: node.tokenKey,
128
+ path,
129
+ label: node.label,
130
+ excerpt,
131
+ startLine: readOptionalLine(node.data?.startLine),
132
+ endLine: readOptionalLine(node.data?.endLine),
133
+ rawText,
134
+ });
135
+ }
136
+
89
137
  export function buildInlineTokensFromComposer(
90
138
  nodes: readonly ChatComposerNode[],
91
139
  skillRecords: readonly ChatSkillReferenceSnapshot[] = [],
@@ -100,7 +148,9 @@ export function buildInlineTokensFromComposer(
100
148
  node.tokenKind !== 'skill' &&
101
149
  node.tokenKind !== CHAT_PROJECT_TOKEN_KIND &&
102
150
  node.tokenKind !== CHAT_WORKSPACE_FILE_TOKEN_KIND &&
103
- node.tokenKind !== CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND
151
+ node.tokenKind !== CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND &&
152
+ node.tokenKind !== CHAT_WORKSPACE_EXCERPT_TOKEN_KIND &&
153
+ node.tokenKind !== CHAT_CONVERSATION_EXCERPT_TOKEN_KIND
104
154
  ) {
105
155
  continue;
106
156
  }
@@ -133,6 +183,15 @@ export function buildInlineTokensFromComposer(
133
183
  });
134
184
  continue;
135
185
  }
186
+ if (node.tokenKind === CHAT_WORKSPACE_EXCERPT_TOKEN_KIND) {
187
+ appendWorkspaceExcerptToken(tokens, node, rawText);
188
+ continue;
189
+ }
190
+ if (node.tokenKind === CHAT_CONVERSATION_EXCERPT_TOKEN_KIND) {
191
+ const token = buildConversationExcerptInlineToken(node, rawText);
192
+ if (token) tokens.push(token);
193
+ continue;
194
+ }
136
195
  const workspaceKind = node.tokenKind === CHAT_WORKSPACE_FILE_TOKEN_KIND
137
196
  ? CHAT_WORKSPACE_FILE_TOKEN_KIND
138
197
  : CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND;
@@ -215,7 +274,15 @@ export function resolveInlineTokensForText(
215
274
  text: string,
216
275
  tokens: readonly ChatInlineTokenSource[]
217
276
  ): ChatInlineTokenSource[] {
218
- return dedupeInlineTokens([...tokens, ...buildInlineTokensFromTextProtocol(text)]);
277
+ const inferredTokens = buildInlineTokensFromTextProtocol(text).filter((inferredToken) => {
278
+ const inferredIndex = text.indexOf(inferredToken.rawText);
279
+ return !tokens.some((explicitToken) => (
280
+ explicitToken.kind === inferredToken.kind &&
281
+ inferredToken.rawText.startsWith(explicitToken.rawText) &&
282
+ text.indexOf(explicitToken.rawText) === inferredIndex
283
+ ));
284
+ });
285
+ return dedupeInlineTokens([...tokens, ...inferredTokens]);
219
286
  }
220
287
 
221
288
  export function readInlineTokensFromMetadata(
@@ -235,36 +302,8 @@ export function readInlineTokensFromMetadata(
235
302
 
236
303
  const tokens: ChatInlineTokenSource[] = [];
237
304
  for (const entry of raw.items) {
238
- if (!isRecord(entry)) {
239
- continue;
240
- }
241
- const kind = readOptionalString(entry.kind);
242
- const rawText = readOptionalString(entry.rawText);
243
- const label = readOptionalString(entry.label);
244
- if (!kind || !label || !rawText) {
245
- continue;
246
- }
247
- if (kind === 'skill') {
248
- const ref = readOptionalString(entry.ref);
249
- const name = readOptionalString(entry.name);
250
- const path = readOptionalString(entry.path);
251
- const source = readSkillSource(entry.source);
252
- if (!ref || !name || !path || !source) {
253
- continue;
254
- }
255
- tokens.push({ kind, ref, name, source, path, label, rawText });
256
- continue;
257
- }
258
- const key = readOptionalString(entry.key);
259
- if (!key) {
260
- continue;
261
- }
262
- tokens.push({
263
- kind,
264
- key,
265
- rawText,
266
- label,
267
- });
305
+ const token = readInlineTokenEntry(entry);
306
+ if (token) tokens.push(token);
268
307
  }
269
308
 
270
309
  return dedupeInlineTokens(tokens);
@@ -276,6 +315,58 @@ function readSkillSource(value: unknown): ChatSkillSource | null {
276
315
  : null;
277
316
  }
278
317
 
318
+ function readInlineTokenEntry(entry: unknown): ChatInlineTokenSource | null {
319
+ if (!isRecord(entry)) {
320
+ return null;
321
+ }
322
+ const kind = readOptionalString(entry.kind);
323
+ const rawText = readOptionalString(entry.rawText);
324
+ const label = readOptionalString(entry.label);
325
+ if (!kind || !label || !rawText) {
326
+ return null;
327
+ }
328
+ if (kind === 'skill') {
329
+ const ref = readOptionalString(entry.ref);
330
+ const name = readOptionalString(entry.name);
331
+ const path = readOptionalString(entry.path);
332
+ const source = readSkillSource(entry.source);
333
+ return ref && name && path && source
334
+ ? { kind, ref, name, source, path, label, rawText }
335
+ : null;
336
+ }
337
+ if (kind === CHAT_WORKSPACE_EXCERPT_TOKEN_KIND) {
338
+ return readWorkspaceExcerptInlineToken({ entry, label, rawText });
339
+ }
340
+ if (kind === CHAT_CONVERSATION_EXCERPT_TOKEN_KIND) {
341
+ return readConversationExcerptInlineToken({ entry, label, rawText });
342
+ }
343
+ const key = readOptionalString(entry.key);
344
+ return key ? { kind, key, rawText, label } : null;
345
+ }
346
+
347
+ function readWorkspaceExcerptInlineToken(params: {
348
+ entry: Record<string, unknown>;
349
+ label: string;
350
+ rawText: string;
351
+ }): ChatInlineTokenSource | null {
352
+ const { entry, label, rawText } = params;
353
+ const key = readOptionalString(entry.key);
354
+ const path = readOptionalString(entry.path);
355
+ const excerpt = readOptionalString(entry.excerpt);
356
+ return key && path && excerpt
357
+ ? {
358
+ kind: CHAT_WORKSPACE_EXCERPT_TOKEN_KIND,
359
+ key,
360
+ path,
361
+ label,
362
+ excerpt,
363
+ startLine: readOptionalLine(entry.startLine),
364
+ endLine: readOptionalLine(entry.endLine),
365
+ rawText,
366
+ }
367
+ : null;
368
+ }
369
+
279
370
  function readLegacyInlineTokens(entries: readonly unknown[]): ChatInlineTokenSource[] {
280
371
  const tokens: ChatInlineTokenSource[] = [];
281
372
  for (const entry of entries) {
@@ -8,6 +8,7 @@ export {
8
8
  buildModelStateHint,
9
9
  buildModelToolbarSelect,
10
10
  buildThinkingToolbarSelect,
11
+ toChatModelRecords,
11
12
  } from "./chat-input-toolbar.utils";
12
13
  import type {
13
14
  ChatInputBarAdapterTexts,
@@ -7,12 +7,42 @@ import type {
7
7
  ChatModelRecord,
8
8
  ChatThinkingLevel,
9
9
  } from "@/features/chat/types/chat-input-bar.types";
10
+ import type { ChatModelOption } from "@/features/chat/types/chat-input.types";
10
11
 
11
12
  function formatModelOptionLabel(option: ChatModelRecord): string {
12
13
  const modelLabel = option.modelLabel.trim();
13
14
  const providerLabel = option.providerLabel.trim();
14
15
  return providerLabel ? `${providerLabel}/${modelLabel}` : modelLabel;
15
16
  }
17
+ function buildDiscoveredModelGroups(options: ChatModelRecord[]) {
18
+ const groups = new Map<string, { key: string; label: string; options: Array<{ value: string; label: string }> }>();
19
+ for (const option of options) {
20
+ const separatorIndex = option.value.indexOf('/');
21
+ const providerKey = separatorIndex > 0 ? option.value.slice(0, separatorIndex) : option.providerLabel;
22
+ const group = groups.get(providerKey) ?? {
23
+ key: providerKey,
24
+ label: option.providerLabel,
25
+ options: [],
26
+ };
27
+ group.options.push({ value: option.value, label: option.modelLabel });
28
+ groups.set(providerKey, group);
29
+ }
30
+ return [...groups.values()];
31
+ }
32
+
33
+ export function toChatModelRecords(snapshotModels: ChatModelOption[]): ChatModelRecord[] {
34
+ return snapshotModels.map((model) => ({
35
+ value: model.value,
36
+ modelLabel: model.modelLabel,
37
+ providerLabel: model.providerLabel,
38
+ thinkingCapability: model.thinkingCapability
39
+ ? {
40
+ supported: model.thinkingCapability.supported as ChatThinkingLevel[],
41
+ default: (model.thinkingCapability.default as ChatThinkingLevel | null | undefined) ?? null,
42
+ }
43
+ : null,
44
+ }));
45
+ }
16
46
 
17
47
  function normalizeThinkingLevels(
18
48
  levels: ChatThinkingLevel[],
@@ -27,7 +57,6 @@ function normalizeThinkingLevels(
27
57
  }
28
58
 
29
59
  export function buildModelStateHint(params: {
30
- isModelOptionsLoading: boolean;
31
60
  isModelOptionsEmpty: boolean;
32
61
  onGoToProviders: () => void;
33
62
  texts: Pick<
@@ -37,19 +66,12 @@ export function buildModelStateHint(params: {
37
66
  }): ChatInlineHint | null {
38
67
  const {
39
68
  isModelOptionsEmpty,
40
- isModelOptionsLoading,
41
69
  onGoToProviders,
42
70
  texts,
43
71
  } = params;
44
- if (!isModelOptionsLoading && !isModelOptionsEmpty) {
72
+ if (!isModelOptionsEmpty) {
45
73
  return null;
46
74
  }
47
- if (isModelOptionsLoading) {
48
- return {
49
- tone: "neutral",
50
- loading: true,
51
- };
52
- }
53
75
  return {
54
76
  tone: "warning",
55
77
  text: texts.noModelOptionsLabel,
@@ -58,24 +80,73 @@ export function buildModelStateHint(params: {
58
80
  };
59
81
  }
60
82
 
83
+ function buildModelDiscovery(params: {
84
+ options: ChatModelRecord[];
85
+ onDismiss?: () => void;
86
+ onSelect?: (value: string) => Promise<void> | void;
87
+ texts: Pick<
88
+ ChatInputBarAdapterTexts,
89
+ | "allModelsLabel"
90
+ | "discoveredModelAddLabel"
91
+ | "discoveredModelAddedLabel"
92
+ | "discoveredModelsCloseLabel"
93
+ | "discoveredModelsDismissLabel"
94
+ | "discoveredModelsDoneLabel"
95
+ | "discoveredModelsGroupLabel"
96
+ | "discoveredModelsSummaryLabel"
97
+ | "discoveredModelsViewLabel"
98
+ | "modelSearchEmptyLabel"
99
+ | "modelSearchPlaceholder"
100
+ >;
101
+ }): ChatToolbarSelect["discovery"] {
102
+ const { onDismiss, onSelect, options, texts } = params;
103
+ if (!onDismiss || !onSelect || options.length === 0) {
104
+ return undefined;
105
+ }
106
+ return {
107
+ summaryLabel: texts.discoveredModelsSummaryLabel.replace('{count}', String(options.length)),
108
+ viewLabel: texts.discoveredModelsViewLabel,
109
+ groupLabel: texts.discoveredModelsGroupLabel,
110
+ allGroupLabel: texts.allModelsLabel,
111
+ actionLabel: texts.discoveredModelAddLabel,
112
+ addedLabel: texts.discoveredModelAddedLabel,
113
+ dismissLabel: texts.discoveredModelsDismissLabel,
114
+ doneLabel: texts.discoveredModelsDoneLabel,
115
+ closeLabel: texts.discoveredModelsCloseLabel,
116
+ searchPlaceholder: texts.modelSearchPlaceholder,
117
+ searchEmptyLabel: texts.modelSearchEmptyLabel,
118
+ groups: buildDiscoveredModelGroups(options),
119
+ onDismiss,
120
+ onSelect,
121
+ };
122
+ }
123
+
61
124
  export function buildModelToolbarSelect({
62
125
  modelOptions,
126
+ discoveredModelOptions,
63
127
  favoriteModelValues,
64
128
  recentModelValues,
65
129
  selectedModel,
66
130
  isModelOptionsLoading,
67
131
  hasModelOptions,
68
132
  onFavoriteToggle,
133
+ onDiscoveredModelSelect,
134
+ onDiscoveredModelsDismiss,
135
+ onOpen,
69
136
  onValueChange,
70
137
  texts,
71
138
  }: {
72
139
  modelOptions: ChatModelRecord[];
140
+ discoveredModelOptions?: ChatModelRecord[];
73
141
  favoriteModelValues?: string[];
74
142
  recentModelValues?: string[];
75
143
  selectedModel: string;
76
144
  isModelOptionsLoading: boolean;
77
145
  hasModelOptions: boolean;
78
146
  onFavoriteToggle?: (value: string, favorite: boolean) => void;
147
+ onDiscoveredModelSelect?: (value: string) => Promise<void> | void;
148
+ onDiscoveredModelsDismiss?: () => void;
149
+ onOpen?: () => void;
79
150
  onValueChange: (value: string) => void;
80
151
  texts: Pick<
81
152
  ChatInputBarAdapterTexts,
@@ -87,6 +158,14 @@ export function buildModelToolbarSelect({
87
158
  | "favoriteModelLabel"
88
159
  | "unfavoriteModelLabel"
89
160
  | "manageModelsLabel"
161
+ | "discoveredModelsSummaryLabel"
162
+ | "discoveredModelsViewLabel"
163
+ | "discoveredModelsGroupLabel"
164
+ | "discoveredModelAddLabel"
165
+ | "discoveredModelAddedLabel"
166
+ | "discoveredModelsDismissLabel"
167
+ | "discoveredModelsDoneLabel"
168
+ | "discoveredModelsCloseLabel"
90
169
  | "recentModelsLabel"
91
170
  | "allModelsLabel"
92
171
  >;
@@ -143,6 +222,7 @@ export function buildModelToolbarSelect({
143
222
  },
144
223
  ].filter((group) => group.options.length > 0)
145
224
  : undefined;
225
+ const discoveryOptions = discoveredModelOptions ?? [];
146
226
 
147
227
  return {
148
228
  key: "model",
@@ -157,7 +237,7 @@ export function buildModelToolbarSelect({
157
237
  label: formatModelOptionLabel(option),
158
238
  })),
159
239
  groups: optionGroups,
160
- disabled: !hasModelOptions,
240
+ disabled: !hasModelOptions && discoveryOptions.length === 0,
161
241
  loading: isModelOptionsLoading,
162
242
  emptyLabel: texts.modelNoOptionsLabel,
163
243
  search: {
@@ -173,8 +253,15 @@ export function buildModelToolbarSelect({
173
253
  onToggle: onFavoriteToggle,
174
254
  }
175
255
  : undefined,
256
+ discovery: buildModelDiscovery({
257
+ options: discoveryOptions,
258
+ onDismiss: onDiscoveredModelsDismiss,
259
+ onSelect: onDiscoveredModelSelect,
260
+ texts,
261
+ }),
176
262
  manageLabel: texts.manageModelsLabel,
177
263
  manageHref: "/providers",
264
+ onOpen,
178
265
  onValueChange,
179
266
  };
180
267
  }