@qredence/fleet 0.5.11 → 0.6.1

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 (132) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/web/client/assets/action-swap-roll-COzQ86wm.js +1 -0
  3. package/dist/web/client/assets/agent-activity-svETLf_5.js +1 -0
  4. package/dist/web/client/assets/agent-disclosure-DAMuuGcM.js +1 -0
  5. package/dist/web/client/assets/analytics-stub-B6zGMuSV.js +1 -0
  6. package/dist/web/client/assets/{artifacts-panel-CQmFEF_H.js → artifacts-panel-HVjqBMyx.js} +1 -1
  7. package/dist/web/client/assets/button-C5wFDY5_.js +1 -0
  8. package/dist/web/client/assets/card-DA3Vnqay.js +1 -0
  9. package/dist/web/client/assets/{chat-yh02zgA7.js → chat-BhYseKd6.js} +7 -7
  10. package/dist/web/client/assets/{chat-command-palette-CmHTS3rs.js → chat-command-palette-LILDO_vG.js} +1 -1
  11. package/dist/web/client/assets/{citations-C_tPOJE_.js → citations-BPv8HIpf.js} +2 -2
  12. package/dist/web/client/assets/command-PhicLCoO.js +1 -0
  13. package/dist/web/client/assets/dialog-pBGUjAQ7.js +1 -0
  14. package/dist/web/client/assets/fleet-message-queue-fbYYwMj0.js +1 -0
  15. package/dist/web/client/assets/{fleet-pi-tool-renderer-BEDIO3al.js → fleet-pi-tool-renderer-BRgNnmaJ.js} +28 -28
  16. package/dist/web/client/assets/fleet-reasoning-panel-CjA2dwUJ.js +1 -0
  17. package/dist/web/client/assets/{fork-picker-dialog-ByBOGVsa.js → fork-picker-dialog-B7NNpVmW.js} +1 -1
  18. package/dist/web/client/assets/globals-CuIiqOTg.css +2 -0
  19. package/dist/web/client/assets/horizontal-resize-Cp3iHMH7.js +1 -0
  20. package/dist/web/client/assets/index-CSGplyr_.js +12 -0
  21. package/dist/web/client/assets/{input-0wwjWjKq.js → input-PcdubeKn.js} +1 -1
  22. package/dist/web/client/assets/input-group-C2HJEZvl.js +1 -0
  23. package/dist/web/client/assets/ipython-tool-CyuMOxz1.js +3 -0
  24. package/dist/web/client/assets/{markdown-code-CuQcR3UO.js → markdown-code-xNyjdZ4g.js} +1 -1
  25. package/dist/web/client/assets/{markdown-DInPmRUG.js → markdown-lvexwooo.js} +2 -2
  26. package/dist/web/client/assets/{model-selector-list-DsKTuAgS.js → model-selector-list-CoXX358G.js} +1 -1
  27. package/dist/web/client/assets/{notify-DgMKgZZW.js → notify-DxL0VD7Z.js} +1 -1
  28. package/dist/web/client/assets/openui-renderer-TLNe5e_Q.js +378 -0
  29. package/dist/web/client/assets/pencil-DcUwsKLn.js +1 -0
  30. package/dist/web/client/assets/prompt-input-fwP8TsgQ.js +1 -0
  31. package/dist/web/client/assets/{prompt-suggestions-Cc--lSNR.js → prompt-suggestions-BlGXmWS7.js} +1 -1
  32. package/dist/web/client/assets/provider-catalog-DYMFdbKc.js +1 -0
  33. package/dist/web/client/assets/provider-label-B4q5mI65.js +1 -0
  34. package/dist/web/client/assets/{repl-panel-DsPX04Xr.js → repl-panel-C5om8MGU.js} +1 -1
  35. package/dist/web/client/assets/{right-panel-registry-DewpBWHM.js → right-panel-registry-BBlxLxnx.js} +2 -2
  36. package/dist/web/client/assets/routes-XGsAIqBJ.js +16 -0
  37. package/dist/web/client/assets/settings-dialog-BSBasR3o.js +2 -0
  38. package/dist/web/client/assets/{subagent-chat-panel-DkVUumrP.js → subagent-chat-panel-oeLDPDAK.js} +1 -1
  39. package/dist/web/client/assets/{subagent-transcript-D8wmQxXe.js → subagent-transcript-DaVHjNUH.js} +1 -1
  40. package/dist/web/client/assets/{subagents-panel-BMp2Ego0.js → subagents-panel-D_aVgSCi.js} +1 -1
  41. package/dist/web/client/assets/surfaces-BhAXz9So.js +1 -0
  42. package/dist/web/client/assets/{switch-9rTeg6J6.js → switch-WhXeJNNY.js} +1 -1
  43. package/dist/web/client/assets/{toast-Bt9p2OL4.js → toast-D3c9XeGe.js} +1 -1
  44. package/dist/web/client/assets/{tool-result-DfnPN0FQ.js → tool-result-Cki8WUNc.js} +3 -3
  45. package/dist/web/client/assets/virtualized-turn-list-CdMa-ydT.js +6 -0
  46. package/dist/web/client/assets/workspace-panel-77bDbprs.js +1 -0
  47. package/dist/web/client/bundle-budget.json +6 -6
  48. package/dist/web/launcher.mjs +10 -1
  49. package/dist/web/server/{_tanstack-start-manifest_v-po4woX6g-AUGCOEZ2.js → _tanstack-start-manifest_v-0L7fqnH4-KMVGUFE4.js} +25 -24
  50. package/dist/web/server/{agent-activity-CS2rbF_C-A3R26CYH.js → agent-activity-BuEE6iVw-CDZLK4D2.js} +4 -4
  51. package/dist/web/server/{artifacts-panel-8GGUCtOd-X5TNKTY2.js → artifacts-panel-D3ySO--V-XSL53CXL.js} +18 -18
  52. package/dist/web/server/{chat-command-palette-Dgl5tqWn-72LLEWUD.js → chat-command-palette-kAbXhWVg-TJWXH2WW.js} +9 -9
  53. package/dist/web/server/{chunk-O4OTWWPF.js → chunk-2DDWXTVG.js} +5 -5
  54. package/dist/web/server/{chunk-MIW4LJTV.js → chunk-2JHTYWD5.js} +15 -23
  55. package/dist/web/server/{chunk-7MDZVLIY.js → chunk-3TIMGWWI.js} +1 -2
  56. package/dist/web/server/{chunk-AGFRJQZ7.js → chunk-4WBFCB4O.js} +257 -257
  57. package/dist/web/server/{chunk-P6UMR32I.js → chunk-5UCAJBAH.js} +4 -4
  58. package/dist/web/server/{chunk-BXWAUTTH.js → chunk-D27LRYVT.js} +42 -42
  59. package/dist/web/server/{chunk-YXXRP3NS.js → chunk-DLUZFOG3.js} +5 -5
  60. package/dist/web/server/{chunk-K3BXQG3N.js → chunk-EBG3XFGQ.js} +60 -60
  61. package/dist/web/server/{chunk-2ATMSG7U.js → chunk-EJEPDVK6.js} +3 -3
  62. package/dist/web/server/{chunk-IJPCV6DH.js → chunk-FYDQK6UX.js} +1 -1
  63. package/dist/web/server/{chunk-WKV2Z4KO.js → chunk-HF7DG7PC.js} +74 -74
  64. package/dist/web/server/{chunk-WYL2DW5R.js → chunk-JTCCQZLN.js} +2 -2
  65. package/dist/web/server/{chunk-UAPNEWCA.js → chunk-KP2PNDGL.js} +6 -6
  66. package/dist/web/server/chunk-M5RSPNYN.js +22 -0
  67. package/dist/web/server/{chunk-7SR6WDM2.js → chunk-MNBGB2SP.js} +2 -2
  68. package/dist/web/server/{chunk-HSJMLO36.js → chunk-MNPHMZU7.js} +3 -3
  69. package/dist/web/server/{chunk-JQDLJ7KI.js → chunk-NGCQJ33Y.js} +1134 -1134
  70. package/dist/web/server/{chunk-7SD2ZSEA.js → chunk-NIDBN7BQ.js} +1 -1
  71. package/dist/web/server/{chunk-ZJFA4F5X.js → chunk-NIZBFVJF.js} +2 -2
  72. package/dist/web/server/{chunk-P7H2OPKF.js → chunk-NJ4QEZHK.js} +11 -4
  73. package/dist/web/server/{chunk-WM4PUT6M.js → chunk-NNKJTKOQ.js} +234 -234
  74. package/dist/web/server/{chunk-N6NOTURM.js → chunk-NWDI3OWB.js} +4 -4
  75. package/dist/web/server/{chunk-ABBWBBSS.js → chunk-O3HDELZ2.js} +74826 -74825
  76. package/dist/web/server/{chunk-GGT2ZKDB.js → chunk-PLYZ7P2Y.js} +11 -155
  77. package/dist/web/server/chunk-PTCKOR23.js +39 -0
  78. package/dist/web/server/{chunk-RNOJACWQ.js → chunk-QFRCJWQQ.js} +7 -7
  79. package/dist/web/server/{chunk-NXJ32VFR.js → chunk-QIULBRTU.js} +13 -13
  80. package/dist/web/server/{chunk-XKKXXG6N.js → chunk-RIP3PBWT.js} +3 -3
  81. package/dist/web/server/{chunk-YKVGQBZA.js → chunk-S5LFXXDY.js} +625 -622
  82. package/dist/web/server/{chunk-XLNCWZMZ.js → chunk-UCUFC553.js} +3 -1
  83. package/dist/web/server/{chunk-KOUEJ3NU.js → chunk-UL2725KQ.js} +3 -3
  84. package/dist/web/server/{chunk-HFQTI7AX.js → chunk-VPKX7QRU.js} +3 -3
  85. package/dist/web/server/{chunk-LZ4Q5KUJ.js → chunk-WNCAL4HN.js} +133 -133
  86. package/dist/web/server/{chunk-C5X6G26O.js → chunk-XIB6XNUX.js} +436 -436
  87. package/dist/web/server/{chunk-JUROEEWM.js → chunk-YGYOBX7G.js} +82 -4
  88. package/dist/web/server/{chunk-WILCIZYU.js → chunk-ZIFSJ3HQ.js} +626 -626
  89. package/dist/web/server/fleet-message-queue-D_w8lSeY-T3HB7HVQ.js +152 -0
  90. package/dist/web/server/{fleet-pi-tool-renderer-umKunT3K-O4GBH35V.js → fleet-pi-tool-renderer-qwc-qZFq-4U556N5Q.js} +12 -12
  91. package/dist/web/server/{fleet-reasoning-panel-BxC2COxi-GPLMQSTT.js → fleet-reasoning-panel-DG_vuvv0-NCZWEEIT.js} +26 -26
  92. package/dist/web/server/{fleet-subagent-list-CpSYzCIq-JDH2LHSS.js → fleet-subagent-list-BGb0PjhF-2X67FMUV.js} +1 -1
  93. package/dist/web/server/{fleet-tool-timeline-IB36_PB2-XJENTO6I.js → fleet-tool-timeline-D6yM4ZMz-ZSFVRRRI.js} +1 -1
  94. package/dist/web/server/{fork-picker-dialog-DlINz9UQ-UWIXFDJX.js → fork-picker-dialog-Ds6Tgtu3-DIBWN7MV.js} +5 -5
  95. package/dist/web/server/{model-selector-list-OYnzNbyu-HBGCNF2L.js → model-selector-list-DxeGZoD2-P6S6FSJH.js} +9 -9
  96. package/dist/web/server/{openui-renderer-Cj7kfqyL-EKJ6JTTQ.js → openui-renderer-MKFv6LlH-MDRSVFEY.js} +11 -11
  97. package/dist/web/server/{prompt-suggestions-D4AEEY7K-RZSP7PWV.js → prompt-suggestions-BOItmGnD-YQA7RHLG.js} +2 -2
  98. package/dist/web/server/{repl-panel-D5KsoJik-BQKO53JO.js → repl-panel-CuyF8wt5-CHDT2Q3N.js} +2 -2
  99. package/dist/web/server/{router-Ci1wxI7I-E2AYMNON.js → router-Dg72SESP-PCWKLFRG.js} +1239 -470
  100. package/dist/web/server/{routes-CRpm9-Sa-IYK6MUJO.js → routes-BGrVtPYF-SURPAJN6.js} +5781 -5588
  101. package/dist/web/server/server.js +2 -2
  102. package/dist/web/server/{settings-dialog-BvkgNflv-GPJ6COQO.js → settings-dialog-CK0FJlTg-HZRBAXDZ.js} +1577 -1264
  103. package/dist/web/server/{subagent-chat-panel-CMuvW9Sd-M5TEEGQW.js → subagent-chat-panel-gzamnTgK-PQ2TLLJF.js} +21 -21
  104. package/dist/web/server/{subagents-panel-DgfB6_Yy-4INEPE3Y.js → subagents-panel-Berjd7HJ-EI5ZKC6V.js} +20 -20
  105. package/dist/web/server/{workspace-panel-bpwj-Veu-Q2CEJMN4.js → workspace-panel-CBBa_3UO-B4PPFPMA.js} +46 -21
  106. package/package.json +1 -1
  107. package/dist/web/client/assets/action-swap-roll-BB8DFMuf.js +0 -1
  108. package/dist/web/client/assets/agent-activity-CwMDU7aP.js +0 -1
  109. package/dist/web/client/assets/agent-disclosure-B8SnhQXL.js +0 -1
  110. package/dist/web/client/assets/analytics-stub-Cas1He0w.js +0 -1
  111. package/dist/web/client/assets/button-BR2WWrFo.js +0 -1
  112. package/dist/web/client/assets/card-BKl76a3R.js +0 -1
  113. package/dist/web/client/assets/command-DPfiaYUL.js +0 -1
  114. package/dist/web/client/assets/dialog-BEZuZhzk.js +0 -1
  115. package/dist/web/client/assets/fleet-message-queue-DUz6Hfrf.js +0 -1
  116. package/dist/web/client/assets/fleet-reasoning-panel-CbkO7u-v.js +0 -1
  117. package/dist/web/client/assets/globals-B_I1Eq1u.css +0 -2
  118. package/dist/web/client/assets/horizontal-resize-qK6eRhIG.js +0 -1
  119. package/dist/web/client/assets/index-C1zOjrMK.js +0 -12
  120. package/dist/web/client/assets/input-group-BFDsCVS9.js +0 -1
  121. package/dist/web/client/assets/ipython-tool-ldJ9F1Cw.js +0 -3
  122. package/dist/web/client/assets/openui-renderer-J3dhPpL4.js +0 -378
  123. package/dist/web/client/assets/prompt-input-qPIXZ-Rb.js +0 -1
  124. package/dist/web/client/assets/provider-catalog-D8G66gwc.js +0 -1
  125. package/dist/web/client/assets/provider-label-Dpr7djG9.js +0 -1
  126. package/dist/web/client/assets/routes-vNkVToAF.js +0 -16
  127. package/dist/web/client/assets/settings-dialog-B-v4me3Y.js +0 -1
  128. package/dist/web/client/assets/surfaces-DI-U-_2a.js +0 -1
  129. package/dist/web/client/assets/virtualized-turn-list-DMJENRY0.js +0 -6
  130. package/dist/web/client/assets/workspace-panel-G6yMpKJ8.js +0 -1
  131. package/dist/web/server/chunk-QK4CHLVV.js +0 -17
  132. package/dist/web/server/fleet-message-queue-j0DCU5Xy-5HKBHMPV.js +0 -75
@@ -12,7 +12,6 @@ import {
12
12
  HIT_AREA_EXPAND_CLASS,
13
13
  HIT_AREA_EXPAND_DENSE_CLASS,
14
14
  PROVIDER_METADATA,
15
- Pencil,
16
15
  ProviderBrandIcon,
17
16
  Settings,
18
17
  Spinner,
@@ -25,7 +24,7 @@ import {
25
24
  useHoverInteractionSharedState,
26
25
  useHoverReferenceInteraction,
27
26
  usePopupHandleStore
28
- } from "./chunk-MIW4LJTV.js";
27
+ } from "./chunk-2JHTYWD5.js";
29
28
  import {
30
29
  UI_PREFERENCES_KEY,
31
30
  isModelPatternEnabled,
@@ -36,6 +35,9 @@ import {
36
35
  import {
37
36
  MessageSquare
38
37
  } from "./chunk-GL6HWDJB.js";
38
+ import {
39
+ Pencil
40
+ } from "./chunk-M5RSPNYN.js";
39
41
  import {
40
42
  Keyboard,
41
43
  Monitor,
@@ -54,11 +56,11 @@ import {
54
56
  } from "./chunk-OKL5ZRVB.js";
55
57
  import {
56
58
  notify
57
- } from "./chunk-WYL2DW5R.js";
59
+ } from "./chunk-JTCCQZLN.js";
58
60
  import {
59
61
  FloatingPortalLite,
60
62
  Info
61
- } from "./chunk-KOUEJ3NU.js";
63
+ } from "./chunk-UL2725KQ.js";
62
64
  import "./chunk-UCA4WN66.js";
63
65
  import {
64
66
  Eye
@@ -80,8 +82,9 @@ import {
80
82
  InputGroup,
81
83
  InputGroupAddon,
82
84
  InputGroupButton,
83
- InputGroupInput
84
- } from "./chunk-P7H2OPKF.js";
85
+ InputGroupInput,
86
+ Textarea
87
+ } from "./chunk-NJ4QEZHK.js";
85
88
  import {
86
89
  Search
87
90
  } from "./chunk-JIULUYHT.js";
@@ -102,7 +105,7 @@ import {
102
105
  FieldGroup,
103
106
  FieldLabel,
104
107
  Switch
105
- } from "./chunk-UAPNEWCA.js";
108
+ } from "./chunk-KP2PNDGL.js";
106
109
  import {
107
110
  Select,
108
111
  clamp,
@@ -114,7 +117,7 @@ import {
114
117
  useDirection,
115
118
  usePositioner,
116
119
  useRender
117
- } from "./chunk-WM4PUT6M.js";
120
+ } from "./chunk-NNKJTKOQ.js";
118
121
  import {
119
122
  isCustomProviderId,
120
123
  isNamedOccInstanceId,
@@ -123,7 +126,7 @@ import {
123
126
  import "./chunk-WWJF4RO2.js";
124
127
  import {
125
128
  Input
126
- } from "./chunk-P6UMR32I.js";
129
+ } from "./chunk-5UCAJBAH.js";
127
130
  import {
128
131
  Dialog,
129
132
  DialogBackdrop,
@@ -157,7 +160,7 @@ import {
157
160
  usePopupRootStore,
158
161
  useSurface,
159
162
  useTriggerDataForwarding
160
- } from "./chunk-AGFRJQZ7.js";
163
+ } from "./chunk-4WBFCB4O.js";
161
164
  import {
162
165
  Button,
163
166
  POPUP_COLLISION_AVOIDANCE,
@@ -178,7 +181,7 @@ import {
178
181
  ownerDocument,
179
182
  useTimeout,
180
183
  webkit
181
- } from "./chunk-LZ4Q5KUJ.js";
184
+ } from "./chunk-WNCAL4HN.js";
182
185
  import {
183
186
  Check
184
187
  } from "./chunk-4QD4PCVP.js";
@@ -186,7 +189,7 @@ import "./chunk-S3PQ6HKH.js";
186
189
  import {
187
190
  useBaseUiId,
188
191
  useValueAsRef
189
- } from "./chunk-7SR6WDM2.js";
192
+ } from "./chunk-MNBGB2SP.js";
190
193
  import {
191
194
  X
192
195
  } from "./chunk-JBLHIEVG.js";
@@ -209,8 +212,10 @@ import {
209
212
  useOpenChangeComplete,
210
213
  useRenderElement,
211
214
  useStableCallback
212
- } from "./chunk-C5X6G26O.js";
213
- import "./chunk-AHTO6GDS.js";
215
+ } from "./chunk-XIB6XNUX.js";
216
+ import {
217
+ Plug
218
+ } from "./chunk-AHTO6GDS.js";
214
219
  import "./chunk-EAJTWMK5.js";
215
220
  import "./chunk-HFQZLPU2.js";
216
221
  import {
@@ -223,7 +228,7 @@ import "./chunk-JDCCC7A7.js";
223
228
  import "./chunk-L547YHC4.js";
224
229
  import "./chunk-MLKGABMK.js";
225
230
 
226
- // web/app/dist/server/assets/settings-dialog-BvkgNflv.js
231
+ // web/app/dist/server/assets/settings-dialog-CK0FJlTg.js
227
232
  import * as React$1 from "react";
228
233
  import { useCallback as useCallback2, useEffect as useEffect2, useId, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
229
234
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -717,6 +722,46 @@ function useFocus(context, props = {}) {
717
722
  trigger: reference
718
723
  } : {}, [enabled, reference]);
719
724
  }
725
+ function Breadcrumb({ className, ...props }) {
726
+ return /* @__PURE__ */ jsx("nav", {
727
+ "aria-label": "breadcrumb",
728
+ "data-slot": "breadcrumb",
729
+ className: cn(className),
730
+ ...props
731
+ });
732
+ }
733
+ function BreadcrumbList({ className, ...props }) {
734
+ return /* @__PURE__ */ jsx("ol", {
735
+ "data-slot": "breadcrumb-list",
736
+ className: cn("flex flex-wrap items-center gap-1.5 text-sm wrap-break-word text-muted-foreground", className),
737
+ ...props
738
+ });
739
+ }
740
+ function BreadcrumbItem({ className, ...props }) {
741
+ return /* @__PURE__ */ jsx("li", {
742
+ "data-slot": "breadcrumb-item",
743
+ className: cn("inline-flex items-center gap-1", className),
744
+ ...props
745
+ });
746
+ }
747
+ function BreadcrumbPage({ className, ...props }) {
748
+ return /* @__PURE__ */ jsx("span", {
749
+ "data-slot": "breadcrumb-page",
750
+ "aria-current": "page",
751
+ className: cn("font-normal text-foreground", className),
752
+ ...props
753
+ });
754
+ }
755
+ function BreadcrumbSeparator({ children, className, ...props }) {
756
+ return /* @__PURE__ */ jsx("li", {
757
+ "data-slot": "breadcrumb-separator",
758
+ role: "presentation",
759
+ "aria-hidden": "true",
760
+ className: cn("[&>svg]:size-3.5", className),
761
+ ...props,
762
+ children: children ?? /* @__PURE__ */ jsx(ChevronRight, {})
763
+ });
764
+ }
720
765
  var ScrollAreaRootContext = /* @__PURE__ */ React$1.createContext(void 0);
721
766
  function useScrollAreaRootContext() {
722
767
  const context = React$1.useContext(ScrollAreaRootContext);
@@ -1409,46 +1454,6 @@ function ScrollBar({ className, orientation = "vertical", ...props }) {
1409
1454
  })
1410
1455
  });
1411
1456
  }
1412
- function Breadcrumb({ className, ...props }) {
1413
- return /* @__PURE__ */ jsx("nav", {
1414
- "aria-label": "breadcrumb",
1415
- "data-slot": "breadcrumb",
1416
- className: cn(className),
1417
- ...props
1418
- });
1419
- }
1420
- function BreadcrumbList({ className, ...props }) {
1421
- return /* @__PURE__ */ jsx("ol", {
1422
- "data-slot": "breadcrumb-list",
1423
- className: cn("flex flex-wrap items-center gap-1.5 text-sm wrap-break-word text-muted-foreground", className),
1424
- ...props
1425
- });
1426
- }
1427
- function BreadcrumbItem({ className, ...props }) {
1428
- return /* @__PURE__ */ jsx("li", {
1429
- "data-slot": "breadcrumb-item",
1430
- className: cn("inline-flex items-center gap-1", className),
1431
- ...props
1432
- });
1433
- }
1434
- function BreadcrumbPage({ className, ...props }) {
1435
- return /* @__PURE__ */ jsx("span", {
1436
- "data-slot": "breadcrumb-page",
1437
- "aria-current": "page",
1438
- className: cn("font-normal text-foreground", className),
1439
- ...props
1440
- });
1441
- }
1442
- function BreadcrumbSeparator({ children, className, ...props }) {
1443
- return /* @__PURE__ */ jsx("li", {
1444
- "data-slot": "breadcrumb-separator",
1445
- role: "presentation",
1446
- "aria-hidden": "true",
1447
- className: cn("[&>svg]:size-3.5", className),
1448
- ...props,
1449
- children: children ?? /* @__PURE__ */ jsx(ChevronRight, {})
1450
- });
1451
- }
1452
1457
  var MOBILE_BREAKPOINT = 768;
1453
1458
  function useIsMobile() {
1454
1459
  const [isMobile, setIsMobile] = React$1.useState(void 0);
@@ -2248,40 +2253,42 @@ function SidebarMenuButton({ render, isActive = false, variant = "default", size
2248
2253
  ...tooltip
2249
2254
  })] });
2250
2255
  }
2251
- var THEME_OPTIONS = [
2252
- {
2253
- value: "system",
2254
- label: "System",
2255
- icon: Monitor
2256
- },
2257
- {
2258
- value: "light",
2259
- label: "Light",
2260
- icon: Sun
2261
- },
2262
- {
2263
- value: "dark",
2264
- label: "Dark",
2265
- icon: Moon
2266
- }
2267
- ];
2268
- function PersonalizationSection({ onThemePreferenceChange, themePreference }) {
2269
- return /* @__PURE__ */ jsxs(Field, {
2270
- orientation: "horizontal",
2271
- className: "items-center justify-between gap-6 rounded-xl border p-4",
2272
- children: [/* @__PURE__ */ jsxs("div", {
2273
- className: "min-w-0",
2274
- children: [/* @__PURE__ */ jsx(FieldLabel, { children: "Theme" }), /* @__PURE__ */ jsx(FieldDescription, { children: "Match the system theme or pick light or dark." })]
2275
- }), /* @__PURE__ */ jsx("div", {
2276
- className: "shrink-0",
2277
- children: /* @__PURE__ */ jsx(Select, {
2278
- "aria-label": "Theme",
2279
- className: "w-[160px]",
2280
- onValueChange: (value) => onThemePreferenceChange(value),
2281
- options: [...THEME_OPTIONS],
2282
- value: themePreference
2283
- })
2284
- })]
2256
+ function RowSurface({ className, interactive, padding, tone, ...props }) {
2257
+ return /* @__PURE__ */ jsx("div", {
2258
+ className: cn(fleetPiRowSurface({
2259
+ interactive,
2260
+ padding,
2261
+ tone
2262
+ }), className),
2263
+ ...props
2264
+ });
2265
+ }
2266
+ function ItemRow({ icon, title, subtitle, trailing, className, interactive = true, tone = "default" }) {
2267
+ return /* @__PURE__ */ jsxs(RowSurface, {
2268
+ tone,
2269
+ padding: "md",
2270
+ interactive,
2271
+ className: cn("items-center gap-3", className),
2272
+ children: [
2273
+ icon ? /* @__PURE__ */ jsx("div", {
2274
+ className: "flex size-8 shrink-0 items-center justify-center rounded-md border border-border/40 bg-background/60 text-foreground/70",
2275
+ children: icon
2276
+ }) : null,
2277
+ /* @__PURE__ */ jsxs("div", {
2278
+ className: "min-w-0 flex-1",
2279
+ children: [/* @__PURE__ */ jsx("div", {
2280
+ className: "truncate text-sm font-medium text-foreground",
2281
+ children: title
2282
+ }), subtitle ? /* @__PURE__ */ jsx("div", {
2283
+ className: "mt-0.5 truncate text-xs text-muted-foreground",
2284
+ children: subtitle
2285
+ }) : null]
2286
+ }),
2287
+ trailing ? /* @__PURE__ */ jsx("div", {
2288
+ className: "flex shrink-0 items-center gap-1.5",
2289
+ children: trailing
2290
+ }) : null
2291
+ ]
2285
2292
  });
2286
2293
  }
2287
2294
  function SettingsPane({ actions, children, description, title }) {
@@ -2331,444 +2338,62 @@ function SettingsCommitActions({ dirty, disabled: disabled2, onRevert, onSave, s
2331
2338
  }) : /* @__PURE__ */ jsx(Save, { "data-icon": "inline-start" }), "Commit"]
2332
2339
  })] });
2333
2340
  }
2334
- function isFieldInvalid(value, attemptedSave, required = true) {
2335
- return attemptedSave && required && value.trim().length === 0;
2336
- }
2337
- function SecretCredentialField({ attemptedSave, label, onChange, onToggleVisibility, placeholder, required = true, showPassword, value }) {
2338
- const controlId = useId();
2339
- const invalid = isFieldInvalid(value, attemptedSave, required);
2340
- return /* @__PURE__ */ jsxs(Field, {
2341
- "data-invalid": invalid || void 0,
2342
- children: [/* @__PURE__ */ jsx(FieldLabel, {
2343
- htmlFor: controlId,
2344
- children: label
2345
- }), /* @__PURE__ */ jsxs(InputGroup, { children: [
2346
- /* @__PURE__ */ jsx(InputGroupAddon, {
2347
- align: "inline-start",
2348
- children: /* @__PURE__ */ jsx(Lock, {})
2349
- }),
2350
- /* @__PURE__ */ jsx(InputGroupInput, {
2351
- id: controlId,
2352
- type: showPassword ? "text" : "password",
2353
- placeholder,
2354
- value,
2355
- onChange: (event) => onChange(event.target.value),
2356
- "aria-label": label,
2357
- "aria-invalid": invalid || void 0
2358
- }),
2359
- /* @__PURE__ */ jsx(InputGroupAddon, {
2360
- align: "inline-end",
2361
- children: /* @__PURE__ */ jsx(InputGroupButton, {
2362
- size: "icon-xs",
2363
- className: cn(HIT_AREA_EXPAND_CLASS, "transition-[background-color,transform] duration-150 active:scale-[0.96]"),
2364
- "aria-label": showPassword ? "Hide value" : "Show value",
2365
- onClick: onToggleVisibility,
2366
- children: showPassword ? /* @__PURE__ */ jsx(EyeOff, {}) : /* @__PURE__ */ jsx(Eye, {})
2367
- })
2368
- })
2369
- ] })]
2370
- });
2371
- }
2372
- function TextCredentialField({ attemptedSave, icon: Icon, inputType = "text", label, onChange, placeholder, required = true, value }) {
2373
- const controlId = useId();
2374
- const invalid = isFieldInvalid(value, attemptedSave, required);
2375
- return /* @__PURE__ */ jsxs(Field, {
2376
- "data-invalid": invalid || void 0,
2377
- children: [/* @__PURE__ */ jsx(FieldLabel, {
2378
- htmlFor: controlId,
2379
- children: label
2380
- }), /* @__PURE__ */ jsxs(InputGroup, { children: [/* @__PURE__ */ jsx(InputGroupAddon, {
2381
- align: "inline-start",
2382
- children: /* @__PURE__ */ jsx(Icon, {})
2383
- }), /* @__PURE__ */ jsx(InputGroupInput, {
2384
- id: controlId,
2385
- type: inputType,
2386
- placeholder,
2387
- value,
2388
- onChange: (event) => onChange(event.target.value),
2389
- "aria-label": label,
2390
- "aria-invalid": invalid || void 0
2391
- })] })]
2341
+ var OAUTH_POLL_MS = 1500;
2342
+ var STALE_LOGIN_ERROR = "Unknown or expired OAuth login";
2343
+ function delay(ms) {
2344
+ return new Promise((resolve) => {
2345
+ setTimeout(resolve, ms);
2392
2346
  });
2393
2347
  }
2394
- var OPENAI_CHAT_BASE_URL_PLACEHOLDER = "https://opencode.ai/zen/v1";
2395
- var OPENAI_CHAT_MODEL_PLACEHOLDER = "qwen35-122b-a10b";
2396
- var OPENAI_CHAT_NAME_PLACEHOLDER = "e.g. OpenCode Zen, Nebius, Groq\u2026";
2397
- var CUSTOM_MODELS_PLACEHOLDER = "model-a, model-b (comma-separated)";
2398
- var CUSTOM_PROVIDER_API_OPTIONS = [
2399
- {
2400
- value: "openai-completions",
2401
- label: "OpenAI Chat Completions"
2402
- },
2403
- {
2404
- value: "openai-responses",
2405
- label: "OpenAI Responses"
2406
- },
2407
- {
2408
- value: "anthropic-messages",
2409
- label: "Anthropic Messages"
2410
- },
2411
- {
2412
- value: "google-genai",
2413
- label: "Google Generative AI"
2414
- }
2415
- ];
2416
- function ProviderCredentialFields({ attemptedSave, apiKey, api, baseUrl, modelId, models, displayName, isCustom, onApiKeyChange, onApiChange, onBaseUrlChange, onModelIdChange, onModelsChange, onDisplayNameChange, onTogglePassword, openAiChat, placeholder, showPassword }) {
2417
- const showApiSelector = isCustom && onApiChange;
2418
- const showMultiModels = isCustom && onModelsChange;
2419
- return /* @__PURE__ */ jsxs(FieldGroup, {
2420
- className: "gap-2",
2421
- children: [
2422
- (openAiChat || isCustom) && onDisplayNameChange ? /* @__PURE__ */ jsx(TextCredentialField, {
2423
- attemptedSave,
2424
- icon: Tag,
2425
- label: "Provider name",
2426
- placeholder: OPENAI_CHAT_NAME_PLACEHOLDER,
2427
- value: displayName ?? "",
2428
- onChange: onDisplayNameChange
2429
- }) : null,
2430
- showApiSelector ? /* @__PURE__ */ jsx(ApiSelector, {
2431
- value: api ?? "openai-completions",
2432
- onChange: onApiChange
2433
- }) : null,
2434
- /* @__PURE__ */ jsx(SecretCredentialField, {
2435
- attemptedSave,
2436
- label: "API key",
2437
- placeholder,
2438
- value: apiKey,
2439
- showPassword,
2440
- onChange: onApiKeyChange,
2441
- onToggleVisibility: onTogglePassword
2442
- }),
2443
- openAiChat || isCustom ? /* @__PURE__ */ jsx(TextCredentialField, {
2444
- attemptedSave,
2445
- icon: Globe,
2446
- inputType: "url",
2447
- label: "Base URL",
2448
- placeholder: OPENAI_CHAT_BASE_URL_PLACEHOLDER,
2449
- value: baseUrl,
2450
- onChange: onBaseUrlChange
2451
- }) : null,
2452
- showMultiModels ? /* @__PURE__ */ jsx(TextCredentialField, {
2453
- attemptedSave,
2454
- icon: Layers,
2455
- label: "Model ids",
2456
- placeholder: CUSTOM_MODELS_PLACEHOLDER,
2457
- value: models ?? "",
2458
- onChange: onModelsChange
2459
- }) : null,
2460
- openAiChat && !showMultiModels ? /* @__PURE__ */ jsx(TextCredentialField, {
2461
- attemptedSave,
2462
- icon: Box,
2463
- label: "Model name",
2464
- placeholder: OPENAI_CHAT_MODEL_PLACEHOLDER,
2465
- value: modelId,
2466
- onChange: onModelIdChange
2467
- }) : null
2468
- ]
2469
- });
2470
- }
2471
- function ApiSelector({ value, onChange }) {
2472
- return /* @__PURE__ */ jsxs("div", {
2473
- className: "flex flex-col gap-1",
2474
- children: [/* @__PURE__ */ jsx("span", {
2475
- className: "text-xs font-medium text-muted-foreground",
2476
- children: "API family"
2477
- }), /* @__PURE__ */ jsx(Select, {
2478
- "aria-label": "API family",
2479
- value,
2480
- options: CUSTOM_PROVIDER_API_OPTIONS,
2481
- placeholder: "Select API family",
2482
- onValueChange: (next) => onChange(next)
2483
- })]
2484
- });
2485
- }
2486
- function isOAuthProvider(provider) {
2487
- return provider.authType === "oauth";
2488
- }
2489
- function supportsOAuth(provider) {
2490
- return provider.supportsOAuth === true;
2491
- }
2492
- var CUSTOM_PROVIDER_PICKER_ROW = {
2493
- id: "custom",
2494
- name: "Custom provider",
2495
- isConfigured: false,
2496
- envVarName: "CUSTOM_PROVIDER_API_KEY",
2497
- displayName: "Custom provider"
2498
- };
2499
- function parseCustomProviderModels(raw) {
2500
- return raw.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
2501
- }
2502
- function useProviderCredentialsController({ onRemoveProvider, onUpdateProvider, providers }) {
2503
- const [editingProvider, setEditingProvider] = useState2(null);
2504
- const [editingIsNewOccInstance, setEditingIsNewOccInstance] = useState2(false);
2505
- const [apiKey, setApiKey] = useState2("");
2506
- const [baseUrl, setBaseUrl] = useState2("");
2507
- const [modelId, setModelId] = useState2("");
2508
- const [models, setModels] = useState2("");
2509
- const [api, setApi] = useState2("openai-completions");
2510
- const [displayName, setDisplayName] = useState2("");
2511
- const [showPassword, setShowPassword] = useState2(false);
2512
- const [attemptedSave, setAttemptedSave] = useState2(false);
2513
- const [searchQuery, setSearchQuery] = useState2("");
2514
- const [addPickerOpen, setAddPickerOpen] = useState2(false);
2515
- const [addPickerQuery, setAddPickerQuery] = useState2("");
2516
- const [confirmRemoveProvider, setConfirmRemoveProvider] = useState2(null);
2517
- const credentialProviders = useMemo2(() => {
2518
- return [...providers.filter((provider) => provider.id !== CUSTOM_PROVIDER_PICKER_ROW.id), CUSTOM_PROVIDER_PICKER_ROW];
2519
- }, [providers]);
2520
- const activeProviders = useMemo2(() => credentialProviders.filter((provider) => provider.isConfigured), [credentialProviders]);
2521
- const unconfiguredProviders = useMemo2(() => credentialProviders.filter((provider) => !provider.isConfigured || provider.id === "openai-chat-completions" && provider.providerFamily === void 0), [credentialProviders]);
2522
- const filteredActiveProviders = useMemo2(() => {
2523
- const query = searchQuery.trim().toLowerCase();
2524
- if (!query) return activeProviders;
2525
- return activeProviders.filter((provider) => providerMatchesQuery(provider, query));
2526
- }, [activeProviders, searchQuery]);
2527
- const filteredPickerAvailable = useMemo2(() => {
2528
- const query = addPickerQuery.trim().toLowerCase();
2529
- return unconfiguredProviders.filter((provider) => providerMatchesQuery(provider, query));
2530
- }, [addPickerQuery, unconfiguredProviders]);
2531
- const filteredPickerConfigured = useMemo2(() => {
2532
- const query = addPickerQuery.trim().toLowerCase();
2533
- return activeProviders.filter((provider) => providerMatchesQuery(provider, query));
2534
- }, [activeProviders, addPickerQuery]);
2535
- const pickerHasResults = filteredPickerAvailable.length > 0 || filteredPickerConfigured.length > 0;
2536
- const editingUnconfiguredProvider = editingProvider && !activeProviders.some((provider) => provider.id === editingProvider) ? credentialProviders.find((entry) => entry.id === editingProvider) ?? null : null;
2537
- const resetEditor = () => {
2538
- setApiKey("");
2539
- setBaseUrl("");
2540
- setModelId("");
2541
- setModels("");
2542
- setApi("openai-completions");
2543
- setDisplayName("");
2544
- setShowPassword(false);
2545
- setAttemptedSave(false);
2546
- };
2547
- const closeEditor = () => {
2548
- setEditingProvider(null);
2549
- setEditingIsNewOccInstance(false);
2550
- resetEditor();
2551
- };
2552
- const openEditor = (providerId, opts) => {
2553
- setAddPickerOpen(false);
2554
- setAddPickerQuery("");
2555
- setEditingProvider(providerId);
2556
- setEditingIsNewOccInstance(opts?.newOccInstance === true);
2557
- resetEditor();
2558
- const existing = providers.find((provider) => provider.id === providerId);
2559
- setBaseUrl(existing?.baseUrl ?? "");
2560
- setDisplayName(existing?.displayName ?? (isCustomProviderId(providerId) ? existing?.name ?? "" : ""));
2561
- setApi(existing?.api ?? "openai-completions");
2562
- setModels((existing?.modelIds ?? []).join(", "));
2563
- setModelId(existing?.modelIds?.[0] ?? "");
2564
- };
2565
- const openAddPicker = () => {
2566
- closeEditor();
2567
- setAddPickerQuery("");
2568
- setAddPickerOpen(true);
2569
- };
2570
- const closeAddPicker = () => {
2571
- setAddPickerOpen(false);
2572
- setAddPickerQuery("");
2573
- };
2574
- const selectProviderFromPicker = (providerId) => {
2575
- openEditor(providerId, { newOccInstance: isOccProviderId(providerId) });
2576
- };
2577
- const handleSave = async (providerId) => {
2578
- if (!onUpdateProvider) return;
2579
- setAttemptedSave(true);
2580
- const isCustomEditor = providerId === "custom" || isCustomProviderId(providerId);
2581
- const openAiChat = isOccProviderId(providerId);
2582
- if (isCustomEditor) {
2583
- const modelList = parseCustomProviderModels(models);
2584
- if (!apiKey.trim() || !baseUrl.trim() || !displayName.trim()) return;
2585
- if (modelList.length === 0) return;
2586
- try {
2587
- await onUpdateProvider({
2588
- providerId,
2589
- apiKey: apiKey.trim(),
2590
- baseUrl: baseUrl.trim(),
2591
- displayName: displayName.trim(),
2592
- api,
2593
- models: modelList,
2594
- ...providerId === "custom" ? { createOccInstance: true } : {}
2595
- });
2596
- notify.success("Provider credentials applied to your active sessions.");
2597
- closeEditor();
2598
- closeAddPicker();
2599
- } catch (error) {
2600
- notify.error(error instanceof Error ? error.message : "Failed to update provider");
2601
- }
2602
- return;
2603
- }
2604
- const isNamedOcc = openAiChat && isNamedOccInstanceId(providerId);
2605
- const isOccLike = openAiChat && (isNamedOcc || editingIsNewOccInstance);
2606
- if (!apiKey.trim()) return;
2607
- if (openAiChat && (!baseUrl.trim() || !modelId.trim())) return;
2608
- if (isOccLike && !displayName.trim()) return;
2348
+ function useOAuthLoginFlow({ onConfigured, onOAuthLogin, providerId }) {
2349
+ const [login, setLogin] = useState2(null);
2350
+ const [busy, setBusy] = useState2(false);
2351
+ const [promptAnswer, setPromptAnswer] = useState2("");
2352
+ const loginIdRef = useRef2(null);
2353
+ const generationRef = useRef2(0);
2354
+ const startingRef = useRef2(false);
2355
+ const onOAuthLoginRef = useRef2(onOAuthLogin);
2356
+ const onConfiguredRef = useRef2(onConfigured);
2357
+ const providerIdRef = useRef2(providerId);
2358
+ useEffect2(() => {
2359
+ onOAuthLoginRef.current = onOAuthLogin;
2360
+ onConfiguredRef.current = onConfigured;
2361
+ providerIdRef.current = providerId;
2362
+ }, [
2363
+ onConfigured,
2364
+ onOAuthLogin,
2365
+ providerId
2366
+ ]);
2367
+ const cancelLoginId = useCallback2(async (loginId) => {
2368
+ if (!loginId || !onOAuthLoginRef.current) return;
2609
2369
  try {
2610
- await onUpdateProvider({
2611
- providerId,
2612
- apiKey: apiKey.trim(),
2613
- ...openAiChat ? {
2614
- baseUrl: baseUrl.trim(),
2615
- modelId: modelId.trim(),
2616
- ...isOccLike ? { displayName: displayName.trim() } : {},
2617
- ...editingIsNewOccInstance ? { createOccInstance: true } : {}
2618
- } : {}
2370
+ await onOAuthLoginRef.current({
2371
+ providerId: providerIdRef.current,
2372
+ loginId,
2373
+ cancel: true
2619
2374
  });
2620
- notify.success("Provider credentials applied to your active sessions.");
2621
- closeEditor();
2622
- closeAddPicker();
2623
- } catch (error) {
2624
- notify.error(error instanceof Error ? error.message : "Failed to update provider");
2375
+ } catch {
2625
2376
  }
2626
- };
2627
- const handleRemove = async (provider) => {
2628
- if (!onRemoveProvider) return;
2629
- try {
2630
- await onRemoveProvider({ providerId: provider.id });
2631
- if (editingProvider === provider.id) closeEditor();
2632
- notify.success(`${provider.name} removed from your configured providers.`);
2633
- setConfirmRemoveProvider(null);
2634
- } catch (error) {
2635
- notify.error(error instanceof Error ? error.message : "Failed to remove provider");
2636
- }
2637
- };
2638
- return {
2639
- activeProviders,
2640
- addPickerOpen,
2641
- addPickerQuery,
2642
- api,
2643
- apiKey,
2644
- attemptedSave,
2645
- baseUrl,
2646
- canSave: useMemo2(() => {
2647
- if (apiKey.trim().length === 0) return false;
2648
- if (!editingProvider) return true;
2649
- if (editingProvider === "custom" || isCustomProviderId(editingProvider)) {
2650
- if (baseUrl.trim().length === 0) return false;
2651
- if (parseCustomProviderModels(models).length === 0) return false;
2652
- return displayName.trim().length > 0;
2653
- }
2654
- if (!isOccProviderId(editingProvider)) return true;
2655
- if (baseUrl.trim().length === 0 || modelId.trim().length === 0) return false;
2656
- if (isNamedOccInstanceId(editingProvider) || editingIsNewOccInstance) return displayName.trim().length > 0;
2657
- return true;
2658
- }, [
2659
- apiKey,
2660
- baseUrl,
2661
- modelId,
2662
- models,
2663
- displayName,
2664
- editingProvider,
2665
- editingIsNewOccInstance
2666
- ]),
2667
- closeAddPicker,
2668
- closeEditor,
2669
- confirmRemoveProvider,
2670
- credentialProviders,
2671
- displayName,
2672
- editingIsNewOccInstance,
2673
- editingProvider,
2674
- editingUnconfiguredProvider,
2675
- filteredActiveProviders,
2676
- filteredPickerAvailable,
2677
- filteredPickerConfigured,
2678
- handleRemove,
2679
- handleSave,
2680
- modelId,
2681
- models,
2682
- openAddPicker,
2683
- openEditor,
2684
- pickerHasResults,
2685
- resetEditor,
2686
- searchQuery,
2687
- selectProviderFromPicker,
2688
- setAddPickerOpen,
2689
- setAddPickerQuery,
2690
- setApi,
2691
- setApiKey,
2692
- setAttemptedSave,
2693
- setBaseUrl,
2694
- setConfirmRemoveProvider,
2695
- setDisplayName,
2696
- setEditingIsNewOccInstance,
2697
- setEditingProvider,
2698
- setModelId,
2699
- setModels,
2700
- setSearchQuery,
2701
- setShowPassword,
2702
- showPassword,
2703
- unconfiguredProviders
2704
- };
2705
- }
2706
- function providerMatchesQuery(provider, query) {
2707
- if (!query) return true;
2708
- const keywords = supportsOAuth(provider) ? isOAuthProvider(provider) ? "oauth sign-in login" : "api key oauth sign-in login" : provider.id === "custom" || isCustomProviderId(provider.id) ? "custom provider endpoint openai anthropic google compatible" : isOccProviderId(provider.id) ? "api key base url model name chat completions" : "";
2709
- return [
2710
- provider.name,
2711
- provider.envVarName,
2712
- provider.id,
2713
- keywords
2714
- ].join(" ").toLowerCase().includes(query);
2715
- }
2716
- var OAUTH_POLL_MS = 1500;
2717
- var STALE_LOGIN_ERROR = "Unknown or expired OAuth login";
2718
- function delay(ms) {
2719
- return new Promise((resolve) => {
2720
- setTimeout(resolve, ms);
2721
- });
2722
- }
2723
- function useOAuthLoginFlow({ onConfigured, onOAuthLogin, providerId }) {
2724
- const [login, setLogin] = useState2(null);
2725
- const [busy, setBusy] = useState2(false);
2726
- const [promptAnswer, setPromptAnswer] = useState2("");
2727
- const loginIdRef = useRef2(null);
2728
- const generationRef = useRef2(0);
2729
- const startingRef = useRef2(false);
2730
- const onOAuthLoginRef = useRef2(onOAuthLogin);
2731
- const onConfiguredRef = useRef2(onConfigured);
2732
- const providerIdRef = useRef2(providerId);
2733
- useEffect2(() => {
2734
- onOAuthLoginRef.current = onOAuthLogin;
2735
- onConfiguredRef.current = onConfigured;
2736
- providerIdRef.current = providerId;
2737
- }, [
2738
- onConfigured,
2739
- onOAuthLogin,
2740
- providerId
2741
- ]);
2742
- const cancelLoginId = useCallback2(async (loginId) => {
2743
- if (!loginId || !onOAuthLoginRef.current) return;
2744
- try {
2745
- await onOAuthLoginRef.current({
2746
- providerId: providerIdRef.current,
2747
- loginId,
2748
- cancel: true
2749
- });
2750
- } catch {
2751
- }
2752
- }, []);
2753
- const resetLocal = useCallback2(() => {
2754
- loginIdRef.current = null;
2755
- startingRef.current = false;
2756
- setLogin(null);
2757
- setPromptAnswer("");
2758
- setBusy(false);
2759
- }, []);
2760
- useEffect2(() => {
2761
- return () => {
2762
- generationRef.current += 1;
2763
- const loginId = loginIdRef.current;
2764
- loginIdRef.current = null;
2765
- cancelLoginId(loginId);
2766
- };
2767
- }, [cancelLoginId]);
2768
- const applyResult = useCallback2((result, generation) => {
2769
- if (generation !== generationRef.current) {
2770
- if (result.status === "waiting" && result.loginId) cancelLoginId(result.loginId);
2771
- return;
2377
+ }, []);
2378
+ const resetLocal = useCallback2(() => {
2379
+ loginIdRef.current = null;
2380
+ startingRef.current = false;
2381
+ setLogin(null);
2382
+ setPromptAnswer("");
2383
+ setBusy(false);
2384
+ }, []);
2385
+ useEffect2(() => {
2386
+ return () => {
2387
+ generationRef.current += 1;
2388
+ const loginId = loginIdRef.current;
2389
+ loginIdRef.current = null;
2390
+ cancelLoginId(loginId);
2391
+ };
2392
+ }, [cancelLoginId]);
2393
+ const applyResult = useCallback2((result, generation) => {
2394
+ if (generation !== generationRef.current) {
2395
+ if (result.status === "waiting" && result.loginId) cancelLoginId(result.loginId);
2396
+ return;
2772
2397
  }
2773
2398
  if (result.status === "success") {
2774
2399
  loginIdRef.current = null;
@@ -2928,14 +2553,10 @@ function ProviderOAuthSignIn({ onConfigured, onOAuthLogin, provider }) {
2928
2553
  }),
2929
2554
  userCode ? /* @__PURE__ */ jsxs("p", {
2930
2555
  className: "text-sm",
2931
- children: [
2932
- "Enter this code:",
2933
- " ",
2934
- /* @__PURE__ */ jsx("span", {
2935
- className: "font-mono text-foreground",
2936
- children: userCode
2937
- })
2938
- ]
2556
+ children: ["Enter this code: ", /* @__PURE__ */ jsx("span", {
2557
+ className: "font-mono text-foreground",
2558
+ children: userCode
2559
+ })]
2939
2560
  }) : null,
2940
2561
  instructions && !userCode ? /* @__PURE__ */ jsx("p", {
2941
2562
  className: "text-xs text-pretty text-muted-foreground",
@@ -3023,554 +2644,336 @@ function ProviderOAuthSignIn({ onConfigured, onOAuthLogin, provider }) {
3023
2644
  })]
3024
2645
  });
3025
2646
  }
3026
- function AddProviderPickerPanel({ addPickerQuery, credentialProvidersLength, filteredPickerAvailable, filteredPickerConfigured, pickerHasResults, onClose, onQueryChange, onSelect }) {
3027
- return /* @__PURE__ */ jsxs("div", {
3028
- className: "flex flex-col gap-3",
2647
+ function statusLabel(connection) {
2648
+ switch (connection.status) {
2649
+ case "connected":
2650
+ return "Connected";
2651
+ case "anonymous":
2652
+ return "Available without sign-in";
2653
+ case "needs_auth":
2654
+ return "Sign-in required";
2655
+ case "disabled":
2656
+ return "Disabled";
2657
+ default:
2658
+ return connection.status;
2659
+ }
2660
+ }
2661
+ function connectionSubtitle(connection) {
2662
+ const details = connection.transport === "http" ? connection.url : connection.commandPreview;
2663
+ return [
2664
+ connection.source === "builtin" ? "Built-in" : "User",
2665
+ connection.transport === "http" ? "HTTP" : "stdio",
2666
+ statusLabel(connection),
2667
+ details
2668
+ ].filter((part) => Boolean(part)).join(" \xB7 ");
2669
+ }
2670
+ function McpConnectionsSection({ connections, isLoading, isPending, onOAuth, onRemove, onUpsert }) {
2671
+ const [adding, setAdding] = useState2(false);
2672
+ const [transport, setTransport] = useState2("http");
2673
+ const [name, setName] = useState2("");
2674
+ const [url, setUrl] = useState2("");
2675
+ const [oauth, setOauth] = useState2(true);
2676
+ const [bearerTokenEnvVar, setBearerTokenEnvVar] = useState2("");
2677
+ const [command, setCommand] = useState2("");
2678
+ const [args, setArgs] = useState2("");
2679
+ const [cwd, setCwd] = useState2("");
2680
+ const [envLines, setEnvLines] = useState2("");
2681
+ const [confirmRemove, setConfirmRemove] = useState2(null);
2682
+ const resetAddForm = () => {
2683
+ setName("");
2684
+ setUrl("");
2685
+ setOauth(true);
2686
+ setBearerTokenEnvVar("");
2687
+ setCommand("");
2688
+ setArgs("");
2689
+ setCwd("");
2690
+ setEnvLines("");
2691
+ setTransport("http");
2692
+ setAdding(false);
2693
+ };
2694
+ const handleAdd = async () => {
2695
+ if (!onUpsert) return;
2696
+ const trimmedName = name.trim();
2697
+ try {
2698
+ if (transport === "http") await onUpsert({
2699
+ name: trimmedName,
2700
+ transport: "http",
2701
+ url: url.trim(),
2702
+ ...oauth ? { oauth: true } : {},
2703
+ ...bearerTokenEnvVar.trim() ? { bearerTokenEnvVar: bearerTokenEnvVar.trim() } : {},
2704
+ force: true
2705
+ });
2706
+ else {
2707
+ const parsedArgs = args.split(/\s+/).map((part) => part.trim()).filter(Boolean);
2708
+ const env = envLines.split("\n").map((line) => line.trim()).filter(Boolean).flatMap((line) => {
2709
+ const equals = line.indexOf("=");
2710
+ if (equals <= 0) return [];
2711
+ const child = line.slice(0, equals).trim();
2712
+ const source = line.slice(equals + 1).trim();
2713
+ if (!child || !source) return [];
2714
+ return [{
2715
+ child,
2716
+ source
2717
+ }];
2718
+ });
2719
+ await onUpsert({
2720
+ name: trimmedName,
2721
+ transport: "stdio",
2722
+ command: command.trim(),
2723
+ ...parsedArgs.length > 0 ? { args: parsedArgs } : {},
2724
+ ...cwd.trim() ? { cwd: cwd.trim() } : {},
2725
+ ...env.length > 0 ? { env } : {},
2726
+ force: true
2727
+ });
2728
+ }
2729
+ notify.success("MCP connection saved");
2730
+ resetAddForm();
2731
+ } catch (error) {
2732
+ notify.error(error instanceof Error ? error.message : "Could not save MCP connection");
2733
+ }
2734
+ };
2735
+ const handleRemove = async (connectionName) => {
2736
+ if (!onRemove) return;
2737
+ try {
2738
+ await onRemove({ name: connectionName });
2739
+ notify.success(`Removed ${connectionName}`);
2740
+ setConfirmRemove(null);
2741
+ } catch (error) {
2742
+ notify.error(error instanceof Error ? error.message : "Could not remove MCP connection");
2743
+ }
2744
+ };
2745
+ const handleLogout = async (connectionName) => {
2746
+ if (!onOAuth) return;
2747
+ try {
2748
+ await onOAuth({
2749
+ name: connectionName,
2750
+ action: "logout"
2751
+ });
2752
+ notify.success(`Signed out of ${connectionName}`);
2753
+ } catch (error) {
2754
+ notify.error(error instanceof Error ? error.message : "Could not sign out");
2755
+ }
2756
+ };
2757
+ const oauthAdapter = useMemo2(() => {
2758
+ if (!onOAuth) return void 0;
2759
+ return (connection) => async (request) => {
2760
+ const starting = !request.loginId && !request.cancel;
2761
+ return await onOAuth({
2762
+ name: connection.name,
2763
+ action: starting && connection.status === "connected" ? "reconnect" : "login",
2764
+ loginId: request.loginId,
2765
+ promptAnswer: request.promptAnswer,
2766
+ cancel: request.cancel
2767
+ });
2768
+ };
2769
+ }, [onOAuth]);
2770
+ return /* @__PURE__ */ jsxs(SettingsPane, {
2771
+ title: "MCP",
2772
+ description: "Connect built-in and user MCP servers. Tokens stay on the server; this list only shows status and environment-variable names.",
2773
+ actions: /* @__PURE__ */ jsxs(Button, {
2774
+ type: "button",
2775
+ size: "sm",
2776
+ variant: "outline",
2777
+ disabled: isPending || adding,
2778
+ onClick: () => setAdding(true),
2779
+ children: [/* @__PURE__ */ jsx(Plus, { "data-icon": "inline-start" }), "Add server"]
2780
+ }),
3029
2781
  children: [
3030
- /* @__PURE__ */ jsxs("div", {
3031
- className: "flex items-center gap-2",
3032
- children: [/* @__PURE__ */ jsx(Button, {
3033
- type: "button",
3034
- variant: "ghost",
3035
- size: "icon-sm",
3036
- "aria-label": "Back to providers",
3037
- onClick: onClose,
3038
- children: /* @__PURE__ */ jsx(ArrowLeft, {})
3039
- }), /* @__PURE__ */ jsxs("div", {
3040
- className: "min-w-0 flex-1",
3041
- children: [/* @__PURE__ */ jsx("p", {
3042
- className: "text-sm font-medium",
3043
- children: "Add provider"
3044
- }), /* @__PURE__ */ jsx("p", {
3045
- className: "text-xs text-pretty text-muted-foreground",
3046
- children: "Choose a provider to store encrypted credentials for this account."
3047
- })]
3048
- })]
3049
- }),
3050
- /* @__PURE__ */ jsxs(InputGroup, { children: [/* @__PURE__ */ jsx(InputGroupAddon, {
3051
- align: "inline-start",
3052
- children: /* @__PURE__ */ jsx(Search, {})
3053
- }), /* @__PURE__ */ jsx(InputGroupInput, {
3054
- type: "text",
3055
- placeholder: "Search providers\u2026",
3056
- value: addPickerQuery,
3057
- onChange: (event) => onQueryChange(event.target.value),
3058
- "aria-label": "Search providers to add"
3059
- })] }),
2782
+ isLoading ? /* @__PURE__ */ jsxs("div", {
2783
+ className: "flex items-center gap-2 text-sm text-muted-foreground",
2784
+ children: [/* @__PURE__ */ jsx(Spinner, { className: "size-3.5" }), "Loading MCP connections\u2026"]
2785
+ }) : null,
2786
+ adding ? /* @__PURE__ */ jsxs("div", {
2787
+ className: "flex flex-col gap-3 rounded-xl border p-4",
2788
+ children: [
2789
+ /* @__PURE__ */ jsxs(Field, { children: [/* @__PURE__ */ jsx(FieldLabel, { children: "Name" }), /* @__PURE__ */ jsx(Input, {
2790
+ value: name,
2791
+ onChange: (event) => setName(event.target.value),
2792
+ "aria-label": "MCP server name"
2793
+ })] }),
2794
+ /* @__PURE__ */ jsxs(Field, { children: [/* @__PURE__ */ jsx(FieldLabel, { children: "Transport" }), /* @__PURE__ */ jsx(Select, {
2795
+ value: transport,
2796
+ onValueChange: (value) => setTransport(value),
2797
+ options: [{
2798
+ label: "HTTP",
2799
+ value: "http"
2800
+ }, {
2801
+ label: "stdio",
2802
+ value: "stdio"
2803
+ }]
2804
+ })] }),
2805
+ transport === "http" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
2806
+ /* @__PURE__ */ jsxs(Field, { children: [/* @__PURE__ */ jsx(FieldLabel, { children: "URL" }), /* @__PURE__ */ jsx(Input, {
2807
+ value: url,
2808
+ onChange: (event) => setUrl(event.target.value),
2809
+ "aria-label": "MCP server URL"
2810
+ })] }),
2811
+ /* @__PURE__ */ jsxs(Field, {
2812
+ orientation: "horizontal",
2813
+ className: "items-center justify-between",
2814
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(FieldLabel, { children: "OAuth" }), /* @__PURE__ */ jsx(FieldDescription, { children: "Use the browser sign-in flow for this server." })] }), /* @__PURE__ */ jsx(Switch, {
2815
+ checked: oauth,
2816
+ onCheckedChange: (checked) => setOauth(checked),
2817
+ "aria-label": "Use OAuth"
2818
+ })]
2819
+ }),
2820
+ !oauth ? /* @__PURE__ */ jsxs(Field, { children: [/* @__PURE__ */ jsx(FieldLabel, { children: "Bearer token environment variable" }), /* @__PURE__ */ jsx(Input, {
2821
+ value: bearerTokenEnvVar,
2822
+ onChange: (event) => setBearerTokenEnvVar(event.target.value),
2823
+ "aria-label": "Bearer token environment variable",
2824
+ placeholder: "MCP_BEARER_TOKEN"
2825
+ })] }) : null
2826
+ ] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
2827
+ /* @__PURE__ */ jsxs(Field, { children: [/* @__PURE__ */ jsx(FieldLabel, { children: "Command" }), /* @__PURE__ */ jsx(Input, {
2828
+ value: command,
2829
+ onChange: (event) => setCommand(event.target.value),
2830
+ "aria-label": "MCP command"
2831
+ })] }),
2832
+ /* @__PURE__ */ jsxs(Field, { children: [/* @__PURE__ */ jsx(FieldLabel, { children: "Arguments" }), /* @__PURE__ */ jsx(Input, {
2833
+ value: args,
2834
+ onChange: (event) => setArgs(event.target.value),
2835
+ "aria-label": "MCP command arguments"
2836
+ })] }),
2837
+ /* @__PURE__ */ jsxs(Field, { children: [
2838
+ /* @__PURE__ */ jsx(FieldLabel, { children: "Working directory" }),
2839
+ /* @__PURE__ */ jsx(Input, {
2840
+ value: cwd,
2841
+ onChange: (event) => setCwd(event.target.value),
2842
+ "aria-label": "MCP working directory"
2843
+ }),
2844
+ /* @__PURE__ */ jsx(FieldDescription, { children: "Must stay inside the current workspace." })
2845
+ ] }),
2846
+ /* @__PURE__ */ jsxs(Field, { children: [
2847
+ /* @__PURE__ */ jsx(FieldLabel, { children: "Environment mappings" }),
2848
+ /* @__PURE__ */ jsx(Textarea, {
2849
+ value: envLines,
2850
+ onChange: (event) => setEnvLines(event.target.value),
2851
+ "aria-label": "MCP environment mappings",
2852
+ placeholder: "CHILD=SOURCE"
2853
+ }),
2854
+ /* @__PURE__ */ jsx(FieldDescription, { children: "One CHILD=SOURCE pair per line. Only variable names are stored." })
2855
+ ] })
2856
+ ] }),
2857
+ /* @__PURE__ */ jsxs("div", {
2858
+ className: "flex justify-end gap-2",
2859
+ children: [/* @__PURE__ */ jsx(Button, {
2860
+ type: "button",
2861
+ size: "sm",
2862
+ variant: "ghost",
2863
+ onClick: resetAddForm,
2864
+ children: "Cancel"
2865
+ }), /* @__PURE__ */ jsxs(Button, {
2866
+ type: "button",
2867
+ size: "sm",
2868
+ disabled: isPending || !name.trim(),
2869
+ onClick: () => void handleAdd(),
2870
+ children: [isPending ? /* @__PURE__ */ jsx(Spinner, { "data-icon": "inline-start" }) : null, "Save"]
2871
+ })]
2872
+ })
2873
+ ]
2874
+ }) : null,
2875
+ !isLoading && connections.length === 0 && !adding ? /* @__PURE__ */ jsx("p", {
2876
+ className: "text-sm text-muted-foreground",
2877
+ children: "No MCP connections yet."
2878
+ }) : null,
3060
2879
  /* @__PURE__ */ jsx("div", {
3061
- className: "max-h-[min(24rem,50vh)] overflow-y-auto",
3062
- children: !pickerHasResults ? /* @__PURE__ */ jsx("p", {
3063
- className: "py-6 text-center text-xs text-pretty text-muted-foreground",
3064
- children: credentialProvidersLength === 0 ? "No providers available." : "No matching providers found."
3065
- }) : /* @__PURE__ */ jsxs("div", {
3066
- className: "flex flex-col gap-3",
3067
- children: [filteredPickerAvailable.length > 0 ? /* @__PURE__ */ jsx("div", {
3068
- className: "flex flex-col gap-1",
3069
- children: filteredPickerAvailable.map((provider) => /* @__PURE__ */ jsx(ProviderPickerRow, {
3070
- provider,
3071
- configured: false,
3072
- onSelect: () => onSelect(provider.id)
3073
- }, provider.id))
3074
- }) : null, filteredPickerConfigured.length > 0 ? /* @__PURE__ */ jsxs("div", {
3075
- className: "flex flex-col gap-1",
3076
- children: [filteredPickerAvailable.length > 0 ? /* @__PURE__ */ jsx("p", {
3077
- className: "px-2 pt-1 text-[0.6875rem] font-medium tracking-wide text-muted-foreground uppercase",
3078
- children: "Already configured"
3079
- }) : null, filteredPickerConfigured.map((provider) => /* @__PURE__ */ jsx(ProviderPickerRow, {
3080
- provider,
3081
- configured: true,
3082
- onSelect: () => onSelect(provider.id)
3083
- }, provider.id))]
3084
- }) : null]
3085
- })
2880
+ className: "flex flex-col gap-2",
2881
+ children: connections.map((connection) => /* @__PURE__ */ jsxs("div", {
2882
+ className: "flex flex-col gap-2",
2883
+ children: [
2884
+ /* @__PURE__ */ jsx(ItemRow, {
2885
+ icon: /* @__PURE__ */ jsx(Plug, { className: "size-4" }),
2886
+ title: connection.label,
2887
+ subtitle: connectionSubtitle(connection),
2888
+ trailing: /* @__PURE__ */ jsxs("div", {
2889
+ className: "flex items-center gap-1.5",
2890
+ children: [connection.usesOAuth && connection.status !== "needs_auth" ? /* @__PURE__ */ jsx(Button, {
2891
+ type: "button",
2892
+ size: "sm",
2893
+ variant: "ghost",
2894
+ disabled: isPending || !onOAuth,
2895
+ onClick: () => {
2896
+ handleLogout(connection.name);
2897
+ },
2898
+ children: "Sign out"
2899
+ }) : null, connection.source === "user" ? confirmRemove === connection.name ? /* @__PURE__ */ jsx(Button, {
2900
+ type: "button",
2901
+ size: "sm",
2902
+ variant: "destructive",
2903
+ disabled: isPending,
2904
+ onClick: () => {
2905
+ handleRemove(connection.name);
2906
+ },
2907
+ children: "Confirm remove"
2908
+ }) : /* @__PURE__ */ jsx(Button, {
2909
+ type: "button",
2910
+ size: "sm",
2911
+ variant: "ghost",
2912
+ disabled: isPending || !onRemove,
2913
+ "aria-label": `Remove ${connection.name}`,
2914
+ onClick: () => setConfirmRemove(connection.name),
2915
+ children: /* @__PURE__ */ jsx(Trash2, { className: "size-3.5" })
2916
+ }) : null]
2917
+ })
2918
+ }),
2919
+ connection.error ? /* @__PURE__ */ jsx(Alert, {
2920
+ className: "px-3 py-2",
2921
+ children: /* @__PURE__ */ jsx(AlertDescription, {
2922
+ className: "text-xs text-pretty",
2923
+ children: connection.error
2924
+ })
2925
+ }) : null,
2926
+ connection.usesOAuth && oauthAdapter ? /* @__PURE__ */ jsx(ProviderOAuthSignIn, {
2927
+ provider: {
2928
+ id: connection.name,
2929
+ name: connection.label,
2930
+ isConfigured: connection.status === "connected",
2931
+ envVarName: "",
2932
+ authType: "oauth",
2933
+ supportsOAuth: true
2934
+ },
2935
+ onOAuthLogin: oauthAdapter(connection)
2936
+ }) : null
2937
+ ]
2938
+ }, `${connection.source}:${connection.name}`))
3086
2939
  })
3087
2940
  ]
3088
2941
  });
3089
2942
  }
3090
- function AddProviderOAuthPanel({ onBack, onConfigured, onOAuthLogin, provider }) {
3091
- return /* @__PURE__ */ jsxs("div", {
3092
- className: "flex flex-col gap-3",
3093
- children: [/* @__PURE__ */ jsxs("div", {
3094
- className: "flex items-center gap-2",
3095
- children: [/* @__PURE__ */ jsx(Button, {
3096
- type: "button",
3097
- variant: "ghost",
3098
- size: "icon-sm",
3099
- "aria-label": "Back to provider list",
3100
- onClick: onBack,
3101
- children: /* @__PURE__ */ jsx(ArrowLeft, {})
3102
- }), /* @__PURE__ */ jsxs("div", {
3103
- className: "min-w-0 flex-1",
3104
- children: [/* @__PURE__ */ jsxs("p", {
3105
- className: "text-sm font-medium",
3106
- children: ["Configure ", provider.name]
3107
- }), /* @__PURE__ */ jsx("p", {
3108
- className: "text-xs text-pretty text-muted-foreground",
3109
- children: "OAuth sign-in. Credentials are stored in auth.json for this account."
3110
- })]
3111
- })]
3112
- }), /* @__PURE__ */ jsx(ProviderOAuthSignIn, {
3113
- provider,
3114
- onConfigured,
3115
- onOAuthLogin
3116
- }, provider.id)]
3117
- });
3118
- }
3119
- function AddProviderEditorPanel({ provider, api, apiKey, baseUrl, modelId, models, displayName, showPassword, attemptedSave, isPending, canSave, onApiKeyChange, onApiChange, onBaseUrlChange, onModelIdChange, onModelsChange, onDisplayNameChange, onTogglePassword, onBack, onCancel, onConfigured, onOAuthLogin, onSave }) {
3120
- const isCustomTemplate = provider.id === "custom" || isCustomProviderId(provider.id);
3121
- const meta = PROVIDER_METADATA[provider.id] ?? (isCustomTemplate ? PROVIDER_METADATA["custom"] : {
3122
- placeholder: "Enter credentials\u2026",
3123
- help: "Stored securely in your local environment overrides."
3124
- });
3125
- const openAiChat = isOccProviderId(provider.id) && !isCustomTemplate;
3126
- const oauthAvailable = supportsOAuth(provider);
3127
- return /* @__PURE__ */ jsxs("div", {
3128
- className: "flex flex-col gap-3",
3129
- children: [
3130
- /* @__PURE__ */ jsxs("div", {
3131
- className: "flex items-center gap-2",
3132
- children: [/* @__PURE__ */ jsx(Button, {
3133
- type: "button",
3134
- variant: "ghost",
3135
- size: "icon-sm",
3136
- "aria-label": "Back to provider list",
3137
- onClick: onBack,
3138
- children: /* @__PURE__ */ jsx(ArrowLeft, {})
3139
- }), /* @__PURE__ */ jsxs("div", {
3140
- className: "min-w-0 flex-1",
3141
- children: [/* @__PURE__ */ jsxs("p", {
3142
- className: "text-sm font-medium",
3143
- children: ["Configure ", provider.name]
3144
- }), /* @__PURE__ */ jsx("p", {
3145
- className: "text-xs text-pretty text-muted-foreground",
3146
- children: isCustomTemplate ? "Name this custom provider, pick its API family, then enter its API key, https base URL, and model ids." : openAiChat ? "Name this OpenAI-compatible provider, then enter its API key, base URL, and model name." : `Stored as ${provider.envVarName} for this account.`
3147
- })]
3148
- })]
3149
- }),
3150
- /* @__PURE__ */ jsx(ProviderCredentialFields, {
3151
- attemptedSave,
3152
- api,
3153
- apiKey,
3154
- baseUrl,
3155
- modelId,
3156
- models,
3157
- displayName,
3158
- isCustom: isCustomTemplate,
3159
- openAiChat,
3160
- placeholder: meta.placeholder,
3161
- showPassword,
3162
- onApiKeyChange,
3163
- onApiChange,
3164
- onBaseUrlChange,
3165
- onModelIdChange,
3166
- onModelsChange,
3167
- onDisplayNameChange,
3168
- onTogglePassword
3169
- }),
3170
- /* @__PURE__ */ jsxs(Alert, {
3171
- className: "px-3 py-2",
3172
- children: [/* @__PURE__ */ jsx(Info, {}), /* @__PURE__ */ jsx(AlertDescription, {
3173
- className: "text-xs text-pretty",
3174
- children: meta.help
3175
- })]
3176
- }),
3177
- /* @__PURE__ */ jsxs("div", {
3178
- className: "flex items-center justify-end gap-1.5",
3179
- children: [/* @__PURE__ */ jsx(Button, {
3180
- type: "button",
3181
- variant: "ghost",
3182
- size: "sm",
3183
- onClick: onCancel,
3184
- children: "Cancel"
3185
- }), /* @__PURE__ */ jsxs(Button, {
3186
- type: "button",
3187
- size: "sm",
3188
- disabled: isPending || !canSave,
3189
- onClick: onSave,
3190
- children: [isPending ? /* @__PURE__ */ jsx(Spinner, { "data-icon": "inline-start" }) : null, "Save"]
3191
- })]
3192
- }),
3193
- oauthAvailable ? /* @__PURE__ */ jsx(ProviderOAuthSignIn, {
3194
- provider,
3195
- onConfigured: onConfigured ?? onCancel,
3196
- onOAuthLogin
3197
- }, `${provider.id}-oauth`) : null
3198
- ]
3199
- });
2943
+ function customModelKey(provider, model) {
2944
+ return `${provider}/${model}`;
3200
2945
  }
3201
- function RemoveProviderConfirmDialog({ confirmRemoveProvider, isPending, onClose, onConfirm }) {
3202
- return /* @__PURE__ */ jsx(AlertDialog, {
3203
- open: confirmRemoveProvider !== null,
3204
- onOpenChange: (open) => {
3205
- if (!open) onClose();
3206
- },
3207
- children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [/* @__PURE__ */ jsxs("div", {
3208
- className: "flex flex-col gap-2",
3209
- children: [/* @__PURE__ */ jsxs(AlertDialogTitle, { children: [
3210
- "Remove ",
3211
- confirmRemoveProvider?.name ?? "provider",
3212
- "?"
3213
- ] }), /* @__PURE__ */ jsx(AlertDialogDescription, { children: "This clears stored credentials for this provider from your workspace settings. Active sessions will stop using it immediately." })]
3214
- }), /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(AlertDialogCancel, {
3215
- disabled: isPending,
3216
- children: "Cancel"
3217
- }), /* @__PURE__ */ jsxs(AlertDialogAction, {
3218
- disabled: isPending || !confirmRemoveProvider,
3219
- onClick: () => {
3220
- if (confirmRemoveProvider) onConfirm(confirmRemoveProvider);
3221
- },
3222
- children: [isPending ? /* @__PURE__ */ jsx(Spinner, { "data-icon": "inline-start" }) : null, "Remove provider"]
3223
- })] })] })
3224
- });
2946
+ function isModelEnabled(model, patterns) {
2947
+ return isModelPatternEnabled({
2948
+ id: model.id,
2949
+ name: model.name,
2950
+ provider: model.provider,
2951
+ modelId: model.modelId
2952
+ }, patterns);
3225
2953
  }
3226
- function ProviderPickerRow({ configured, onSelect, provider }) {
3227
- const openAiChat = isOccProviderId(provider.id);
3228
- const isCustomTemplate = provider.id === "custom" || isCustomProviderId(provider.id);
3229
- const oauthOnly = isOAuthProvider(provider);
3230
- const oauthAvailable = supportsOAuth(provider);
3231
- return /* @__PURE__ */ jsxs("button", {
3232
- type: "button",
3233
- className: cn("flex w-full items-center gap-3 rounded-lg px-2 py-2 text-left transition-[background-color,transform] duration-150", "hover:bg-muted/60 focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none active:scale-[0.96]"),
3234
- onClick: onSelect,
3235
- children: [/* @__PURE__ */ jsx("div", {
3236
- className: "flex size-8 shrink-0 items-center justify-center rounded-[4px] border border-border/40 bg-background/60",
3237
- children: /* @__PURE__ */ jsx(ProviderBrandIcon, {
3238
- provider: isCustomTemplate ? "custom" : provider.id,
3239
- api: provider.api,
3240
- className: "text-foreground/70"
3241
- })
3242
- }), /* @__PURE__ */ jsxs("div", {
3243
- className: "min-w-0 flex-1",
3244
- children: [/* @__PURE__ */ jsx("div", {
3245
- className: "truncate text-sm font-medium",
3246
- children: provider.name
3247
- }), /* @__PURE__ */ jsxs("div", {
3248
- className: "truncate text-xs text-muted-foreground",
3249
- children: [oauthOnly ? "OAuth sign-in" : oauthAvailable ? "API key or OAuth" : isCustomTemplate ? "API family + key + https base URL + models" : openAiChat ? "API key + base URL + model name" : provider.envVarName, configured ? " \xB7 Update" : ""]
3250
- })]
3251
- })]
3252
- });
2954
+ function nextEnabledModelPatterns({ currentPatterns, enabled, model, models }) {
2955
+ if (currentPatterns === void 0 && enabled) return void 0;
2956
+ const current = currentPatterns ?? [];
2957
+ const knownModelPatterns = new Set(models.map((item) => modelPatternFor(item)));
2958
+ const activeKnown = /* @__PURE__ */ new Set();
2959
+ for (const item of models) if (isModelEnabled(item, currentPatterns)) activeKnown.add(modelPatternFor(item));
2960
+ if (enabled) activeKnown.add(modelPatternFor(model));
2961
+ else activeKnown.delete(modelPatternFor(model));
2962
+ return [...current.filter((pattern) => {
2963
+ if (knownModelPatterns.has(pattern)) return false;
2964
+ return enabled || !modelMatchesPattern(model, pattern);
2965
+ }), ...activeKnown];
3253
2966
  }
3254
- function RowSurface({ className, interactive, padding, tone, ...props }) {
3255
- return /* @__PURE__ */ jsx("div", {
3256
- className: cn(fleetPiRowSurface({
3257
- interactive,
3258
- padding,
3259
- tone
3260
- }), className),
3261
- ...props
3262
- });
2967
+ function modelPatternFor(model) {
2968
+ return modelPattern(model.provider, model.modelId);
3263
2969
  }
3264
- function ItemRow({ icon, title, subtitle, trailing, className, interactive = true, tone = "default" }) {
3265
- return /* @__PURE__ */ jsxs(RowSurface, {
3266
- tone,
3267
- padding: "md",
3268
- interactive,
3269
- className: cn("items-center gap-3", className),
3270
- children: [
3271
- icon ? /* @__PURE__ */ jsx("div", {
3272
- className: "flex size-8 shrink-0 items-center justify-center rounded-md border border-border/40 bg-background/60 text-foreground/70",
3273
- children: icon
3274
- }) : null,
3275
- /* @__PURE__ */ jsxs("div", {
3276
- className: "min-w-0 flex-1",
3277
- children: [/* @__PURE__ */ jsx("div", {
3278
- className: "truncate text-sm font-medium text-foreground",
3279
- children: title
3280
- }), subtitle ? /* @__PURE__ */ jsx("div", {
3281
- className: "mt-0.5 truncate text-xs text-muted-foreground",
3282
- children: subtitle
3283
- }) : null]
3284
- }),
3285
- trailing ? /* @__PURE__ */ jsx("div", {
3286
- className: "flex shrink-0 items-center gap-1.5",
3287
- children: trailing
3288
- }) : null
3289
- ]
3290
- });
3291
- }
3292
- function ActiveProviderList({ providers, editingProvider, form, operation, actions, onEdit, onCancelEdit, onSave, onRemove, onOAuthLogin, onConfigured }) {
3293
- const { api, apiKey, baseUrl, modelId, models, displayName, showPassword, attemptedSave, canSave } = form;
3294
- const { isPending, canRemove } = operation;
3295
- const { onApiKeyChange, onApiChange, onBaseUrlChange, onModelIdChange, onModelsChange, onDisplayNameChange, onTogglePassword } = actions;
3296
- return /* @__PURE__ */ jsx("div", {
3297
- className: "flex flex-col gap-1.5",
3298
- children: providers.map((provider) => {
3299
- const isEditing = editingProvider === provider.id;
3300
- const isCustom = isCustomProviderId(provider.id);
3301
- const meta = PROVIDER_METADATA[provider.id] ?? PROVIDER_METADATA[isCustom ? "custom" : "openai-chat-completions"];
3302
- const openAiChat = isOccProviderId(provider.id);
3303
- const oauthOnly = isOAuthProvider(provider);
3304
- const oauthAvailable = supportsOAuth(provider);
3305
- return /* @__PURE__ */ jsxs("div", {
3306
- className: "flex flex-col",
3307
- children: [/* @__PURE__ */ jsx(ItemRow, {
3308
- interactive: false,
3309
- icon: /* @__PURE__ */ jsx(ProviderBrandIcon, {
3310
- provider: provider.id,
3311
- api: provider.api,
3312
- className: "text-foreground/80"
3313
- }),
3314
- title: provider.name,
3315
- subtitle: oauthOnly ? "OAuth sign-in" : oauthAvailable ? "API key or OAuth" : isCustom ? "Custom provider \xB7 API key + base URL + models" : openAiChat ? "OpenAI-compatible \xB7 API key + base URL + model name" : provider.envVarName,
3316
- trailing: /* @__PURE__ */ jsxs("div", {
3317
- className: "flex items-center gap-0.5",
3318
- children: [/* @__PURE__ */ jsxs(Button, {
3319
- type: "button",
3320
- variant: "ghost",
3321
- size: "sm",
3322
- className: cn(HIT_AREA_EXPAND_DENSE_CLASS, "h-7 px-2 text-xs text-muted-foreground transition-[background-color,color,transform] duration-150 hover:text-destructive active:scale-[0.96]"),
3323
- disabled: isPending || !canRemove,
3324
- "aria-label": `Remove ${provider.name}`,
3325
- onClick: () => onRemove(provider),
3326
- children: [/* @__PURE__ */ jsx(Trash2, { "data-icon": "inline-start" }), "Remove"]
3327
- }), /* @__PURE__ */ jsxs(Button, {
3328
- type: "button",
3329
- variant: "ghost",
3330
- size: "sm",
3331
- className: cn(HIT_AREA_EXPAND_DENSE_CLASS, "h-7 px-2 text-xs transition-[background-color,transform] duration-150 active:scale-[0.96]"),
3332
- disabled: isPending,
3333
- onClick: () => {
3334
- if (isEditing) {
3335
- onCancelEdit();
3336
- return;
3337
- }
3338
- onEdit(provider.id);
3339
- },
3340
- children: [!isEditing ? /* @__PURE__ */ jsx(Pencil, { "data-icon": "inline-start" }) : null, isEditing ? "Cancel" : "Update"]
3341
- })]
3342
- })
3343
- }), isEditing ? /* @__PURE__ */ jsx(RowSurface, {
3344
- tone: "inset",
3345
- padding: "md",
3346
- className: "flex flex-col gap-2 border-t border-border/30",
3347
- children: oauthOnly ? /* @__PURE__ */ jsx(ProviderOAuthSignIn, {
3348
- provider,
3349
- onConfigured,
3350
- onOAuthLogin
3351
- }, provider.id) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
3352
- /* @__PURE__ */ jsx(ProviderCredentialFields, {
3353
- attemptedSave,
3354
- api: provider.api ?? api,
3355
- apiKey,
3356
- baseUrl,
3357
- modelId,
3358
- models,
3359
- displayName,
3360
- isCustom,
3361
- openAiChat,
3362
- placeholder: meta.placeholder,
3363
- showPassword,
3364
- onApiKeyChange,
3365
- onApiChange,
3366
- onBaseUrlChange,
3367
- onModelIdChange,
3368
- onModelsChange,
3369
- onDisplayNameChange: isCustom || provider.providerFamily !== void 0 ? onDisplayNameChange : void 0,
3370
- onTogglePassword
3371
- }),
3372
- /* @__PURE__ */ jsxs(Alert, {
3373
- className: "px-3 py-2",
3374
- children: [/* @__PURE__ */ jsx(Info, {}), /* @__PURE__ */ jsx(AlertDescription, {
3375
- className: "text-xs text-pretty",
3376
- children: meta.help
3377
- })]
3378
- }),
3379
- /* @__PURE__ */ jsx("div", {
3380
- className: "flex items-center justify-end",
3381
- children: /* @__PURE__ */ jsxs(Button, {
3382
- type: "button",
3383
- size: "sm",
3384
- disabled: isPending || !canSave,
3385
- onClick: () => onSave(provider.id),
3386
- children: [isPending ? /* @__PURE__ */ jsx(Spinner, { "data-icon": "inline-start" }) : null, "Save"]
3387
- })
3388
- }),
3389
- oauthAvailable ? /* @__PURE__ */ jsx(ProviderOAuthSignIn, {
3390
- provider,
3391
- onConfigured,
3392
- onOAuthLogin
3393
- }, `${provider.id}-oauth`) : null
3394
- ] })
3395
- }) : null]
3396
- }, provider.id);
3397
- })
3398
- });
3399
- }
3400
- function ProviderCredentialsSection({ isLoading, isPending, onOAuthLogin, onRemoveProvider, onUpdateProvider, providers }) {
3401
- const { activeProviders, addPickerOpen, addPickerQuery, api, apiKey, attemptedSave, baseUrl, canSave, closeAddPicker, closeEditor, confirmRemoveProvider, credentialProviders, displayName, editingProvider, editingUnconfiguredProvider, filteredActiveProviders, filteredPickerAvailable, filteredPickerConfigured, handleRemove, handleSave, modelId, models, openAddPicker, openEditor, pickerHasResults, searchQuery, selectProviderFromPicker, setAddPickerQuery, setApi, setApiKey, setBaseUrl, setConfirmRemoveProvider, setDisplayName, setModelId, setModels, setSearchQuery, setShowPassword, showPassword } = useProviderCredentialsController({
3402
- onRemoveProvider,
3403
- onUpdateProvider,
3404
- providers
3405
- });
3406
- const form = {
3407
- api,
3408
- apiKey,
3409
- baseUrl,
3410
- modelId,
3411
- models,
3412
- displayName,
3413
- showPassword,
3414
- attemptedSave,
3415
- canSave
3416
- };
3417
- const operation = {
3418
- isPending,
3419
- canRemove: Boolean(onRemoveProvider)
3420
- };
3421
- const actions = {
3422
- onApiKeyChange: setApiKey,
3423
- onApiChange: setApi,
3424
- onBaseUrlChange: setBaseUrl,
3425
- onModelIdChange: setModelId,
3426
- onModelsChange: setModels,
3427
- onDisplayNameChange: setDisplayName,
3428
- onTogglePassword: () => setShowPassword((current) => !current)
3429
- };
3430
- return /* @__PURE__ */ jsxs(SettingsPane, {
3431
- title: "Providers",
3432
- description: "Locally, API keys are stored in `.env.local`. When deployed, they are stored encrypted in your account.",
3433
- children: [addPickerOpen ? /* @__PURE__ */ jsx(AddProviderPickerPanel, {
3434
- addPickerQuery,
3435
- credentialProvidersLength: credentialProviders.length,
3436
- filteredPickerAvailable,
3437
- filteredPickerConfigured,
3438
- pickerHasResults,
3439
- onClose: closeAddPicker,
3440
- onQueryChange: setAddPickerQuery,
3441
- onSelect: selectProviderFromPicker
3442
- }) : editingUnconfiguredProvider && isOAuthProvider(editingUnconfiguredProvider) ? /* @__PURE__ */ jsx(AddProviderOAuthPanel, {
3443
- provider: editingUnconfiguredProvider,
3444
- onBack: openAddPicker,
3445
- onConfigured: closeEditor,
3446
- onOAuthLogin
3447
- }, editingUnconfiguredProvider.id) : editingUnconfiguredProvider ? /* @__PURE__ */ jsx(AddProviderEditorPanel, {
3448
- provider: editingUnconfiguredProvider,
3449
- api,
3450
- apiKey,
3451
- baseUrl,
3452
- modelId,
3453
- models,
3454
- displayName,
3455
- showPassword,
3456
- attemptedSave,
3457
- isPending,
3458
- canSave,
3459
- onApiKeyChange: setApiKey,
3460
- onApiChange: setApi,
3461
- onBaseUrlChange: setBaseUrl,
3462
- onModelIdChange: setModelId,
3463
- onModelsChange: setModels,
3464
- onDisplayNameChange: setDisplayName,
3465
- onTogglePassword: () => setShowPassword((current) => !current),
3466
- onBack: openAddPicker,
3467
- onCancel: closeEditor,
3468
- onConfigured: closeEditor,
3469
- onOAuthLogin,
3470
- onSave: () => {
3471
- if (editingProvider) handleSave(editingProvider);
3472
- }
3473
- }) : /* @__PURE__ */ jsxs("div", {
3474
- className: "flex flex-col gap-3",
3475
- children: [/* @__PURE__ */ jsxs("div", {
3476
- className: "flex items-center gap-2",
3477
- children: [/* @__PURE__ */ jsxs(InputGroup, {
3478
- className: "flex-1",
3479
- children: [/* @__PURE__ */ jsx(InputGroupAddon, {
3480
- align: "inline-start",
3481
- children: /* @__PURE__ */ jsx(Search, {})
3482
- }), /* @__PURE__ */ jsx(InputGroupInput, {
3483
- type: "text",
3484
- placeholder: "Search credentials\u2026",
3485
- value: searchQuery,
3486
- onChange: (event) => setSearchQuery(event.target.value),
3487
- "aria-label": "Search credentials",
3488
- disabled: isLoading || activeProviders.length === 0
3489
- })]
3490
- }), /* @__PURE__ */ jsxs(Button, {
3491
- type: "button",
3492
- variant: "outline",
3493
- size: "sm",
3494
- disabled: isLoading || credentialProviders.length === 0,
3495
- onClick: openAddPicker,
3496
- children: [/* @__PURE__ */ jsx(Plus, { "data-icon": "inline-start" }), "Add provider"]
3497
- })]
3498
- }), isLoading ? /* @__PURE__ */ jsxs("div", {
3499
- className: "flex items-center justify-center gap-2 py-6 text-xs text-muted-foreground",
3500
- children: [/* @__PURE__ */ jsx(Spinner, { className: "size-3" }), /* @__PURE__ */ jsx("span", { children: "Loading\u2026" })]
3501
- }) : activeProviders.length === 0 ? /* @__PURE__ */ jsxs("div", {
3502
- className: "flex flex-col items-center gap-3 py-8",
3503
- children: [/* @__PURE__ */ jsx("p", {
3504
- className: "text-center text-xs text-pretty text-muted-foreground",
3505
- children: "No providers configured"
3506
- }), /* @__PURE__ */ jsxs(Button, {
3507
- type: "button",
3508
- variant: "outline",
3509
- size: "sm",
3510
- disabled: credentialProviders.length === 0,
3511
- onClick: openAddPicker,
3512
- children: [/* @__PURE__ */ jsx(Plus, { "data-icon": "inline-start" }), "Add provider"]
3513
- })]
3514
- }) : filteredActiveProviders.length === 0 ? /* @__PURE__ */ jsx("p", {
3515
- className: "py-6 text-center text-xs text-pretty text-muted-foreground",
3516
- children: "No matching providers found."
3517
- }) : /* @__PURE__ */ jsx(ActiveProviderList, {
3518
- providers: filteredActiveProviders,
3519
- editingProvider,
3520
- form,
3521
- operation,
3522
- actions,
3523
- onEdit: openEditor,
3524
- onCancelEdit: closeEditor,
3525
- onSave: (providerId) => void handleSave(providerId),
3526
- onRemove: setConfirmRemoveProvider,
3527
- onOAuthLogin,
3528
- onConfigured: closeEditor
3529
- })]
3530
- }), /* @__PURE__ */ jsx(RemoveProviderConfirmDialog, {
3531
- confirmRemoveProvider,
3532
- isPending,
3533
- onClose: () => setConfirmRemoveProvider(null),
3534
- onConfirm: (provider) => {
3535
- handleRemove(provider);
3536
- }
3537
- })]
3538
- });
3539
- }
3540
- function customModelKey(provider, model) {
3541
- return `${provider}/${model}`;
3542
- }
3543
- function isModelEnabled(model, patterns) {
3544
- return isModelPatternEnabled({
3545
- id: model.id,
3546
- name: model.name,
3547
- provider: model.provider,
3548
- modelId: model.modelId
3549
- }, patterns);
3550
- }
3551
- function nextEnabledModelPatterns({ currentPatterns, enabled, model, models }) {
3552
- if (currentPatterns === void 0 && enabled) return void 0;
3553
- const current = currentPatterns ?? [];
3554
- const knownModelPatterns = new Set(models.map((item) => modelPatternFor(item)));
3555
- const activeKnown = /* @__PURE__ */ new Set();
3556
- for (const item of models) if (isModelEnabled(item, currentPatterns)) activeKnown.add(modelPatternFor(item));
3557
- if (enabled) activeKnown.add(modelPatternFor(model));
3558
- else activeKnown.delete(modelPatternFor(model));
3559
- return [...current.filter((pattern) => {
3560
- if (knownModelPatterns.has(pattern)) return false;
3561
- return enabled || !modelMatchesPattern(model, pattern);
3562
- }), ...activeKnown];
3563
- }
3564
- function modelPatternFor(model) {
3565
- return modelPattern(model.provider, model.modelId);
3566
- }
3567
- function searchableModelText(model) {
3568
- return [
3569
- model.name,
3570
- model.modelId,
3571
- model.provider,
3572
- model.id
3573
- ].join(" ").toLowerCase();
2970
+ function searchableModelText(model) {
2971
+ return [
2972
+ model.name,
2973
+ model.modelId,
2974
+ model.provider,
2975
+ model.id
2976
+ ].join(" ").toLowerCase();
3574
2977
  }
3575
2978
  function AddModelsDialog({ configuredProviderIds, discoverableProviderIds, discoveringProviderId, enabledModels, modelOptions, onAddModels, onDiscoverProvider, onOpenChange, open, providerLabel = formatProviderLabel }) {
3576
2979
  const [filter, setFilter] = useState2("");
@@ -3742,18 +3145,14 @@ function AddModelsDialog({ configuredProviderIds, discoverableProviderIds, disco
3742
3145
  size: "sm",
3743
3146
  disabled: selectedKeys.size === 0,
3744
3147
  onClick: confirmAdd,
3745
- children: [
3746
- "Add",
3747
- " ",
3748
- selectedKeys.size > 0 ? /* @__PURE__ */ jsxs("span", {
3749
- className: "tabular-nums",
3750
- children: [
3751
- "(",
3752
- selectedKeys.size,
3753
- ")"
3754
- ]
3755
- }) : null
3756
- ]
3148
+ children: ["Add ", selectedKeys.size > 0 ? /* @__PURE__ */ jsxs("span", {
3149
+ className: "tabular-nums",
3150
+ children: [
3151
+ "(",
3152
+ selectedKeys.size,
3153
+ ")"
3154
+ ]
3155
+ }) : null]
3757
3156
  })]
3758
3157
  })
3759
3158
  ]
@@ -3824,8 +3223,7 @@ function ModelDefaultsSection({ draft, discoveringProviderId, modelDirty, modelF
3824
3223
  return /* @__PURE__ */ jsxs(SettingsPane, {
3825
3224
  title: "LLM Models",
3826
3225
  description: /* @__PURE__ */ jsxs(Fragment$1, { children: [
3827
- "Models available in chat. Discover from configured providers, add the ones you want, and remove any you do not need \u2014 changes save automatically.",
3828
- " ",
3226
+ "Models available in chat. Discover from configured providers, add the ones you want, and remove any you do not need \u2014 changes save automatically. ",
3829
3227
  /* @__PURE__ */ jsx("span", {
3830
3228
  className: "tabular-nums",
3831
3229
  children: listedModels.length
@@ -3838,8 +3236,7 @@ function ModelDefaultsSection({ draft, discoveringProviderId, modelDirty, modelF
3838
3236
  className: "tabular-nums",
3839
3237
  children: modelOptions.length
3840
3238
  }),
3841
- " ",
3842
- "discovered"
3239
+ " discovered"
3843
3240
  ] }) : null,
3844
3241
  "."
3845
3242
  ] }),
@@ -3853,200 +3250,989 @@ function ModelDefaultsSection({ draft, discoveringProviderId, modelDirty, modelF
3853
3250
  children: [
3854
3251
  /* @__PURE__ */ jsxs("div", {
3855
3252
  className: "flex items-center gap-2",
3856
- children: [/* @__PURE__ */ jsxs(InputGroup, {
3857
- className: "flex-1",
3858
- children: [/* @__PURE__ */ jsx(InputGroupAddon, {
3859
- align: "inline-start",
3860
- children: /* @__PURE__ */ jsx(Search, {})
3861
- }), /* @__PURE__ */ jsx(InputGroupInput, {
3862
- "aria-label": "Search listed models",
3863
- value: modelFilter,
3864
- onChange: (event) => onModelFilterChange(event.target.value),
3865
- placeholder: "Search listed models\u2026",
3866
- disabled: disabled2 || listedModels.length === 0
3867
- })]
3868
- }), /* @__PURE__ */ jsxs(Button, {
3253
+ children: [/* @__PURE__ */ jsxs(InputGroup, {
3254
+ className: "flex-1",
3255
+ children: [/* @__PURE__ */ jsx(InputGroupAddon, {
3256
+ align: "inline-start",
3257
+ children: /* @__PURE__ */ jsx(Search, {})
3258
+ }), /* @__PURE__ */ jsx(InputGroupInput, {
3259
+ "aria-label": "Search listed models",
3260
+ value: modelFilter,
3261
+ onChange: (event) => onModelFilterChange(event.target.value),
3262
+ placeholder: "Search listed models\u2026",
3263
+ disabled: disabled2 || listedModels.length === 0
3264
+ })]
3265
+ }), /* @__PURE__ */ jsxs(Button, {
3266
+ type: "button",
3267
+ variant: "outline",
3268
+ size: "sm",
3269
+ disabled: disabled2,
3270
+ onClick: openAddDialog,
3271
+ children: [/* @__PURE__ */ jsx(Plus, { "data-icon": "inline-start" }), "Add models"]
3272
+ })]
3273
+ }),
3274
+ /* @__PURE__ */ jsx("div", {
3275
+ className: "flex flex-col gap-3",
3276
+ "data-testid": "runtime-models-list",
3277
+ children: emptyMessage ? /* @__PURE__ */ jsxs("div", {
3278
+ className: "flex flex-col items-center gap-3 py-8",
3279
+ children: [/* @__PURE__ */ jsx("p", {
3280
+ className: "px-1 text-center text-xs text-pretty text-muted-foreground",
3281
+ children: emptyMessage
3282
+ }), !normalizedFilter ? /* @__PURE__ */ jsxs(Button, {
3283
+ type: "button",
3284
+ variant: "outline",
3285
+ size: "sm",
3286
+ disabled: disabled2,
3287
+ onClick: openAddDialog,
3288
+ children: [/* @__PURE__ */ jsx(Plus, { "data-icon": "inline-start" }), "Add models"]
3289
+ }) : null]
3290
+ }) : groupedListed.map(([provider, models]) => /* @__PURE__ */ jsxs("div", {
3291
+ className: "flex flex-col gap-1.5",
3292
+ children: [/* @__PURE__ */ jsx("div", {
3293
+ className: "px-1 text-xs font-medium text-muted-foreground",
3294
+ children: providerLabel(provider)
3295
+ }), models.map((model) => /* @__PURE__ */ jsx(ItemRow, {
3296
+ icon: /* @__PURE__ */ jsx(ProviderBrandIcon, { provider: model.provider }),
3297
+ title: model.name,
3298
+ subtitle: `${providerLabel(model.provider)} \xB7 ${model.modelId}`,
3299
+ trailing: /* @__PURE__ */ jsxs("div", {
3300
+ className: "flex items-center",
3301
+ children: [isDefaultModel(model) ? /* @__PURE__ */ jsx("span", {
3302
+ className: "px-2 text-xs font-medium text-muted-foreground",
3303
+ children: "Default"
3304
+ }) : onSetDefaultModel ? /* @__PURE__ */ jsxs(Button, {
3305
+ type: "button",
3306
+ variant: "ghost",
3307
+ size: "sm",
3308
+ className: cn(HIT_AREA_EXPAND_DENSE_CLASS, "h-7 px-2 text-xs text-muted-foreground transition-[background-color,color,transform] duration-150 active:scale-[0.96]"),
3309
+ disabled: disabled2,
3310
+ "aria-label": `Set ${model.name} as default`,
3311
+ onClick: () => onSetDefaultModel(model),
3312
+ children: [/* @__PURE__ */ jsx(Star, { "data-icon": "inline-start" }), "Set default"]
3313
+ }) : null, /* @__PURE__ */ jsxs(Button, {
3314
+ type: "button",
3315
+ variant: "ghost",
3316
+ size: "sm",
3317
+ className: cn(HIT_AREA_EXPAND_DENSE_CLASS, "h-7 px-2 text-xs text-muted-foreground transition-[background-color,color,transform] duration-150 hover:text-destructive active:scale-[0.96]"),
3318
+ disabled: disabled2,
3319
+ "aria-label": `Remove ${model.name}`,
3320
+ onClick: () => onRemoveModel(model),
3321
+ children: [/* @__PURE__ */ jsx(Trash2, { "data-icon": "inline-start" }), "Remove"]
3322
+ })]
3323
+ })
3324
+ }, model.id))]
3325
+ }, provider))
3326
+ }),
3327
+ /* @__PURE__ */ jsx(AddModelsDialog, {
3328
+ open: addOpen,
3329
+ configuredProviderIds,
3330
+ discoverableProviderIds,
3331
+ discoveringProviderId,
3332
+ enabledModels: draft?.enabledModels,
3333
+ modelOptions,
3334
+ onAddModels,
3335
+ onDiscoverProvider,
3336
+ onOpenChange: setAddOpen,
3337
+ providerLabel
3338
+ })
3339
+ ]
3340
+ });
3341
+ }
3342
+ var THEME_OPTIONS = [
3343
+ {
3344
+ value: "system",
3345
+ label: "System",
3346
+ icon: Monitor
3347
+ },
3348
+ {
3349
+ value: "light",
3350
+ label: "Light",
3351
+ icon: Sun
3352
+ },
3353
+ {
3354
+ value: "dark",
3355
+ label: "Dark",
3356
+ icon: Moon
3357
+ }
3358
+ ];
3359
+ function PersonalizationSection({ onThemePreferenceChange, themePreference }) {
3360
+ return /* @__PURE__ */ jsxs(Field, {
3361
+ orientation: "horizontal",
3362
+ className: "items-center justify-between gap-6 rounded-xl border p-4",
3363
+ children: [/* @__PURE__ */ jsxs("div", {
3364
+ className: "min-w-0",
3365
+ children: [/* @__PURE__ */ jsx(FieldLabel, { children: "Theme" }), /* @__PURE__ */ jsx(FieldDescription, { children: "Match the system theme or pick light or dark." })]
3366
+ }), /* @__PURE__ */ jsx("div", {
3367
+ className: "shrink-0",
3368
+ children: /* @__PURE__ */ jsx(Select, {
3369
+ "aria-label": "Theme",
3370
+ className: "w-[160px]",
3371
+ onValueChange: (value) => onThemePreferenceChange(value),
3372
+ options: [...THEME_OPTIONS],
3373
+ value: themePreference
3374
+ })
3375
+ })]
3376
+ });
3377
+ }
3378
+ function isFieldInvalid(value, attemptedSave, required = true) {
3379
+ return attemptedSave && required && value.trim().length === 0;
3380
+ }
3381
+ function SecretCredentialField({ attemptedSave, label, onChange, onToggleVisibility, placeholder, required = true, showPassword, value }) {
3382
+ const controlId = useId();
3383
+ const invalid = isFieldInvalid(value, attemptedSave, required);
3384
+ return /* @__PURE__ */ jsxs(Field, {
3385
+ "data-invalid": invalid || void 0,
3386
+ children: [/* @__PURE__ */ jsx(FieldLabel, {
3387
+ htmlFor: controlId,
3388
+ children: label
3389
+ }), /* @__PURE__ */ jsxs(InputGroup, { children: [
3390
+ /* @__PURE__ */ jsx(InputGroupAddon, {
3391
+ align: "inline-start",
3392
+ children: /* @__PURE__ */ jsx(Lock, {})
3393
+ }),
3394
+ /* @__PURE__ */ jsx(InputGroupInput, {
3395
+ id: controlId,
3396
+ type: showPassword ? "text" : "password",
3397
+ placeholder,
3398
+ value,
3399
+ onChange: (event) => onChange(event.target.value),
3400
+ "aria-label": label,
3401
+ "aria-invalid": invalid || void 0
3402
+ }),
3403
+ /* @__PURE__ */ jsx(InputGroupAddon, {
3404
+ align: "inline-end",
3405
+ children: /* @__PURE__ */ jsx(InputGroupButton, {
3406
+ size: "icon-xs",
3407
+ className: cn(HIT_AREA_EXPAND_CLASS, "transition-[background-color,transform] duration-150 active:scale-[0.96]"),
3408
+ "aria-label": showPassword ? "Hide value" : "Show value",
3409
+ onClick: onToggleVisibility,
3410
+ children: showPassword ? /* @__PURE__ */ jsx(EyeOff, {}) : /* @__PURE__ */ jsx(Eye, {})
3411
+ })
3412
+ })
3413
+ ] })]
3414
+ });
3415
+ }
3416
+ function TextCredentialField({ attemptedSave, icon: Icon, inputType = "text", label, onChange, placeholder, required = true, value }) {
3417
+ const controlId = useId();
3418
+ const invalid = isFieldInvalid(value, attemptedSave, required);
3419
+ return /* @__PURE__ */ jsxs(Field, {
3420
+ "data-invalid": invalid || void 0,
3421
+ children: [/* @__PURE__ */ jsx(FieldLabel, {
3422
+ htmlFor: controlId,
3423
+ children: label
3424
+ }), /* @__PURE__ */ jsxs(InputGroup, { children: [/* @__PURE__ */ jsx(InputGroupAddon, {
3425
+ align: "inline-start",
3426
+ children: /* @__PURE__ */ jsx(Icon, {})
3427
+ }), /* @__PURE__ */ jsx(InputGroupInput, {
3428
+ id: controlId,
3429
+ type: inputType,
3430
+ placeholder,
3431
+ value,
3432
+ onChange: (event) => onChange(event.target.value),
3433
+ "aria-label": label,
3434
+ "aria-invalid": invalid || void 0
3435
+ })] })]
3436
+ });
3437
+ }
3438
+ var OPENAI_CHAT_BASE_URL_PLACEHOLDER = "https://opencode.ai/zen/v1";
3439
+ var OPENAI_CHAT_MODEL_PLACEHOLDER = "qwen35-122b-a10b";
3440
+ var OPENAI_CHAT_NAME_PLACEHOLDER = "e.g. OpenCode Zen, Nebius, Groq\u2026";
3441
+ var CUSTOM_MODELS_PLACEHOLDER = "model-a, model-b (comma-separated)";
3442
+ var CUSTOM_PROVIDER_API_OPTIONS = [
3443
+ {
3444
+ value: "openai-completions",
3445
+ label: "OpenAI Chat Completions"
3446
+ },
3447
+ {
3448
+ value: "openai-responses",
3449
+ label: "OpenAI Responses"
3450
+ },
3451
+ {
3452
+ value: "anthropic-messages",
3453
+ label: "Anthropic Messages"
3454
+ },
3455
+ {
3456
+ value: "google-genai",
3457
+ label: "Google Generative AI"
3458
+ }
3459
+ ];
3460
+ function ProviderCredentialFields({ attemptedSave, apiKey, api, baseUrl, modelId, models, displayName, isCustom, onApiKeyChange, onApiChange, onBaseUrlChange, onModelIdChange, onModelsChange, onDisplayNameChange, onTogglePassword, openAiChat, placeholder, showPassword }) {
3461
+ const showApiSelector = isCustom && onApiChange;
3462
+ const showMultiModels = isCustom && onModelsChange;
3463
+ return /* @__PURE__ */ jsxs(FieldGroup, {
3464
+ className: "gap-2",
3465
+ children: [
3466
+ (openAiChat || isCustom) && onDisplayNameChange ? /* @__PURE__ */ jsx(TextCredentialField, {
3467
+ attemptedSave,
3468
+ icon: Tag,
3469
+ label: "Provider name",
3470
+ placeholder: OPENAI_CHAT_NAME_PLACEHOLDER,
3471
+ value: displayName ?? "",
3472
+ onChange: onDisplayNameChange
3473
+ }) : null,
3474
+ showApiSelector ? /* @__PURE__ */ jsx(ApiSelector, {
3475
+ value: api ?? "openai-completions",
3476
+ onChange: onApiChange
3477
+ }) : null,
3478
+ /* @__PURE__ */ jsx(SecretCredentialField, {
3479
+ attemptedSave,
3480
+ label: "API key",
3481
+ placeholder,
3482
+ value: apiKey,
3483
+ showPassword,
3484
+ onChange: onApiKeyChange,
3485
+ onToggleVisibility: onTogglePassword
3486
+ }),
3487
+ openAiChat || isCustom ? /* @__PURE__ */ jsx(TextCredentialField, {
3488
+ attemptedSave,
3489
+ icon: Globe,
3490
+ inputType: "url",
3491
+ label: "Base URL",
3492
+ placeholder: OPENAI_CHAT_BASE_URL_PLACEHOLDER,
3493
+ value: baseUrl,
3494
+ onChange: onBaseUrlChange
3495
+ }) : null,
3496
+ showMultiModels ? /* @__PURE__ */ jsx(TextCredentialField, {
3497
+ attemptedSave,
3498
+ icon: Layers,
3499
+ label: "Model ids",
3500
+ placeholder: CUSTOM_MODELS_PLACEHOLDER,
3501
+ value: models ?? "",
3502
+ onChange: onModelsChange
3503
+ }) : null,
3504
+ openAiChat && !showMultiModels ? /* @__PURE__ */ jsx(TextCredentialField, {
3505
+ attemptedSave,
3506
+ icon: Box,
3507
+ label: "Model name",
3508
+ placeholder: OPENAI_CHAT_MODEL_PLACEHOLDER,
3509
+ value: modelId,
3510
+ onChange: onModelIdChange
3511
+ }) : null
3512
+ ]
3513
+ });
3514
+ }
3515
+ function ApiSelector({ value, onChange }) {
3516
+ return /* @__PURE__ */ jsxs("div", {
3517
+ className: "flex flex-col gap-1",
3518
+ children: [/* @__PURE__ */ jsx("span", {
3519
+ className: "text-xs font-medium text-muted-foreground",
3520
+ children: "API family"
3521
+ }), /* @__PURE__ */ jsx(Select, {
3522
+ "aria-label": "API family",
3523
+ value,
3524
+ options: CUSTOM_PROVIDER_API_OPTIONS,
3525
+ placeholder: "Select API family",
3526
+ onValueChange: (next) => onChange(next)
3527
+ })]
3528
+ });
3529
+ }
3530
+ function isOAuthProvider(provider) {
3531
+ return provider.authType === "oauth";
3532
+ }
3533
+ function supportsOAuth(provider) {
3534
+ return provider.supportsOAuth === true;
3535
+ }
3536
+ var CUSTOM_PROVIDER_PICKER_ROW = {
3537
+ id: "custom",
3538
+ name: "Custom provider",
3539
+ isConfigured: false,
3540
+ envVarName: "CUSTOM_PROVIDER_API_KEY",
3541
+ displayName: "Custom provider"
3542
+ };
3543
+ function parseCustomProviderModels(raw) {
3544
+ return raw.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
3545
+ }
3546
+ function useProviderCredentialsController({ onRemoveProvider, onUpdateProvider, providers }) {
3547
+ const [editingProvider, setEditingProvider] = useState2(null);
3548
+ const [editingIsNewOccInstance, setEditingIsNewOccInstance] = useState2(false);
3549
+ const [apiKey, setApiKey] = useState2("");
3550
+ const [baseUrl, setBaseUrl] = useState2("");
3551
+ const [modelId, setModelId] = useState2("");
3552
+ const [models, setModels] = useState2("");
3553
+ const [api, setApi] = useState2("openai-completions");
3554
+ const [displayName, setDisplayName] = useState2("");
3555
+ const [showPassword, setShowPassword] = useState2(false);
3556
+ const [attemptedSave, setAttemptedSave] = useState2(false);
3557
+ const [searchQuery, setSearchQuery] = useState2("");
3558
+ const [addPickerOpen, setAddPickerOpen] = useState2(false);
3559
+ const [addPickerQuery, setAddPickerQuery] = useState2("");
3560
+ const [confirmRemoveProvider, setConfirmRemoveProvider] = useState2(null);
3561
+ const credentialProviders = useMemo2(() => {
3562
+ return [...providers.filter((provider) => provider.id !== CUSTOM_PROVIDER_PICKER_ROW.id), CUSTOM_PROVIDER_PICKER_ROW];
3563
+ }, [providers]);
3564
+ const activeProviders = useMemo2(() => credentialProviders.filter((provider) => provider.isConfigured), [credentialProviders]);
3565
+ const unconfiguredProviders = useMemo2(() => credentialProviders.filter((provider) => !provider.isConfigured || provider.id === "openai-chat-completions" && provider.providerFamily === void 0), [credentialProviders]);
3566
+ const filteredActiveProviders = useMemo2(() => {
3567
+ const query = searchQuery.trim().toLowerCase();
3568
+ if (!query) return activeProviders;
3569
+ return activeProviders.filter((provider) => providerMatchesQuery(provider, query));
3570
+ }, [activeProviders, searchQuery]);
3571
+ const filteredPickerAvailable = useMemo2(() => {
3572
+ const query = addPickerQuery.trim().toLowerCase();
3573
+ return unconfiguredProviders.filter((provider) => providerMatchesQuery(provider, query));
3574
+ }, [addPickerQuery, unconfiguredProviders]);
3575
+ const filteredPickerConfigured = useMemo2(() => {
3576
+ const query = addPickerQuery.trim().toLowerCase();
3577
+ return activeProviders.filter((provider) => providerMatchesQuery(provider, query));
3578
+ }, [activeProviders, addPickerQuery]);
3579
+ const pickerHasResults = filteredPickerAvailable.length > 0 || filteredPickerConfigured.length > 0;
3580
+ const editingUnconfiguredProvider = editingProvider && !activeProviders.some((provider) => provider.id === editingProvider) ? credentialProviders.find((entry) => entry.id === editingProvider) ?? null : null;
3581
+ const resetEditor = () => {
3582
+ setApiKey("");
3583
+ setBaseUrl("");
3584
+ setModelId("");
3585
+ setModels("");
3586
+ setApi("openai-completions");
3587
+ setDisplayName("");
3588
+ setShowPassword(false);
3589
+ setAttemptedSave(false);
3590
+ };
3591
+ const closeEditor = () => {
3592
+ setEditingProvider(null);
3593
+ setEditingIsNewOccInstance(false);
3594
+ resetEditor();
3595
+ };
3596
+ const openEditor = (providerId, opts) => {
3597
+ setAddPickerOpen(false);
3598
+ setAddPickerQuery("");
3599
+ setEditingProvider(providerId);
3600
+ setEditingIsNewOccInstance(opts?.newOccInstance === true);
3601
+ resetEditor();
3602
+ const existing = providers.find((provider) => provider.id === providerId);
3603
+ setBaseUrl(existing?.baseUrl ?? "");
3604
+ setDisplayName(existing?.displayName ?? (isCustomProviderId(providerId) ? existing?.name ?? "" : ""));
3605
+ setApi(existing?.api ?? "openai-completions");
3606
+ setModels((existing?.modelIds ?? []).join(", "));
3607
+ setModelId(existing?.modelIds?.[0] ?? "");
3608
+ };
3609
+ const openAddPicker = () => {
3610
+ closeEditor();
3611
+ setAddPickerQuery("");
3612
+ setAddPickerOpen(true);
3613
+ };
3614
+ const closeAddPicker = () => {
3615
+ setAddPickerOpen(false);
3616
+ setAddPickerQuery("");
3617
+ };
3618
+ const selectProviderFromPicker = (providerId) => {
3619
+ openEditor(providerId, { newOccInstance: isOccProviderId(providerId) });
3620
+ };
3621
+ const handleSave = async (providerId) => {
3622
+ if (!onUpdateProvider) return;
3623
+ setAttemptedSave(true);
3624
+ const isCustomEditor = providerId === "custom" || isCustomProviderId(providerId);
3625
+ const openAiChat = isOccProviderId(providerId);
3626
+ if (isCustomEditor) {
3627
+ const modelList = parseCustomProviderModels(models);
3628
+ if (!apiKey.trim() || !baseUrl.trim() || !displayName.trim()) return;
3629
+ if (modelList.length === 0) return;
3630
+ try {
3631
+ await onUpdateProvider({
3632
+ providerId,
3633
+ apiKey: apiKey.trim(),
3634
+ baseUrl: baseUrl.trim(),
3635
+ displayName: displayName.trim(),
3636
+ api,
3637
+ models: modelList,
3638
+ ...providerId === "custom" ? { createOccInstance: true } : {}
3639
+ });
3640
+ notify.success("Provider credentials applied to your active sessions.");
3641
+ closeEditor();
3642
+ closeAddPicker();
3643
+ } catch (error) {
3644
+ notify.error(error instanceof Error ? error.message : "Failed to update provider");
3645
+ }
3646
+ return;
3647
+ }
3648
+ const isNamedOcc = openAiChat && isNamedOccInstanceId(providerId);
3649
+ const isOccLike = openAiChat && (isNamedOcc || editingIsNewOccInstance);
3650
+ if (!apiKey.trim()) return;
3651
+ if (openAiChat && (!baseUrl.trim() || !modelId.trim())) return;
3652
+ if (isOccLike && !displayName.trim()) return;
3653
+ try {
3654
+ await onUpdateProvider({
3655
+ providerId,
3656
+ apiKey: apiKey.trim(),
3657
+ ...openAiChat ? {
3658
+ baseUrl: baseUrl.trim(),
3659
+ modelId: modelId.trim(),
3660
+ ...isOccLike ? { displayName: displayName.trim() } : {},
3661
+ ...editingIsNewOccInstance ? { createOccInstance: true } : {}
3662
+ } : {}
3663
+ });
3664
+ notify.success("Provider credentials applied to your active sessions.");
3665
+ closeEditor();
3666
+ closeAddPicker();
3667
+ } catch (error) {
3668
+ notify.error(error instanceof Error ? error.message : "Failed to update provider");
3669
+ }
3670
+ };
3671
+ const handleRemove = async (provider) => {
3672
+ if (!onRemoveProvider) return;
3673
+ try {
3674
+ await onRemoveProvider({ providerId: provider.id });
3675
+ if (editingProvider === provider.id) closeEditor();
3676
+ notify.success(`${provider.name} removed from your configured providers.`);
3677
+ setConfirmRemoveProvider(null);
3678
+ } catch (error) {
3679
+ notify.error(error instanceof Error ? error.message : "Failed to remove provider");
3680
+ }
3681
+ };
3682
+ return {
3683
+ activeProviders,
3684
+ addPickerOpen,
3685
+ addPickerQuery,
3686
+ api,
3687
+ apiKey,
3688
+ attemptedSave,
3689
+ baseUrl,
3690
+ canSave: useMemo2(() => {
3691
+ if (apiKey.trim().length === 0) return false;
3692
+ if (!editingProvider) return true;
3693
+ if (editingProvider === "custom" || isCustomProviderId(editingProvider)) {
3694
+ if (baseUrl.trim().length === 0) return false;
3695
+ if (parseCustomProviderModels(models).length === 0) return false;
3696
+ return displayName.trim().length > 0;
3697
+ }
3698
+ if (!isOccProviderId(editingProvider)) return true;
3699
+ if (baseUrl.trim().length === 0 || modelId.trim().length === 0) return false;
3700
+ if (isNamedOccInstanceId(editingProvider) || editingIsNewOccInstance) return displayName.trim().length > 0;
3701
+ return true;
3702
+ }, [
3703
+ apiKey,
3704
+ baseUrl,
3705
+ modelId,
3706
+ models,
3707
+ displayName,
3708
+ editingProvider,
3709
+ editingIsNewOccInstance
3710
+ ]),
3711
+ closeAddPicker,
3712
+ closeEditor,
3713
+ confirmRemoveProvider,
3714
+ credentialProviders,
3715
+ displayName,
3716
+ editingIsNewOccInstance,
3717
+ editingProvider,
3718
+ editingUnconfiguredProvider,
3719
+ filteredActiveProviders,
3720
+ filteredPickerAvailable,
3721
+ filteredPickerConfigured,
3722
+ handleRemove,
3723
+ handleSave,
3724
+ modelId,
3725
+ models,
3726
+ openAddPicker,
3727
+ openEditor,
3728
+ pickerHasResults,
3729
+ resetEditor,
3730
+ searchQuery,
3731
+ selectProviderFromPicker,
3732
+ setAddPickerOpen,
3733
+ setAddPickerQuery,
3734
+ setApi,
3735
+ setApiKey,
3736
+ setAttemptedSave,
3737
+ setBaseUrl,
3738
+ setConfirmRemoveProvider,
3739
+ setDisplayName,
3740
+ setEditingIsNewOccInstance,
3741
+ setEditingProvider,
3742
+ setModelId,
3743
+ setModels,
3744
+ setSearchQuery,
3745
+ setShowPassword,
3746
+ showPassword,
3747
+ unconfiguredProviders
3748
+ };
3749
+ }
3750
+ function providerMatchesQuery(provider, query) {
3751
+ if (!query) return true;
3752
+ const keywords = supportsOAuth(provider) ? isOAuthProvider(provider) ? "oauth sign-in login" : "api key oauth sign-in login" : provider.id === "custom" || isCustomProviderId(provider.id) ? "custom provider endpoint openai anthropic google compatible" : isOccProviderId(provider.id) ? "api key base url model name chat completions" : "";
3753
+ return [
3754
+ provider.name,
3755
+ provider.envVarName,
3756
+ provider.id,
3757
+ keywords
3758
+ ].join(" ").toLowerCase().includes(query);
3759
+ }
3760
+ function AddProviderPickerPanel({ addPickerQuery, credentialProvidersLength, filteredPickerAvailable, filteredPickerConfigured, pickerHasResults, onClose, onQueryChange, onSelect }) {
3761
+ return /* @__PURE__ */ jsxs("div", {
3762
+ className: "flex flex-col gap-3",
3763
+ children: [
3764
+ /* @__PURE__ */ jsxs("div", {
3765
+ className: "flex items-center gap-2",
3766
+ children: [/* @__PURE__ */ jsx(Button, {
3869
3767
  type: "button",
3870
- variant: "outline",
3871
- size: "sm",
3872
- disabled: disabled2,
3873
- onClick: openAddDialog,
3874
- children: [/* @__PURE__ */ jsx(Plus, { "data-icon": "inline-start" }), "Add models"]
3768
+ variant: "ghost",
3769
+ size: "icon-sm",
3770
+ "aria-label": "Back to providers",
3771
+ onClick: onClose,
3772
+ children: /* @__PURE__ */ jsx(ArrowLeft, {})
3773
+ }), /* @__PURE__ */ jsxs("div", {
3774
+ className: "min-w-0 flex-1",
3775
+ children: [/* @__PURE__ */ jsx("p", {
3776
+ className: "text-sm font-medium",
3777
+ children: "Add provider"
3778
+ }), /* @__PURE__ */ jsx("p", {
3779
+ className: "text-xs text-pretty text-muted-foreground",
3780
+ children: "Choose a provider to store encrypted credentials for this account."
3781
+ })]
3875
3782
  })]
3876
3783
  }),
3784
+ /* @__PURE__ */ jsxs(InputGroup, { children: [/* @__PURE__ */ jsx(InputGroupAddon, {
3785
+ align: "inline-start",
3786
+ children: /* @__PURE__ */ jsx(Search, {})
3787
+ }), /* @__PURE__ */ jsx(InputGroupInput, {
3788
+ type: "text",
3789
+ placeholder: "Search providers\u2026",
3790
+ value: addPickerQuery,
3791
+ onChange: (event) => onQueryChange(event.target.value),
3792
+ "aria-label": "Search providers to add"
3793
+ })] }),
3877
3794
  /* @__PURE__ */ jsx("div", {
3878
- className: "flex flex-col gap-3",
3879
- "data-testid": "runtime-models-list",
3880
- children: emptyMessage ? /* @__PURE__ */ jsxs("div", {
3881
- className: "flex flex-col items-center gap-3 py-8",
3882
- children: [/* @__PURE__ */ jsx("p", {
3883
- className: "px-1 text-center text-xs text-pretty text-muted-foreground",
3884
- children: emptyMessage
3885
- }), !normalizedFilter ? /* @__PURE__ */ jsxs(Button, {
3886
- type: "button",
3887
- variant: "outline",
3888
- size: "sm",
3889
- disabled: disabled2,
3890
- onClick: openAddDialog,
3891
- children: [/* @__PURE__ */ jsx(Plus, { "data-icon": "inline-start" }), "Add models"]
3795
+ className: "max-h-[min(24rem,50vh)] overflow-y-auto",
3796
+ children: !pickerHasResults ? /* @__PURE__ */ jsx("p", {
3797
+ className: "py-6 text-center text-xs text-pretty text-muted-foreground",
3798
+ children: credentialProvidersLength === 0 ? "No providers available." : "No matching providers found."
3799
+ }) : /* @__PURE__ */ jsxs("div", {
3800
+ className: "flex flex-col gap-3",
3801
+ children: [filteredPickerAvailable.length > 0 ? /* @__PURE__ */ jsx("div", {
3802
+ className: "flex flex-col gap-1",
3803
+ children: filteredPickerAvailable.map((provider) => /* @__PURE__ */ jsx(ProviderPickerRow, {
3804
+ provider,
3805
+ configured: false,
3806
+ onSelect: () => onSelect(provider.id)
3807
+ }, provider.id))
3808
+ }) : null, filteredPickerConfigured.length > 0 ? /* @__PURE__ */ jsxs("div", {
3809
+ className: "flex flex-col gap-1",
3810
+ children: [filteredPickerAvailable.length > 0 ? /* @__PURE__ */ jsx("p", {
3811
+ className: "px-2 pt-1 text-[0.6875rem] font-medium tracking-wide text-muted-foreground uppercase",
3812
+ children: "Already configured"
3813
+ }) : null, filteredPickerConfigured.map((provider) => /* @__PURE__ */ jsx(ProviderPickerRow, {
3814
+ provider,
3815
+ configured: true,
3816
+ onSelect: () => onSelect(provider.id)
3817
+ }, provider.id))]
3892
3818
  }) : null]
3893
- }) : groupedListed.map(([provider, models]) => /* @__PURE__ */ jsxs("div", {
3894
- className: "flex flex-col gap-1.5",
3895
- children: [/* @__PURE__ */ jsx("div", {
3896
- className: "px-1 text-xs font-medium text-muted-foreground",
3897
- children: providerLabel(provider)
3898
- }), models.map((model) => /* @__PURE__ */ jsx(ItemRow, {
3899
- icon: /* @__PURE__ */ jsx(ProviderBrandIcon, { provider: model.provider }),
3900
- title: model.name,
3901
- subtitle: `${providerLabel(model.provider)} \xB7 ${model.modelId}`,
3902
- trailing: /* @__PURE__ */ jsxs("div", {
3903
- className: "flex items-center",
3904
- children: [isDefaultModel(model) ? /* @__PURE__ */ jsx("span", {
3905
- className: "px-2 text-xs font-medium text-muted-foreground",
3906
- children: "Default"
3907
- }) : onSetDefaultModel ? /* @__PURE__ */ jsxs(Button, {
3908
- type: "button",
3909
- variant: "ghost",
3910
- size: "sm",
3911
- className: cn(HIT_AREA_EXPAND_DENSE_CLASS, "h-7 px-2 text-xs text-muted-foreground transition-[background-color,color,transform] duration-150 active:scale-[0.96]"),
3912
- disabled: disabled2,
3913
- "aria-label": `Set ${model.name} as default`,
3914
- onClick: () => onSetDefaultModel(model),
3915
- children: [/* @__PURE__ */ jsx(Star, { "data-icon": "inline-start" }), "Set default"]
3916
- }) : null, /* @__PURE__ */ jsxs(Button, {
3917
- type: "button",
3918
- variant: "ghost",
3919
- size: "sm",
3920
- className: cn(HIT_AREA_EXPAND_DENSE_CLASS, "h-7 px-2 text-xs text-muted-foreground transition-[background-color,color,transform] duration-150 hover:text-destructive active:scale-[0.96]"),
3921
- disabled: disabled2,
3922
- "aria-label": `Remove ${model.name}`,
3923
- onClick: () => onRemoveModel(model),
3924
- children: [/* @__PURE__ */ jsx(Trash2, { "data-icon": "inline-start" }), "Remove"]
3925
- })]
3926
- })
3927
- }, model.id))]
3928
- }, provider))
3929
- }),
3930
- /* @__PURE__ */ jsx(AddModelsDialog, {
3931
- open: addOpen,
3932
- configuredProviderIds,
3933
- discoverableProviderIds,
3934
- discoveringProviderId,
3935
- enabledModels: draft?.enabledModels,
3936
- modelOptions,
3937
- onAddModels,
3938
- onDiscoverProvider,
3939
- onOpenChange: setAddOpen,
3940
- providerLabel
3819
+ })
3941
3820
  })
3942
3821
  ]
3943
3822
  });
3944
3823
  }
3945
- var TRANSPORT_OPTIONS = [
3946
- {
3947
- value: "auto",
3948
- label: "Auto"
3949
- },
3950
- {
3951
- value: "sse",
3952
- label: "SSE"
3953
- },
3954
- {
3955
- value: "websocket",
3956
- label: "WebSocket"
3957
- }
3958
- ];
3959
- var DELIVERY_OPTIONS = [{
3960
- value: "all",
3961
- label: "All"
3962
- }, {
3963
- value: "one-at-a-time",
3964
- label: "One at a time"
3965
- }];
3966
- function HarnessRuntimeSection({ draft, updateDraft }) {
3967
- const disabled2 = !draft;
3824
+ function AddProviderOAuthPanel({ onBack, onConfigured, onOAuthLogin, provider }) {
3968
3825
  return /* @__PURE__ */ jsxs("div", {
3969
- className: "flex flex-col gap-2",
3826
+ className: "flex flex-col gap-3",
3827
+ children: [/* @__PURE__ */ jsxs("div", {
3828
+ className: "flex items-center gap-2",
3829
+ children: [/* @__PURE__ */ jsx(Button, {
3830
+ type: "button",
3831
+ variant: "ghost",
3832
+ size: "icon-sm",
3833
+ "aria-label": "Back to provider list",
3834
+ onClick: onBack,
3835
+ children: /* @__PURE__ */ jsx(ArrowLeft, {})
3836
+ }), /* @__PURE__ */ jsxs("div", {
3837
+ className: "min-w-0 flex-1",
3838
+ children: [/* @__PURE__ */ jsxs("p", {
3839
+ className: "text-sm font-medium",
3840
+ children: ["Configure ", provider.name]
3841
+ }), /* @__PURE__ */ jsx("p", {
3842
+ className: "text-xs text-pretty text-muted-foreground",
3843
+ children: "OAuth sign-in. Credentials are stored in auth.json for this account."
3844
+ })]
3845
+ })]
3846
+ }), /* @__PURE__ */ jsx(ProviderOAuthSignIn, {
3847
+ provider,
3848
+ onConfigured,
3849
+ onOAuthLogin
3850
+ }, provider.id)]
3851
+ });
3852
+ }
3853
+ function AddProviderEditorPanel({ provider, api, apiKey, baseUrl, modelId, models, displayName, showPassword, attemptedSave, isPending, canSave, onApiKeyChange, onApiChange, onBaseUrlChange, onModelIdChange, onModelsChange, onDisplayNameChange, onTogglePassword, onBack, onCancel, onConfigured, onOAuthLogin, onSave }) {
3854
+ const isCustomTemplate = provider.id === "custom" || isCustomProviderId(provider.id);
3855
+ const meta = PROVIDER_METADATA[provider.id] ?? (isCustomTemplate ? PROVIDER_METADATA["custom"] : {
3856
+ placeholder: "Enter credentials\u2026",
3857
+ help: "Stored securely in your local environment overrides."
3858
+ });
3859
+ const openAiChat = isOccProviderId(provider.id) && !isCustomTemplate;
3860
+ const oauthAvailable = supportsOAuth(provider);
3861
+ return /* @__PURE__ */ jsxs("div", {
3862
+ className: "flex flex-col gap-3",
3970
3863
  children: [
3971
- /* @__PURE__ */ jsx(ItemRow, {
3972
- title: "Compaction",
3973
- subtitle: "Summarize old turns when context fills up",
3974
- trailing: /* @__PURE__ */ jsx(Switch, {
3975
- "aria-label": "Compaction",
3976
- checked: draft?.compaction.enabled ?? false,
3977
- disabled: disabled2,
3978
- onCheckedChange: (enabled) => updateDraft((current) => ({
3979
- ...current,
3980
- compaction: {
3981
- ...current.compaction,
3982
- enabled
3983
- }
3984
- }))
3985
- })
3986
- }),
3987
- /* @__PURE__ */ jsx(ItemRow, {
3988
- title: "Auto-retry",
3989
- subtitle: "Retry failed provider requests",
3990
- trailing: /* @__PURE__ */ jsx(Switch, {
3991
- "aria-label": "Auto-retry",
3992
- checked: draft?.retry.enabled ?? false,
3993
- disabled: disabled2,
3994
- onCheckedChange: (enabled) => updateDraft((current) => ({
3995
- ...current,
3996
- retry: {
3997
- ...current.retry,
3998
- enabled
3999
- }
4000
- }))
4001
- })
3864
+ /* @__PURE__ */ jsxs("div", {
3865
+ className: "flex items-center gap-2",
3866
+ children: [/* @__PURE__ */ jsx(Button, {
3867
+ type: "button",
3868
+ variant: "ghost",
3869
+ size: "icon-sm",
3870
+ "aria-label": "Back to provider list",
3871
+ onClick: onBack,
3872
+ children: /* @__PURE__ */ jsx(ArrowLeft, {})
3873
+ }), /* @__PURE__ */ jsxs("div", {
3874
+ className: "min-w-0 flex-1",
3875
+ children: [/* @__PURE__ */ jsxs("p", {
3876
+ className: "text-sm font-medium",
3877
+ children: ["Configure ", provider.name]
3878
+ }), /* @__PURE__ */ jsx("p", {
3879
+ className: "text-xs text-pretty text-muted-foreground",
3880
+ children: isCustomTemplate ? "Name this custom provider, pick its API family, then enter its API key, https base URL, and model ids." : openAiChat ? "Name this OpenAI-compatible provider, then enter its API key, base URL, and model name." : `Stored as ${provider.envVarName} for this account.`
3881
+ })]
3882
+ })]
4002
3883
  }),
4003
- /* @__PURE__ */ jsx(ItemRow, {
4004
- title: "Transport",
4005
- trailing: /* @__PURE__ */ jsx(Select, {
4006
- "aria-label": "Transport",
4007
- className: "w-[160px]",
4008
- disabled: disabled2,
4009
- onValueChange: (value) => updateDraft((current) => ({
4010
- ...current,
4011
- transport: value
4012
- })),
4013
- options: [...TRANSPORT_OPTIONS],
4014
- value: draft?.transport ?? "auto"
4015
- })
3884
+ /* @__PURE__ */ jsx(ProviderCredentialFields, {
3885
+ attemptedSave,
3886
+ api,
3887
+ apiKey,
3888
+ baseUrl,
3889
+ modelId,
3890
+ models,
3891
+ displayName,
3892
+ isCustom: isCustomTemplate,
3893
+ openAiChat,
3894
+ placeholder: meta.placeholder,
3895
+ showPassword,
3896
+ onApiKeyChange,
3897
+ onApiChange,
3898
+ onBaseUrlChange,
3899
+ onModelIdChange,
3900
+ onModelsChange,
3901
+ onDisplayNameChange,
3902
+ onTogglePassword
4016
3903
  }),
4017
- /* @__PURE__ */ jsx(ItemRow, {
4018
- title: "Steering",
4019
- subtitle: "How queued steers are delivered mid-turn",
4020
- trailing: /* @__PURE__ */ jsx(Select, {
4021
- "aria-label": "Steering",
4022
- className: "w-[160px]",
4023
- disabled: disabled2,
4024
- onValueChange: (value) => updateDraft((current) => ({
4025
- ...current,
4026
- steeringMode: value
4027
- })),
4028
- options: [...DELIVERY_OPTIONS],
4029
- value: draft?.steeringMode ?? "all"
4030
- })
3904
+ /* @__PURE__ */ jsxs(Alert, {
3905
+ className: "px-3 py-2",
3906
+ children: [/* @__PURE__ */ jsx(Info, {}), /* @__PURE__ */ jsx(AlertDescription, {
3907
+ className: "text-xs text-pretty",
3908
+ children: meta.help
3909
+ })]
4031
3910
  }),
4032
- /* @__PURE__ */ jsx(ItemRow, {
4033
- title: "Follow-up",
4034
- subtitle: "How queued follow-ups run after the turn",
4035
- trailing: /* @__PURE__ */ jsx(Select, {
4036
- "aria-label": "Follow-up",
4037
- className: "w-[160px]",
4038
- disabled: disabled2,
4039
- onValueChange: (value) => updateDraft((current) => ({
4040
- ...current,
4041
- followUpMode: value
4042
- })),
4043
- options: [...DELIVERY_OPTIONS],
4044
- value: draft?.followUpMode ?? "all"
4045
- })
4046
- })
3911
+ /* @__PURE__ */ jsxs("div", {
3912
+ className: "flex items-center justify-end gap-1.5",
3913
+ children: [/* @__PURE__ */ jsx(Button, {
3914
+ type: "button",
3915
+ variant: "ghost",
3916
+ size: "sm",
3917
+ onClick: onCancel,
3918
+ children: "Cancel"
3919
+ }), /* @__PURE__ */ jsxs(Button, {
3920
+ type: "button",
3921
+ size: "sm",
3922
+ disabled: isPending || !canSave,
3923
+ onClick: onSave,
3924
+ children: [isPending ? /* @__PURE__ */ jsx(Spinner, { "data-icon": "inline-start" }) : null, "Save"]
3925
+ })]
3926
+ }),
3927
+ oauthAvailable ? /* @__PURE__ */ jsx(ProviderOAuthSignIn, {
3928
+ provider,
3929
+ onConfigured: onConfigured ?? onCancel,
3930
+ onOAuthLogin
3931
+ }, `${provider.id}-oauth`) : null
4047
3932
  ]
4048
3933
  });
4049
3934
  }
3935
+ function RemoveProviderConfirmDialog({ confirmRemoveProvider, isPending, onClose, onConfirm }) {
3936
+ return /* @__PURE__ */ jsx(AlertDialog, {
3937
+ open: confirmRemoveProvider !== null,
3938
+ onOpenChange: (open) => {
3939
+ if (!open) onClose();
3940
+ },
3941
+ children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [/* @__PURE__ */ jsxs("div", {
3942
+ className: "flex flex-col gap-2",
3943
+ children: [/* @__PURE__ */ jsxs(AlertDialogTitle, { children: [
3944
+ "Remove ",
3945
+ confirmRemoveProvider?.name ?? "provider",
3946
+ "?"
3947
+ ] }), /* @__PURE__ */ jsx(AlertDialogDescription, { children: "This clears stored credentials for this provider from your workspace settings. Active sessions will stop using it immediately." })]
3948
+ }), /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(AlertDialogCancel, {
3949
+ disabled: isPending,
3950
+ children: "Cancel"
3951
+ }), /* @__PURE__ */ jsxs(AlertDialogAction, {
3952
+ disabled: isPending || !confirmRemoveProvider,
3953
+ onClick: () => {
3954
+ if (confirmRemoveProvider) onConfirm(confirmRemoveProvider);
3955
+ },
3956
+ children: [isPending ? /* @__PURE__ */ jsx(Spinner, { "data-icon": "inline-start" }) : null, "Remove provider"]
3957
+ })] })] })
3958
+ });
3959
+ }
3960
+ function ProviderPickerRow({ configured, onSelect, provider }) {
3961
+ const openAiChat = isOccProviderId(provider.id);
3962
+ const isCustomTemplate = provider.id === "custom" || isCustomProviderId(provider.id);
3963
+ const oauthOnly = isOAuthProvider(provider);
3964
+ const oauthAvailable = supportsOAuth(provider);
3965
+ return /* @__PURE__ */ jsxs("button", {
3966
+ type: "button",
3967
+ className: cn("flex w-full items-center gap-3 rounded-lg px-2 py-2 text-left transition-[background-color,transform] duration-150", "hover:bg-muted/60 focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none active:scale-[0.96]"),
3968
+ onClick: onSelect,
3969
+ children: [/* @__PURE__ */ jsx("div", {
3970
+ className: "flex size-8 shrink-0 items-center justify-center rounded-[4px] border border-border/40 bg-background/60",
3971
+ children: /* @__PURE__ */ jsx(ProviderBrandIcon, {
3972
+ provider: isCustomTemplate ? "custom" : provider.id,
3973
+ api: provider.api,
3974
+ className: "text-foreground/70"
3975
+ })
3976
+ }), /* @__PURE__ */ jsxs("div", {
3977
+ className: "min-w-0 flex-1",
3978
+ children: [/* @__PURE__ */ jsx("div", {
3979
+ className: "truncate text-sm font-medium",
3980
+ children: provider.name
3981
+ }), /* @__PURE__ */ jsxs("div", {
3982
+ className: "truncate text-xs text-muted-foreground",
3983
+ children: [oauthOnly ? "OAuth sign-in" : oauthAvailable ? "API key or OAuth" : isCustomTemplate ? "API family + key + https base URL + models" : openAiChat ? "API key + base URL + model name" : provider.envVarName, configured ? " \xB7 Update" : ""]
3984
+ })]
3985
+ })]
3986
+ });
3987
+ }
3988
+ function ActiveProviderList({ providers, editingProvider, form, operation, actions, onEdit, onCancelEdit, onSave, onRemove, onOAuthLogin, onConfigured }) {
3989
+ const { api, apiKey, baseUrl, modelId, models, displayName, showPassword, attemptedSave, canSave } = form;
3990
+ const { isPending, canRemove } = operation;
3991
+ const { onApiKeyChange, onApiChange, onBaseUrlChange, onModelIdChange, onModelsChange, onDisplayNameChange, onTogglePassword } = actions;
3992
+ return /* @__PURE__ */ jsx("div", {
3993
+ className: "flex flex-col gap-1.5",
3994
+ children: providers.map((provider) => {
3995
+ const isEditing = editingProvider === provider.id;
3996
+ const isCustom = isCustomProviderId(provider.id);
3997
+ const meta = PROVIDER_METADATA[provider.id] ?? PROVIDER_METADATA[isCustom ? "custom" : "openai-chat-completions"];
3998
+ const openAiChat = isOccProviderId(provider.id);
3999
+ const oauthOnly = isOAuthProvider(provider);
4000
+ const oauthAvailable = supportsOAuth(provider);
4001
+ return /* @__PURE__ */ jsxs("div", {
4002
+ className: "flex flex-col",
4003
+ children: [/* @__PURE__ */ jsx(ItemRow, {
4004
+ interactive: false,
4005
+ icon: /* @__PURE__ */ jsx(ProviderBrandIcon, {
4006
+ provider: provider.id,
4007
+ api: provider.api,
4008
+ className: "text-foreground/80"
4009
+ }),
4010
+ title: provider.name,
4011
+ subtitle: oauthOnly ? "OAuth sign-in" : oauthAvailable ? "API key or OAuth" : isCustom ? "Custom provider \xB7 API key + base URL + models" : openAiChat ? "OpenAI-compatible \xB7 API key + base URL + model name" : provider.envVarName,
4012
+ trailing: /* @__PURE__ */ jsxs("div", {
4013
+ className: "flex items-center gap-0.5",
4014
+ children: [/* @__PURE__ */ jsxs(Button, {
4015
+ type: "button",
4016
+ variant: "ghost",
4017
+ size: "sm",
4018
+ className: cn(HIT_AREA_EXPAND_DENSE_CLASS, "h-7 px-2 text-xs text-muted-foreground transition-[background-color,color,transform] duration-150 hover:text-destructive active:scale-[0.96]"),
4019
+ disabled: isPending || !canRemove,
4020
+ "aria-label": `Remove ${provider.name}`,
4021
+ onClick: () => onRemove(provider),
4022
+ children: [/* @__PURE__ */ jsx(Trash2, { "data-icon": "inline-start" }), "Remove"]
4023
+ }), /* @__PURE__ */ jsxs(Button, {
4024
+ type: "button",
4025
+ variant: "ghost",
4026
+ size: "sm",
4027
+ className: cn(HIT_AREA_EXPAND_DENSE_CLASS, "h-7 px-2 text-xs transition-[background-color,transform] duration-150 active:scale-[0.96]"),
4028
+ disabled: isPending,
4029
+ onClick: () => {
4030
+ if (isEditing) {
4031
+ onCancelEdit();
4032
+ return;
4033
+ }
4034
+ onEdit(provider.id);
4035
+ },
4036
+ children: [!isEditing ? /* @__PURE__ */ jsx(Pencil, { "data-icon": "inline-start" }) : null, isEditing ? "Cancel" : "Update"]
4037
+ })]
4038
+ })
4039
+ }), isEditing ? /* @__PURE__ */ jsx(RowSurface, {
4040
+ tone: "inset",
4041
+ padding: "md",
4042
+ className: "flex flex-col gap-2 border-t border-border/30",
4043
+ children: oauthOnly ? /* @__PURE__ */ jsx(ProviderOAuthSignIn, {
4044
+ provider,
4045
+ onConfigured,
4046
+ onOAuthLogin
4047
+ }, provider.id) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
4048
+ /* @__PURE__ */ jsx(ProviderCredentialFields, {
4049
+ attemptedSave,
4050
+ api: provider.api ?? api,
4051
+ apiKey,
4052
+ baseUrl,
4053
+ modelId,
4054
+ models,
4055
+ displayName,
4056
+ isCustom,
4057
+ openAiChat,
4058
+ placeholder: meta.placeholder,
4059
+ showPassword,
4060
+ onApiKeyChange,
4061
+ onApiChange,
4062
+ onBaseUrlChange,
4063
+ onModelIdChange,
4064
+ onModelsChange,
4065
+ onDisplayNameChange: isCustom || provider.providerFamily !== void 0 ? onDisplayNameChange : void 0,
4066
+ onTogglePassword
4067
+ }),
4068
+ /* @__PURE__ */ jsxs(Alert, {
4069
+ className: "px-3 py-2",
4070
+ children: [/* @__PURE__ */ jsx(Info, {}), /* @__PURE__ */ jsx(AlertDescription, {
4071
+ className: "text-xs text-pretty",
4072
+ children: meta.help
4073
+ })]
4074
+ }),
4075
+ /* @__PURE__ */ jsx("div", {
4076
+ className: "flex items-center justify-end",
4077
+ children: /* @__PURE__ */ jsxs(Button, {
4078
+ type: "button",
4079
+ size: "sm",
4080
+ disabled: isPending || !canSave,
4081
+ onClick: () => onSave(provider.id),
4082
+ children: [isPending ? /* @__PURE__ */ jsx(Spinner, { "data-icon": "inline-start" }) : null, "Save"]
4083
+ })
4084
+ }),
4085
+ oauthAvailable ? /* @__PURE__ */ jsx(ProviderOAuthSignIn, {
4086
+ provider,
4087
+ onConfigured,
4088
+ onOAuthLogin
4089
+ }, `${provider.id}-oauth`) : null
4090
+ ] })
4091
+ }) : null]
4092
+ }, provider.id);
4093
+ })
4094
+ });
4095
+ }
4096
+ function ProviderCredentialsSection({ isLoading, isPending, onOAuthLogin, onRemoveProvider, onUpdateProvider, providers }) {
4097
+ const { activeProviders, addPickerOpen, addPickerQuery, api, apiKey, attemptedSave, baseUrl, canSave, closeAddPicker, closeEditor, confirmRemoveProvider, credentialProviders, displayName, editingProvider, editingUnconfiguredProvider, filteredActiveProviders, filteredPickerAvailable, filteredPickerConfigured, handleRemove, handleSave, modelId, models, openAddPicker, openEditor, pickerHasResults, searchQuery, selectProviderFromPicker, setAddPickerQuery, setApi, setApiKey, setBaseUrl, setConfirmRemoveProvider, setDisplayName, setModelId, setModels, setSearchQuery, setShowPassword, showPassword } = useProviderCredentialsController({
4098
+ onRemoveProvider,
4099
+ onUpdateProvider,
4100
+ providers
4101
+ });
4102
+ const form = {
4103
+ api,
4104
+ apiKey,
4105
+ baseUrl,
4106
+ modelId,
4107
+ models,
4108
+ displayName,
4109
+ showPassword,
4110
+ attemptedSave,
4111
+ canSave
4112
+ };
4113
+ const operation = {
4114
+ isPending,
4115
+ canRemove: Boolean(onRemoveProvider)
4116
+ };
4117
+ const actions = {
4118
+ onApiKeyChange: setApiKey,
4119
+ onApiChange: setApi,
4120
+ onBaseUrlChange: setBaseUrl,
4121
+ onModelIdChange: setModelId,
4122
+ onModelsChange: setModels,
4123
+ onDisplayNameChange: setDisplayName,
4124
+ onTogglePassword: () => setShowPassword((current) => !current)
4125
+ };
4126
+ return /* @__PURE__ */ jsxs(SettingsPane, {
4127
+ title: "Providers",
4128
+ description: "Locally, API keys are stored in `.env.local`. When deployed, they are stored encrypted in your account.",
4129
+ children: [addPickerOpen ? /* @__PURE__ */ jsx(AddProviderPickerPanel, {
4130
+ addPickerQuery,
4131
+ credentialProvidersLength: credentialProviders.length,
4132
+ filteredPickerAvailable,
4133
+ filteredPickerConfigured,
4134
+ pickerHasResults,
4135
+ onClose: closeAddPicker,
4136
+ onQueryChange: setAddPickerQuery,
4137
+ onSelect: selectProviderFromPicker
4138
+ }) : editingUnconfiguredProvider && isOAuthProvider(editingUnconfiguredProvider) ? /* @__PURE__ */ jsx(AddProviderOAuthPanel, {
4139
+ provider: editingUnconfiguredProvider,
4140
+ onBack: openAddPicker,
4141
+ onConfigured: closeEditor,
4142
+ onOAuthLogin
4143
+ }, editingUnconfiguredProvider.id) : editingUnconfiguredProvider ? /* @__PURE__ */ jsx(AddProviderEditorPanel, {
4144
+ provider: editingUnconfiguredProvider,
4145
+ api,
4146
+ apiKey,
4147
+ baseUrl,
4148
+ modelId,
4149
+ models,
4150
+ displayName,
4151
+ showPassword,
4152
+ attemptedSave,
4153
+ isPending,
4154
+ canSave,
4155
+ onApiKeyChange: setApiKey,
4156
+ onApiChange: setApi,
4157
+ onBaseUrlChange: setBaseUrl,
4158
+ onModelIdChange: setModelId,
4159
+ onModelsChange: setModels,
4160
+ onDisplayNameChange: setDisplayName,
4161
+ onTogglePassword: () => setShowPassword((current) => !current),
4162
+ onBack: openAddPicker,
4163
+ onCancel: closeEditor,
4164
+ onConfigured: closeEditor,
4165
+ onOAuthLogin,
4166
+ onSave: () => {
4167
+ if (editingProvider) handleSave(editingProvider);
4168
+ }
4169
+ }) : /* @__PURE__ */ jsxs("div", {
4170
+ className: "flex flex-col gap-3",
4171
+ children: [/* @__PURE__ */ jsxs("div", {
4172
+ className: "flex items-center gap-2",
4173
+ children: [/* @__PURE__ */ jsxs(InputGroup, {
4174
+ className: "flex-1",
4175
+ children: [/* @__PURE__ */ jsx(InputGroupAddon, {
4176
+ align: "inline-start",
4177
+ children: /* @__PURE__ */ jsx(Search, {})
4178
+ }), /* @__PURE__ */ jsx(InputGroupInput, {
4179
+ type: "text",
4180
+ placeholder: "Search credentials\u2026",
4181
+ value: searchQuery,
4182
+ onChange: (event) => setSearchQuery(event.target.value),
4183
+ "aria-label": "Search credentials",
4184
+ disabled: isLoading || activeProviders.length === 0
4185
+ })]
4186
+ }), /* @__PURE__ */ jsxs(Button, {
4187
+ type: "button",
4188
+ variant: "outline",
4189
+ size: "sm",
4190
+ disabled: isLoading || credentialProviders.length === 0,
4191
+ onClick: openAddPicker,
4192
+ children: [/* @__PURE__ */ jsx(Plus, { "data-icon": "inline-start" }), "Add provider"]
4193
+ })]
4194
+ }), isLoading ? /* @__PURE__ */ jsxs("div", {
4195
+ className: "flex items-center justify-center gap-2 py-6 text-xs text-muted-foreground",
4196
+ children: [/* @__PURE__ */ jsx(Spinner, { className: "size-3" }), /* @__PURE__ */ jsx("span", { children: "Loading\u2026" })]
4197
+ }) : activeProviders.length === 0 ? /* @__PURE__ */ jsxs("div", {
4198
+ className: "flex flex-col items-center gap-3 py-8",
4199
+ children: [/* @__PURE__ */ jsx("p", {
4200
+ className: "text-center text-xs text-pretty text-muted-foreground",
4201
+ children: "No providers configured"
4202
+ }), /* @__PURE__ */ jsxs(Button, {
4203
+ type: "button",
4204
+ variant: "outline",
4205
+ size: "sm",
4206
+ disabled: credentialProviders.length === 0,
4207
+ onClick: openAddPicker,
4208
+ children: [/* @__PURE__ */ jsx(Plus, { "data-icon": "inline-start" }), "Add provider"]
4209
+ })]
4210
+ }) : filteredActiveProviders.length === 0 ? /* @__PURE__ */ jsx("p", {
4211
+ className: "py-6 text-center text-xs text-pretty text-muted-foreground",
4212
+ children: "No matching providers found."
4213
+ }) : /* @__PURE__ */ jsx(ActiveProviderList, {
4214
+ providers: filteredActiveProviders,
4215
+ editingProvider,
4216
+ form,
4217
+ operation,
4218
+ actions,
4219
+ onEdit: openEditor,
4220
+ onCancelEdit: closeEditor,
4221
+ onSave: (providerId) => void handleSave(providerId),
4222
+ onRemove: setConfirmRemoveProvider,
4223
+ onOAuthLogin,
4224
+ onConfigured: closeEditor
4225
+ })]
4226
+ }), /* @__PURE__ */ jsx(RemoveProviderConfirmDialog, {
4227
+ confirmRemoveProvider,
4228
+ isPending,
4229
+ onClose: () => setConfirmRemoveProvider(null),
4230
+ onConfirm: (provider) => {
4231
+ handleRemove(provider);
4232
+ }
4233
+ })]
4234
+ });
4235
+ }
4050
4236
  var PROVENANCE_SOURCES = /* @__PURE__ */ new Set([
4051
4237
  "auto",
4052
4238
  "global",
@@ -4163,6 +4349,111 @@ function CatalogValueList({ addLabel, catalog, onChange, placeholder, values })
4163
4349
  })] })]
4164
4350
  });
4165
4351
  }
4352
+ var TRANSPORT_OPTIONS = [
4353
+ {
4354
+ value: "auto",
4355
+ label: "Auto"
4356
+ },
4357
+ {
4358
+ value: "sse",
4359
+ label: "SSE"
4360
+ },
4361
+ {
4362
+ value: "websocket",
4363
+ label: "WebSocket"
4364
+ }
4365
+ ];
4366
+ var DELIVERY_OPTIONS = [{
4367
+ value: "all",
4368
+ label: "All"
4369
+ }, {
4370
+ value: "one-at-a-time",
4371
+ label: "One at a time"
4372
+ }];
4373
+ function HarnessRuntimeSection({ draft, updateDraft }) {
4374
+ const disabled2 = !draft;
4375
+ return /* @__PURE__ */ jsxs("div", {
4376
+ className: "flex flex-col gap-2",
4377
+ children: [
4378
+ /* @__PURE__ */ jsx(ItemRow, {
4379
+ title: "Compaction",
4380
+ subtitle: "Summarize old turns when context fills up",
4381
+ trailing: /* @__PURE__ */ jsx(Switch, {
4382
+ "aria-label": "Compaction",
4383
+ checked: draft?.compaction.enabled ?? false,
4384
+ disabled: disabled2,
4385
+ onCheckedChange: (enabled) => updateDraft((current) => ({
4386
+ ...current,
4387
+ compaction: {
4388
+ ...current.compaction,
4389
+ enabled
4390
+ }
4391
+ }))
4392
+ })
4393
+ }),
4394
+ /* @__PURE__ */ jsx(ItemRow, {
4395
+ title: "Auto-retry",
4396
+ subtitle: "Retry failed provider requests",
4397
+ trailing: /* @__PURE__ */ jsx(Switch, {
4398
+ "aria-label": "Auto-retry",
4399
+ checked: draft?.retry.enabled ?? false,
4400
+ disabled: disabled2,
4401
+ onCheckedChange: (enabled) => updateDraft((current) => ({
4402
+ ...current,
4403
+ retry: {
4404
+ ...current.retry,
4405
+ enabled
4406
+ }
4407
+ }))
4408
+ })
4409
+ }),
4410
+ /* @__PURE__ */ jsx(ItemRow, {
4411
+ title: "Transport",
4412
+ trailing: /* @__PURE__ */ jsx(Select, {
4413
+ "aria-label": "Transport",
4414
+ className: "w-[160px]",
4415
+ disabled: disabled2,
4416
+ onValueChange: (value) => updateDraft((current) => ({
4417
+ ...current,
4418
+ transport: value
4419
+ })),
4420
+ options: [...TRANSPORT_OPTIONS],
4421
+ value: draft?.transport ?? "auto"
4422
+ })
4423
+ }),
4424
+ /* @__PURE__ */ jsx(ItemRow, {
4425
+ title: "Steering",
4426
+ subtitle: "How queued steers are delivered mid-turn",
4427
+ trailing: /* @__PURE__ */ jsx(Select, {
4428
+ "aria-label": "Steering",
4429
+ className: "w-[160px]",
4430
+ disabled: disabled2,
4431
+ onValueChange: (value) => updateDraft((current) => ({
4432
+ ...current,
4433
+ steeringMode: value
4434
+ })),
4435
+ options: [...DELIVERY_OPTIONS],
4436
+ value: draft?.steeringMode ?? "all"
4437
+ })
4438
+ }),
4439
+ /* @__PURE__ */ jsx(ItemRow, {
4440
+ title: "Follow-up",
4441
+ subtitle: "How queued follow-ups run after the turn",
4442
+ trailing: /* @__PURE__ */ jsx(Select, {
4443
+ "aria-label": "Follow-up",
4444
+ className: "w-[160px]",
4445
+ disabled: disabled2,
4446
+ onValueChange: (value) => updateDraft((current) => ({
4447
+ ...current,
4448
+ followUpMode: value
4449
+ })),
4450
+ options: [...DELIVERY_OPTIONS],
4451
+ value: draft?.followUpMode ?? "all"
4452
+ })
4453
+ })
4454
+ ]
4455
+ });
4456
+ }
4166
4457
  function ResourcesSection({ draft, onEnableSkillCommandsChange, onExtensionsChange, onPackageRowsChange, onPromptsChange, onRevert, onSave, onSkillsChange, onThemesChange, packageError, packageRows, resourceDirty, resources, resourceSummary, saving, scope = "harness", settingsLoading, updateDraft }) {
4167
4458
  const disabled2 = !draft || settingsLoading || !!packageError;
4168
4459
  const catalogSubtitle = resourceSummary.total === 0 ? "Empty" : resourceSummary.reloadRequired > 0 ? `${resourceSummary.total} \xB7 ${resourceSummary.reloadRequired} reload` : `${resourceSummary.total}`;
@@ -4679,6 +4970,14 @@ var SETTINGS_SECTION_REGISTRY = {
4679
4970
  icon: MessageSquare,
4680
4971
  group: "workspace"
4681
4972
  },
4973
+ mcp: {
4974
+ id: "mcp",
4975
+ order: 25,
4976
+ title: "MCP",
4977
+ ariaLabel: "MCP connection settings",
4978
+ icon: Plug,
4979
+ group: "workspace"
4980
+ },
4682
4981
  sandbox: {
4683
4982
  id: "sandbox",
4684
4983
  order: 30,
@@ -4768,7 +5067,7 @@ function PreferenceRow({ children, description, label }) {
4768
5067
  });
4769
5068
  }
4770
5069
  function useSettingsForm() {
4771
- const { isLoadingProviders, isUpdatingProvider, modelCatalog, onDiscoverModels, onOAuthLogin, onRemoveProvider, onThemePreferenceChange, onUpdateProvider, providers = [], saveSettings, settings, settingsLoading, themePreference } = useSettingsActionsContext();
5070
+ const { isLoadingMcp, isLoadingProviders, isUpdatingMcp, isUpdatingProvider, mcpConnections = [], modelCatalog, onDiscoverModels, onMcpOAuth, onOAuthLogin, onRemoveMcp, onRemoveProvider, onThemePreferenceChange, onUpdateMcp, onUpdateProvider, providers = [], saveSettings, settings, settingsLoading, themePreference } = useSettingsActionsContext();
4772
5071
  const { models, resources } = useChatPanelDataContext();
4773
5072
  const [draft, setDraft] = useState2(null);
4774
5073
  const [savingSection, setSavingSection] = useState2(null);
@@ -4832,11 +5131,17 @@ function useSettingsForm() {
4832
5131
  return "close";
4833
5132
  };
4834
5133
  return {
5134
+ isLoadingMcp,
4835
5135
  isLoadingProviders,
5136
+ isUpdatingMcp,
4836
5137
  isUpdatingProvider,
4837
- onThemePreferenceChange,
5138
+ mcpConnections,
5139
+ onMcpOAuth,
4838
5140
  onOAuthLogin,
5141
+ onRemoveMcp,
4839
5142
  onRemoveProvider,
5143
+ onThemePreferenceChange,
5144
+ onUpdateMcp,
4840
5145
  onUpdateProvider,
4841
5146
  providers,
4842
5147
  resources,
@@ -4907,7 +5212,7 @@ function SettingsDialogSession({ initialTab, onOpenChange, open }) {
4907
5212
  });
4908
5213
  }
4909
5214
  function SettingsDialogPaneContent({ activeTab, form, preferences, updatePreference }) {
4910
- const { isLoadingProviders, isUpdatingProvider, onThemePreferenceChange, onOAuthLogin, onRemoveProvider, onUpdateProvider, providers, resources, settingsLoading, themePreference, draft, savingSection, packageRows, packageError, modelFilter, setModelFilter, resourceSummary, modelOptions, modelDirty, resourceDirty, revertResourceDraft, revertModelDraft, updateDraft, handlePackageRowsChange, addModels, removeModel, setDefaultModel, discoverProvider, discoveringProviderId, saveSection } = form;
5215
+ const { isLoadingMcp, isLoadingProviders, isUpdatingMcp, isUpdatingProvider, mcpConnections, onMcpOAuth, onOAuthLogin, onRemoveMcp, onRemoveProvider, onThemePreferenceChange, onUpdateMcp, onUpdateProvider, providers, resources, settingsLoading, themePreference, draft, savingSection, packageRows, packageError, modelFilter, setModelFilter, resourceSummary, modelOptions, modelDirty, resourceDirty, revertResourceDraft, revertModelDraft, updateDraft, handlePackageRowsChange, addModels, removeModel, setDefaultModel, discoverProvider, discoveringProviderId, saveSection } = form;
4911
5216
  const resourcesPane = (scope) => /* @__PURE__ */ jsx(ResourcesSection, {
4912
5217
  scope,
4913
5218
  draft,
@@ -5017,6 +5322,14 @@ function SettingsDialogPaneContent({ activeTab, form, preferences, updatePrefere
5017
5322
  })
5018
5323
  })]
5019
5324
  }),
5325
+ mcp: () => /* @__PURE__ */ jsx(McpConnectionsSection, {
5326
+ connections: mcpConnections,
5327
+ isLoading: isLoadingMcp ?? false,
5328
+ isPending: isUpdatingMcp ?? false,
5329
+ onOAuth: onMcpOAuth,
5330
+ onRemove: onRemoveMcp,
5331
+ onUpsert: onUpdateMcp
5332
+ }),
5020
5333
  sandbox: () => /* @__PURE__ */ jsx(SandboxProviderSection, {
5021
5334
  isLoading: isLoadingProviders ?? false,
5022
5335
  isPending: isUpdatingProvider ?? false,