@syncended/dsh-automations 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -11,6 +11,7 @@ window.__ModuleLoader__.load({
11
11
  // the same-origin HTTP API at /api/automations; every mutation carries the
12
12
  // x-dsh-automation-client fence header plus an application/json body.
13
13
  const React = require("react");
14
+ const { createPortal } = require("react-dom");
14
15
  const h = React.createElement;
15
16
  const { useCallback, useEffect, useId, useRef, useState, useSyncExternalStore } = React;
16
17
  const {
@@ -23,10 +24,12 @@ window.__ModuleLoader__.load({
23
24
  IconChevronDownOutline14,
24
25
  IconChevronLeftOutline14,
25
26
  IconEditOutline16,
27
+ IconGlobeOutline14,
26
28
  IconPlayOutline16,
27
29
  IconPlusOutline16,
28
30
  IconRefreshOutline16,
29
31
  IconSettingsOutline16,
32
+ IconThinkOutline14,
30
33
  IconTrashOutline16,
31
34
  IconWarningOutline16,
32
35
  } = require("@deepseek-ai/dsh-client-ui-primitives");
@@ -50,26 +53,77 @@ window.__ModuleLoader__.load({
50
53
  }
51
54
  })();
52
55
 
53
- const TIMEZONES = Array.from(
54
- new Set([
55
- BROWSER_TIMEZONE,
56
- "UTC",
57
- "America/New_York",
58
- "America/Chicago",
59
- "America/Denver",
60
- "America/Los_Angeles",
61
- "Europe/London",
62
- "Europe/Paris",
63
- "Europe/Berlin",
64
- "Asia/Shanghai",
65
- "Asia/Tokyo",
66
- "Asia/Singapore",
67
- "Asia/Kolkata",
68
- "Australia/Sydney",
69
- ]),
56
+ const POPULAR_TIMEZONE_CANDIDATES = [
57
+ BROWSER_TIMEZONE,
58
+ "UTC",
59
+ "America/New_York",
60
+ "America/Los_Angeles",
61
+ "America/Sao_Paulo",
62
+ "Europe/London",
63
+ "Europe/Berlin",
64
+ "Europe/Moscow",
65
+ "Asia/Dubai",
66
+ "Asia/Kolkata",
67
+ "Asia/Shanghai",
68
+ "Asia/Tokyo",
69
+ "Australia/Sydney",
70
+ ];
71
+ const PREFERRED_TIMEZONE_ALIASES = [
72
+ "Africa/Asmara",
73
+ "America/Argentina/Buenos_Aires",
74
+ "America/Atikokan",
75
+ "America/Indiana/Indianapolis",
76
+ "America/Kentucky/Louisville",
77
+ "America/Nuuk",
78
+ "Asia/Ho_Chi_Minh",
79
+ "Asia/Kathmandu",
80
+ "Asia/Kolkata",
81
+ "Asia/Yangon",
82
+ "Atlantic/Faroe",
83
+ "Europe/Kyiv",
84
+ "Pacific/Chuuk",
85
+ "Pacific/Kanton",
86
+ "Pacific/Pohnpei",
87
+ ];
88
+ const TIMEZONE_ALIASES_BY_ZONE = new Map();
89
+ for (const alias of PREFERRED_TIMEZONE_ALIASES) {
90
+ const zone = canonicalTimezone(alias);
91
+ if (!zone || zone === alias) continue;
92
+ const aliases = TIMEZONE_ALIASES_BY_ZONE.get(zone) || [];
93
+ aliases.push(alias);
94
+ TIMEZONE_ALIASES_BY_ZONE.set(zone, aliases);
95
+ }
96
+ const POPULAR_TIMEZONES = Array.from(
97
+ new Set(POPULAR_TIMEZONE_CANDIDATES.map(canonicalTimezone).filter(Boolean)),
70
98
  );
99
+ const SUPPORTED_TIMEZONES = (() => {
100
+ let discovered = [];
101
+ try {
102
+ discovered = typeof Intl.supportedValuesOf === "function"
103
+ ? Intl.supportedValuesOf("timeZone")
104
+ : [];
105
+ } catch {
106
+ discovered = [];
107
+ }
108
+ return Array.from(
109
+ new Set([
110
+ ...POPULAR_TIMEZONES,
111
+ ...TIMEZONE_ALIASES_BY_ZONE.keys(),
112
+ ...discovered.filter((zone) => typeof zone === "string" && zone !== ""),
113
+ ]),
114
+ ).sort((left, right) => left.localeCompare(right));
115
+ })();
116
+ const TIMEZONE_SEARCH_INDEX = SUPPORTED_TIMEZONES.map((zone) => {
117
+ const aliases = TIMEZONE_ALIASES_BY_ZONE.get(zone) || [];
118
+ const preferred = aliases[0] || zone;
119
+ return {
120
+ zone,
121
+ city: timezoneCityLabel(preferred),
122
+ search: timezoneSearchKey([zone, ...aliases].join(" ")),
123
+ };
124
+ });
71
125
 
72
- const REASONING_EFFORTS = ["low", "medium", "high"];
126
+ const MODEL_METADATA_CACHE = new Map();
73
127
  const OVERLAP_OPTIONS = [
74
128
  { value: "skip", label: "skip", hint: "Skip the run if a previous run is still active." },
75
129
  { value: "queue", label: "queue", hint: "Defer the run until the previous run finishes." },
@@ -147,6 +201,138 @@ window.__ModuleLoader__.load({
147
201
  });
148
202
  }
149
203
 
204
+ function canonicalTimezone(value) {
205
+ const timezone = String(value || "").trim();
206
+ if (timezone === "") return null;
207
+ try {
208
+ return new Intl.DateTimeFormat("en-US", { timeZone: timezone }).resolvedOptions().timeZone;
209
+ } catch {
210
+ return null;
211
+ }
212
+ }
213
+
214
+ function timezoneCityLabel(zone) {
215
+ if (zone === "UTC") return "UTC";
216
+ const segments = zone.split("/");
217
+ return (segments[segments.length - 1] || zone).replace(/_/g, " ");
218
+ }
219
+
220
+ function timezonePreferredValue(zone) {
221
+ return TIMEZONE_ALIASES_BY_ZONE.get(zone)?.[0] || zone;
222
+ }
223
+
224
+ function timezoneSearchKey(zone) {
225
+ const literal = zone.toLowerCase();
226
+ const expanded = literal
227
+ .normalize("NFKD")
228
+ .replace(/[\u0300-\u036f]/g, "")
229
+ .replace(/[\/_\-.]+/g, " ")
230
+ .replace(/\s+/g, " ")
231
+ .trim();
232
+ return expanded;
233
+ }
234
+
235
+ function timezoneOffsetLabel(zone, at = new Date()) {
236
+ try {
237
+ const formatter = new Intl.DateTimeFormat("en-US", {
238
+ timeZone: zone,
239
+ timeZoneName: "longOffset",
240
+ hour: "2-digit",
241
+ });
242
+ const value = formatter.formatToParts(at).find((part) => part.type === "timeZoneName")?.value;
243
+ if (!value) return "UTC offset unavailable";
244
+ if (value === "GMT") return "UTC+00:00";
245
+ return value.replace(/^GMT/, "UTC");
246
+ } catch {
247
+ return "UTC offset unavailable";
248
+ }
249
+ }
250
+
251
+ function timezoneSearchResults(query, currentValue, limit = 30) {
252
+ const rawQuery = String(query || "").trim().toLowerCase();
253
+ const current = canonicalTimezone(currentValue);
254
+ if (rawQuery === "") {
255
+ return Array.from(
256
+ new Set([current, BROWSER_TIMEZONE, ...POPULAR_TIMEZONES].filter(Boolean)),
257
+ ).slice(0, 12);
258
+ }
259
+ const normalizedQuery = timezoneSearchKey(rawQuery);
260
+ if (normalizedQuery === "") return [];
261
+ const terms = normalizedQuery.split(" ").filter(Boolean);
262
+ return TIMEZONE_SEARCH_INDEX
263
+ .filter((entry) => terms.every((term) => entry.search.includes(term)))
264
+ .map((entry) => {
265
+ const canonical = entry.zone.toLowerCase();
266
+ const preferred = timezonePreferredValue(entry.zone).toLowerCase();
267
+ const city = timezoneSearchKey(entry.city);
268
+ let score = 5;
269
+ if (canonical === rawQuery || preferred === rawQuery) score = 0;
270
+ else if (city === normalizedQuery) score = 1;
271
+ else if (city.startsWith(normalizedQuery)) score = 2;
272
+ else if (canonical.split("/").some((segment) => timezoneSearchKey(segment).startsWith(normalizedQuery))) score = 3;
273
+ else if (entry.search.startsWith(normalizedQuery)) score = 4;
274
+ return { ...entry, score };
275
+ })
276
+ .sort((left, right) => left.score - right.score || left.city.localeCompare(right.city) || left.zone.localeCompare(right.zone))
277
+ .slice(0, limit)
278
+ .map((entry) => entry.zone);
279
+ }
280
+
281
+ function comboboxSearchResults(options, query, limit = 30) {
282
+ const rawQuery = String(query || "").trim().toLowerCase();
283
+ if (rawQuery === "") return options.slice(0, limit);
284
+ const normalizedQuery = timezoneSearchKey(rawQuery);
285
+ if (normalizedQuery === "") return [];
286
+ const terms = normalizedQuery.split(" ").filter(Boolean);
287
+ return options
288
+ .map((option, order) => {
289
+ const identifier = String(option.value ?? option.id).toLowerCase();
290
+ const label = timezoneSearchKey(option.label || "");
291
+ const search = timezoneSearchKey(
292
+ [option.label, option.detail, option.value, ...(option.aliases || [])].filter(Boolean).join(" "),
293
+ );
294
+ if (!terms.every((term) => search.includes(term))) return null;
295
+ let score = option.custom ? 6 : 5;
296
+ if (!option.custom && identifier === rawQuery) score = 0;
297
+ else if (!option.custom && label === normalizedQuery) score = 1;
298
+ else if (!option.custom && label.startsWith(normalizedQuery)) score = 2;
299
+ else if (!option.custom && identifier.split(/[\/:._-]+/).some((segment) => segment.startsWith(rawQuery))) score = 3;
300
+ else if (!option.custom && search.startsWith(normalizedQuery)) score = 4;
301
+ return { option, order, score };
302
+ })
303
+ .filter(Boolean)
304
+ .sort((left, right) => left.score - right.score || left.order - right.order)
305
+ .slice(0, limit)
306
+ .map((entry) => entry.option);
307
+ }
308
+
309
+ function timezoneNavigationIndex(key, current, resultCount, reopening) {
310
+ if (resultCount === 0) return -1;
311
+ if (reopening) return key === "ArrowDown" ? 0 : resultCount - 1;
312
+ if (key === "ArrowDown") return current < 0 || current >= resultCount - 1 ? 0 : current + 1;
313
+ return current <= 0 ? resultCount - 1 : current - 1;
314
+ }
315
+
316
+ function timezoneOptionPresentation(zone, offsetCache) {
317
+ let offset = offsetCache?.get(zone);
318
+ if (!offset) {
319
+ offset = timezoneOffsetLabel(zone);
320
+ offsetCache?.set(zone, offset);
321
+ }
322
+ const current = BROWSER_TIMEZONE === zone;
323
+ const preferred = timezonePreferredValue(zone);
324
+ if (zone === "UTC") {
325
+ return {
326
+ label: "UTC",
327
+ detail: offset + " now · Coordinated Universal Time" + (current ? " · Your browser time zone" : ""),
328
+ };
329
+ }
330
+ return {
331
+ label: timezoneCityLabel(preferred),
332
+ detail: preferred + " · " + offset + " now" + (current ? " · Your browser time zone" : ""),
333
+ };
334
+ }
335
+
150
336
  function browserTimezone() {
151
337
  return BROWSER_TIMEZONE;
152
338
  }
@@ -203,6 +389,7 @@ window.__ModuleLoader__.load({
203
389
  try {
204
390
  response = await fetch(API_PREFIX + path, Object.assign({}, options, { headers }));
205
391
  } catch (error) {
392
+ if (error && error.name === "AbortError") throw error;
206
393
  throw new Error("Network error: " + errMessage(error));
207
394
  }
208
395
  const text = await response.text();
@@ -446,6 +633,588 @@ window.__ModuleLoader__.load({
446
633
  );
447
634
  }
448
635
 
636
+ function useComboboxPopoverLayout(open, anchorRef, panelRef) {
637
+ const [layout, setLayout] = useState(null);
638
+ useEffect(() => {
639
+ if (!open) {
640
+ setLayout(null);
641
+ return undefined;
642
+ }
643
+ const place = () => {
644
+ const anchor = anchorRef.current;
645
+ if (!anchor) return;
646
+ const rect = anchor.getBoundingClientRect();
647
+ const panel = panelRef.current;
648
+ const margin = 12;
649
+ const gap = 4;
650
+ const width = Math.min(Math.max(rect.width, 280), window.innerWidth - margin * 2);
651
+ const left = Math.min(Math.max(rect.left, margin), window.innerWidth - width - margin);
652
+ const desiredHeight = Math.min(360, panel?.scrollHeight || 360);
653
+ const below = window.innerHeight - rect.bottom - gap - margin;
654
+ const above = rect.top - gap - margin;
655
+ const side = below >= Math.min(desiredHeight, 220) || below >= above ? "bottom" : "top";
656
+ const available = Math.max(80, side === "bottom" ? below : above);
657
+ const maxHeight = Math.min(360, available);
658
+ const renderedHeight = Math.min(panel?.offsetHeight || desiredHeight, maxHeight);
659
+ const top = side === "bottom"
660
+ ? rect.bottom + gap
661
+ : Math.max(margin, rect.top - gap - renderedHeight);
662
+ setLayout((current) => {
663
+ const next = { left, top, width, maxHeight, side };
664
+ return current
665
+ && current.left === next.left
666
+ && current.top === next.top
667
+ && current.width === next.width
668
+ && current.maxHeight === next.maxHeight
669
+ && current.side === next.side
670
+ ? current
671
+ : next;
672
+ });
673
+ };
674
+ const frame = requestAnimationFrame(place);
675
+ window.addEventListener("scroll", place, true);
676
+ window.addEventListener("resize", place);
677
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(place);
678
+ if (anchorRef.current) observer?.observe(anchorRef.current);
679
+ if (panelRef.current) observer?.observe(panelRef.current);
680
+ return () => {
681
+ cancelAnimationFrame(frame);
682
+ observer?.disconnect();
683
+ window.removeEventListener("scroll", place, true);
684
+ window.removeEventListener("resize", place);
685
+ };
686
+ }, [open, anchorRef, panelRef]);
687
+ return layout;
688
+ }
689
+
690
+ function EditableCombobox(props) {
691
+ const {
692
+ id,
693
+ value,
694
+ onChange,
695
+ optionsForQuery,
696
+ selectedId,
697
+ commitExactValue,
698
+ placeholder,
699
+ Icon,
700
+ listboxLabel,
701
+ initialTitle,
702
+ searchTitle,
703
+ emptyText,
704
+ hintText,
705
+ resultNoun,
706
+ invalidMessage,
707
+ invalid = false,
708
+ disabled = false,
709
+ resumeSearch: resumeSearchProp,
710
+ panelNotice = null,
711
+ onOpen,
712
+ } = props;
713
+ const [open, setOpen] = useState(false);
714
+ const [searching, setSearching] = useState(false);
715
+ const [activeIndex, setActiveIndex] = useState(-1);
716
+ const [announcement, setAnnouncement] = useState("");
717
+ const anchorRef = useRef(null);
718
+ const inputRef = useRef(null);
719
+ const panelRef = useRef(null);
720
+ const listboxId = id + "-options";
721
+ const query = searching ? value : "";
722
+ const results = optionsForQuery(query);
723
+ const active = activeIndex >= 0 && activeIndex < results.length ? activeIndex : -1;
724
+ const resumeSearch = resumeSearchProp === undefined
725
+ ? value.trim() !== "" && selectedId === null
726
+ : resumeSearchProp;
727
+ const popoverLayout = useComboboxPopoverLayout(open, anchorRef, panelRef);
728
+ const close = () => {
729
+ setOpen(false);
730
+ setSearching(false);
731
+ setActiveIndex(-1);
732
+ setAnnouncement("");
733
+ };
734
+ const show = () => {
735
+ setAnnouncement("");
736
+ setOpen(true);
737
+ onOpen?.();
738
+ };
739
+ const choose = (option) => {
740
+ if (option.disabled) return;
741
+ if (option.value !== value) onChange(option.value);
742
+ close();
743
+ requestAnimationFrame(() => inputRef.current?.focus());
744
+ };
745
+
746
+ useEffect(() => {
747
+ if (!open) return undefined;
748
+ const dismiss = (event) => {
749
+ const target = event.target;
750
+ if (!(target instanceof Node)) return;
751
+ if (anchorRef.current?.contains(target) || panelRef.current?.contains(target)) return;
752
+ close();
753
+ };
754
+ document.addEventListener("pointerdown", dismiss, true);
755
+ return () => document.removeEventListener("pointerdown", dismiss, true);
756
+ }, [open]);
757
+
758
+ useEffect(() => {
759
+ if (!open) return undefined;
760
+ const timer = window.setTimeout(() => {
761
+ setAnnouncement(
762
+ results.length === 0
763
+ ? "No matching " + resultNoun + "s."
764
+ : results.length + " " + resultNoun + (results.length === 1 ? " suggestion available." : " suggestions available."),
765
+ );
766
+ }, 250);
767
+ return () => window.clearTimeout(timer);
768
+ }, [open, query, results.length]);
769
+
770
+ useEffect(() => {
771
+ if (!open || active < 0) return;
772
+ document.getElementById(listboxId + "-option-" + active)?.scrollIntoView({ block: "nearest" });
773
+ }, [open, active, listboxId]);
774
+
775
+ const onInputKeyDown = (event) => {
776
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
777
+ event.preventDefault();
778
+ const navigationResults = !open && resumeSearch
779
+ ? optionsForQuery(value)
780
+ : results;
781
+ if (!open) {
782
+ setSearching(resumeSearch);
783
+ show();
784
+ }
785
+ if (navigationResults.length === 0) {
786
+ setActiveIndex(-1);
787
+ return;
788
+ }
789
+ setActiveIndex((current) =>
790
+ timezoneNavigationIndex(event.key, current, navigationResults.length, !open),
791
+ );
792
+ return;
793
+ }
794
+ if (event.key === "Enter" && open) {
795
+ event.preventDefault();
796
+ if (active >= 0) {
797
+ choose(results[active]);
798
+ return;
799
+ }
800
+ const exact = commitExactValue(value);
801
+ if (exact !== null) choose({ id: exact, value: exact });
802
+ else setAnnouncement(invalidMessage);
803
+ return;
804
+ }
805
+ if (event.key === "Escape" && open) {
806
+ event.preventDefault();
807
+ event.stopPropagation();
808
+ (event.nativeEvent || event).__dshAutomationsNested = true;
809
+ close();
810
+ return;
811
+ }
812
+ if (event.key === "Tab" && open) close();
813
+ };
814
+
815
+ const panel = open
816
+ ? h(
817
+ "div",
818
+ {
819
+ ref: panelRef,
820
+ className: "dsh-auto-combobox-popover",
821
+ style: popoverLayout
822
+ ? {
823
+ left: popoverLayout.left,
824
+ top: popoverLayout.top,
825
+ width: popoverLayout.width,
826
+ maxHeight: popoverLayout.maxHeight,
827
+ }
828
+ : { left: 12, top: 12, width: 320, visibility: "hidden" },
829
+ "data-side": popoverLayout?.side,
830
+ "data-dsh-auto-combobox-popover": "true",
831
+ },
832
+ h(
833
+ "div",
834
+ { className: "dsh-auto-combobox-popover-title" },
835
+ searching && query.trim() !== "" ? searchTitle : initialTitle,
836
+ ),
837
+ panelNotice
838
+ ? h("div", { className: "dsh-auto-combobox-notice", role: "status" }, panelNotice)
839
+ : null,
840
+ h(
841
+ "div",
842
+ {
843
+ id: listboxId,
844
+ className: "dsh-auto-combobox-list",
845
+ role: "listbox",
846
+ "aria-label": listboxLabel,
847
+ },
848
+ results.length > 0
849
+ ? results.map((option, index) => {
850
+ const optionId = listboxId + "-option-" + index;
851
+ const OptionIcon = option.Icon || Icon;
852
+ const isSelected = selectedId === option.id;
853
+ return h(
854
+ "div",
855
+ {
856
+ key: option.id,
857
+ id: optionId,
858
+ className: "dsh-auto-combobox-option",
859
+ role: "option",
860
+ "aria-selected": isSelected,
861
+ "aria-disabled": option.disabled ? "true" : undefined,
862
+ "data-active": active === index ? "true" : undefined,
863
+ "data-disabled": option.disabled ? "true" : undefined,
864
+ onPointerMove: () => {
865
+ if (!option.disabled) setActiveIndex(index);
866
+ },
867
+ onPointerDown: (event) => event.preventDefault(),
868
+ onClick: () => choose(option),
869
+ },
870
+ OptionIcon
871
+ ? h("span", { className: "dsh-auto-combobox-option-icon", "aria-hidden": "true" }, h(OptionIcon))
872
+ : null,
873
+ h(
874
+ "span",
875
+ { className: "dsh-auto-picker-item-copy" },
876
+ h("span", { className: "dsh-auto-picker-item-label" }, option.label),
877
+ option.detail
878
+ ? h("span", { className: "dsh-auto-picker-item-detail" }, option.detail)
879
+ : null,
880
+ ),
881
+ isSelected
882
+ ? h("span", { className: "dsh-auto-combobox-option-check", "aria-hidden": "true" }, h(IconCheckOutline16))
883
+ : null,
884
+ );
885
+ })
886
+ : h(
887
+ "div",
888
+ {
889
+ className: "dsh-auto-combobox-empty",
890
+ role: "option",
891
+ "aria-disabled": "true",
892
+ "aria-selected": false,
893
+ },
894
+ emptyText,
895
+ ),
896
+ ),
897
+ hintText ? h("p", { className: "dsh-auto-combobox-popover-hint" }, hintText) : null,
898
+ )
899
+ : null;
900
+
901
+ return h(
902
+ React.Fragment,
903
+ null,
904
+ h(
905
+ "span",
906
+ {
907
+ ref: anchorRef,
908
+ className: "dsh-auto-combobox-control" + (Icon ? "" : " dsh-auto-combobox-control-no-icon"),
909
+ "data-open": open ? "true" : undefined,
910
+ onPointerDown: (event) => {
911
+ if (disabled) return;
912
+ if (event.target === inputRef.current) return;
913
+ event.preventDefault();
914
+ if (open) close();
915
+ else {
916
+ setSearching(resumeSearch);
917
+ show();
918
+ requestAnimationFrame(() => {
919
+ inputRef.current?.focus();
920
+ inputRef.current?.select();
921
+ });
922
+ }
923
+ },
924
+ },
925
+ Icon
926
+ ? h("span", { className: "dsh-auto-combobox-control-icon", "aria-hidden": "true" }, h(Icon))
927
+ : null,
928
+ h("input", {
929
+ ref: inputRef,
930
+ id,
931
+ className: "dsh-auto-combobox-input",
932
+ type: "text",
933
+ role: "combobox",
934
+ value,
935
+ placeholder,
936
+ disabled,
937
+ autoComplete: "off",
938
+ spellCheck: false,
939
+ "aria-autocomplete": "list",
940
+ "aria-haspopup": "listbox",
941
+ "aria-expanded": open,
942
+ "aria-controls": open ? listboxId : undefined,
943
+ "aria-activedescendant": open && active >= 0 ? listboxId + "-option-" + active : undefined,
944
+ "aria-invalid": !open && invalid ? "true" : undefined,
945
+ onFocus: (event) => {
946
+ setSearching(resumeSearch);
947
+ setActiveIndex(-1);
948
+ show();
949
+ event.currentTarget.select();
950
+ },
951
+ onClick: () => {
952
+ if (open) return;
953
+ setSearching(resumeSearch);
954
+ setActiveIndex(-1);
955
+ show();
956
+ },
957
+ onBlur: (event) => {
958
+ const next = event.relatedTarget;
959
+ if (next instanceof Node && (anchorRef.current?.contains(next) || panelRef.current?.contains(next))) return;
960
+ close();
961
+ },
962
+ onChange: (event) => {
963
+ setSearching(true);
964
+ setActiveIndex(0);
965
+ show();
966
+ onChange(event.target.value);
967
+ },
968
+ onKeyDown: onInputKeyDown,
969
+ }),
970
+ h(
971
+ "span",
972
+ {
973
+ className: "dsh-auto-combobox-chevron" + (open ? " dsh-auto-combobox-chevron-open" : ""),
974
+ "aria-hidden": "true",
975
+ },
976
+ h(IconChevronDownOutline14),
977
+ ),
978
+ ),
979
+ open && typeof document !== "undefined" ? createPortal(panel, document.body) : null,
980
+ h("span", { className: "dsh-auto-sr-only", role: "status", "aria-live": "polite" }, announcement),
981
+ );
982
+ }
983
+
984
+ function TimeZonePicker({ id, value, onChange }) {
985
+ const offsetCacheRef = useRef(new Map());
986
+ const selected = canonicalTimezone(value);
987
+ const optionsForQuery = (query) => timezoneSearchResults(query, value).map((zone) => {
988
+ const copy = timezoneOptionPresentation(zone, offsetCacheRef.current);
989
+ return {
990
+ id: zone,
991
+ value: timezonePreferredValue(zone),
992
+ label: copy.label,
993
+ detail: copy.detail,
994
+ };
995
+ });
996
+ return h(EditableCombobox, {
997
+ id,
998
+ value,
999
+ onChange,
1000
+ optionsForQuery,
1001
+ selectedId: selected,
1002
+ commitExactValue: (raw) => {
1003
+ const canonical = canonicalTimezone(raw);
1004
+ return canonical ? timezonePreferredValue(canonical) : null;
1005
+ },
1006
+ placeholder: "Search city or time zone",
1007
+ Icon: IconGlobeOutline14,
1008
+ listboxLabel: "Time zones",
1009
+ initialTitle: "Suggested time zones",
1010
+ searchTitle: "Matching time zones",
1011
+ emptyText: "No matching time zones. You can still enter a recognized IANA time zone, such as Europe/Berlin.",
1012
+ hintText: "Type a city, region, or IANA time zone.",
1013
+ resultNoun: "time zone",
1014
+ invalidMessage: "Enter a valid IANA time zone, such as Europe/Berlin.",
1015
+ invalid: value.trim() !== "" && selected === null,
1016
+ resumeSearch: value.trim() !== "" && selected === null,
1017
+ onOpen: () => offsetCacheRef.current.clear(),
1018
+ });
1019
+ }
1020
+
1021
+ function normalizedProviders(providers) {
1022
+ return (Array.isArray(providers) ? providers : [])
1023
+ .filter((provider) => provider && typeof provider.id === "string" && provider.id !== "")
1024
+ .map((provider) => ({
1025
+ id: provider.id,
1026
+ name: typeof provider.name === "string" && provider.name !== "" ? provider.name : provider.id,
1027
+ models: (Array.isArray(provider.models) ? provider.models : [])
1028
+ .map((model) => typeof model === "string"
1029
+ ? { id: model, name: model }
1030
+ : model && typeof model.id === "string"
1031
+ ? {
1032
+ id: model.id,
1033
+ name: typeof model.name === "string" && model.name !== "" ? model.name : model.id,
1034
+ ...(typeof model.description === "string" ? { description: model.description } : {}),
1035
+ }
1036
+ : null)
1037
+ .filter(Boolean),
1038
+ }));
1039
+ }
1040
+
1041
+ function ProviderPicker({ id, value, providers, defaultModel, onChange }) {
1042
+ const catalog = normalizedProviders(providers);
1043
+ const known = value === "" || catalog.some((provider) => provider.id === value);
1044
+ const options = [
1045
+ {
1046
+ id: "",
1047
+ value: "",
1048
+ label: "Harness default",
1049
+ detail: defaultModel && defaultModel.provider && defaultModel.model
1050
+ ? "Use " + defaultModel.provider + " / " + defaultModel.model + "."
1051
+ : "Resolve the current Harness provider at run time.",
1052
+ Icon: IconSettingsOutline16,
1053
+ },
1054
+ ...catalog.map((provider) => ({
1055
+ id: provider.id,
1056
+ value: provider.id,
1057
+ label: provider.name,
1058
+ detail: (provider.name === provider.id ? "Provider route" : provider.id)
1059
+ + " · " + provider.models.length + (provider.models.length === 1 ? " model" : " models"),
1060
+ aliases: [provider.id],
1061
+ Icon: IconSettingsOutline16,
1062
+ })),
1063
+ ...(value.trim() !== "" && !known
1064
+ ? [{
1065
+ id: "custom-provider:" + value,
1066
+ value,
1067
+ label: value,
1068
+ detail: "Custom provider route · Press Enter to keep this value.",
1069
+ custom: true,
1070
+ Icon: IconSettingsOutline16,
1071
+ }]
1072
+ : []),
1073
+ ];
1074
+ return h(EditableCombobox, {
1075
+ id,
1076
+ value,
1077
+ onChange,
1078
+ optionsForQuery: (query) => comboboxSearchResults(options, query),
1079
+ selectedId: value === "" ? "" : known ? value : "custom-provider:" + value,
1080
+ commitExactValue: (raw) => raw.trim(),
1081
+ placeholder: "Harness default",
1082
+ Icon: IconSettingsOutline16,
1083
+ listboxLabel: "Providers",
1084
+ initialTitle: "Available providers",
1085
+ searchTitle: "Matching providers",
1086
+ emptyText: "No matching providers. Press Enter to keep this custom provider route.",
1087
+ hintText: "Choose a configured provider or enter an adapter-supported route.",
1088
+ resultNoun: "provider",
1089
+ invalidMessage: "Enter a provider route.",
1090
+ resumeSearch: value.trim() !== "" && !known,
1091
+ });
1092
+ }
1093
+
1094
+ function useExactModelMetadata(provider, model) {
1095
+ const cacheKey = provider && model ? provider + "\u0000" + model : "";
1096
+ const [state, setState] = useState(() => {
1097
+ const cached = cacheKey ? MODEL_METADATA_CACHE.get(cacheKey) : null;
1098
+ return cached
1099
+ ? { key: cacheKey, status: "ready", data: cached, error: null }
1100
+ : { key: cacheKey, status: cacheKey ? "loading" : "idle", data: null, error: null };
1101
+ });
1102
+ useEffect(() => {
1103
+ if (!cacheKey) {
1104
+ setState({ key: "", status: "idle", data: null, error: null });
1105
+ return undefined;
1106
+ }
1107
+ const cached = MODEL_METADATA_CACHE.get(cacheKey);
1108
+ if (cached) {
1109
+ setState({ key: cacheKey, status: "ready", data: cached, error: null });
1110
+ return undefined;
1111
+ }
1112
+ const controller = new AbortController();
1113
+ let active = true;
1114
+ setState({ key: cacheKey, status: "loading", data: null, error: null });
1115
+ const timer = window.setTimeout(() => {
1116
+ apiFetch(
1117
+ "/meta/model?provider=" + encodeURIComponent(provider) + "&model=" + encodeURIComponent(model),
1118
+ { signal: controller.signal },
1119
+ ).then((data) => {
1120
+ MODEL_METADATA_CACHE.set(cacheKey, data);
1121
+ if (active) setState({ key: cacheKey, status: "ready", data, error: null });
1122
+ }).catch((error) => {
1123
+ if (!active || (error && error.name === "AbortError")) return;
1124
+ setState({ key: cacheKey, status: "error", data: null, error: errMessage(error) });
1125
+ });
1126
+ }, 180);
1127
+ return () => {
1128
+ active = false;
1129
+ window.clearTimeout(timer);
1130
+ controller.abort();
1131
+ };
1132
+ }, [cacheKey, provider, model]);
1133
+ if (state.key === cacheKey) return state;
1134
+ const cached = cacheKey ? MODEL_METADATA_CACHE.get(cacheKey) : null;
1135
+ return cached
1136
+ ? { key: cacheKey, status: "ready", data: cached, error: null }
1137
+ : { key: cacheKey, status: cacheKey ? "loading" : "idle", data: null, error: null };
1138
+ }
1139
+
1140
+ function ReasoningEffortPicker({ id, value, provider, model, defaultModel, onChange }) {
1141
+ const inherited = provider.trim() === "" && model.trim() === "";
1142
+ const exactProvider = inherited ? String(defaultModel?.provider || "") : provider.trim();
1143
+ const exactModel = inherited ? String(defaultModel?.model || "") : model.trim();
1144
+ const modelState = useExactModelMetadata(exactProvider, exactModel);
1145
+ const reasoning = modelState.status === "ready" && modelState.data
1146
+ ? modelState.data.reasoning
1147
+ : undefined;
1148
+ const efforts = reasoning && Array.isArray(reasoning.efforts) ? reasoning.efforts : [];
1149
+ const known = value === "" || efforts.some((effort) => effort.id === value);
1150
+ const advertisedDefault = inherited
1151
+ ? defaultModel?.reasoningEffort
1152
+ : reasoning?.defaultEffort;
1153
+ const defaultEffort = efforts.find((effort) => effort.id === advertisedDefault);
1154
+ const defaultDetail = advertisedDefault
1155
+ ? "Use " + (defaultEffort?.name || advertisedDefault) + (inherited ? " from the current Harness selection." : " as this model's default.")
1156
+ : inherited
1157
+ ? "Use the current Harness reasoning setting at run time."
1158
+ : "Let the selected model or provider choose.";
1159
+ const options = [
1160
+ {
1161
+ id: "",
1162
+ value: "",
1163
+ label: "Default",
1164
+ detail: defaultDetail,
1165
+ Icon: IconThinkOutline14,
1166
+ },
1167
+ ...efforts.map((effort) => ({
1168
+ id: effort.id,
1169
+ value: effort.id,
1170
+ label: effort.name || effort.id,
1171
+ detail: effort.description || (effort.name === effort.id ? "Adapter-owned effort level." : effort.id),
1172
+ aliases: [effort.id],
1173
+ Icon: IconThinkOutline14,
1174
+ })),
1175
+ ...(value.trim() !== "" && !known
1176
+ ? [{
1177
+ id: "custom-effort:" + value,
1178
+ value,
1179
+ label: value,
1180
+ detail: "Custom effort ID · Availability is checked when the automation runs.",
1181
+ custom: true,
1182
+ Icon: IconThinkOutline14,
1183
+ }]
1184
+ : []),
1185
+ ];
1186
+ let panelNotice = null;
1187
+ if (!exactProvider || !exactModel) {
1188
+ panelNotice = "Choose a model to load its effort levels.";
1189
+ } else if (modelState.status === "loading") {
1190
+ panelNotice = "Loading effort levels for " + exactProvider + " / " + exactModel + "…";
1191
+ } else if (modelState.status === "error") {
1192
+ panelNotice = "Effort metadata is unavailable. You can still enter an adapter-supported ID.";
1193
+ } else if (modelState.status === "ready" && !reasoning) {
1194
+ panelNotice = "This model does not advertise selectable effort levels. Custom IDs remain available.";
1195
+ }
1196
+ return h(EditableCombobox, {
1197
+ id,
1198
+ value,
1199
+ onChange,
1200
+ optionsForQuery: (query) => comboboxSearchResults(options, query),
1201
+ selectedId: value === "" ? "" : known ? value : "custom-effort:" + value,
1202
+ commitExactValue: (raw) => raw.trim(),
1203
+ placeholder: "Default",
1204
+ Icon: IconThinkOutline14,
1205
+ listboxLabel: "Reasoning effort levels",
1206
+ initialTitle: "Reasoning effort",
1207
+ searchTitle: "Matching effort levels",
1208
+ emptyText: "No matching effort levels. Press Enter to keep this custom effort ID.",
1209
+ hintText: "Options come from the exact model; custom adapter-owned IDs remain accepted.",
1210
+ resultNoun: "effort level",
1211
+ invalidMessage: "Enter an effort ID or choose Default.",
1212
+ resumeSearch: value.trim() !== "" && !known,
1213
+ panelNotice,
1214
+ disabled: provider.trim() !== "" && model.trim() === "" && value.trim() === "",
1215
+ });
1216
+ }
1217
+
449
1218
  function AgentPresetPicker({ id, value, presets, onChange }) {
450
1219
  const [open, setOpen] = useState(false);
451
1220
  const ownerId = useId();
@@ -608,7 +1377,7 @@ window.__ModuleLoader__.load({
608
1377
 
609
1378
  function JobForm(props) {
610
1379
  const { meta, draft, editing, saving, error, onChange, onIdTouched, onSubmit, onCancel } = props;
611
- const providers = meta && Array.isArray(meta.providers) ? meta.providers : [];
1380
+ const providers = normalizedProviders(meta && Array.isArray(meta.providers) ? meta.providers : []);
612
1381
  const providerModels = providers.find((provider) => provider.id === draft.provider);
613
1382
  const models = providerModels ? providerModels.models : [];
614
1383
  const permissionPresets =
@@ -692,21 +1461,17 @@ window.__ModuleLoader__.load({
692
1461
  ),
693
1462
  h(
694
1463
  Field,
695
- { label: "Timezone", htmlFor: fieldId("timezone"), required: true },
696
- h("input", {
1464
+ {
1465
+ label: "Time zone",
1466
+ htmlFor: fieldId("timezone"),
1467
+ required: true,
1468
+ hint: "Enter an IANA time zone. Current UTC offsets may change with daylight saving time.",
1469
+ },
1470
+ h(TimeZonePicker, {
697
1471
  id: fieldId("timezone"),
698
- className: "dsh-auto-input",
699
- type: "text",
700
- list: fieldId("timezone-list"),
701
1472
  value: draft.timezone,
702
- spellCheck: false,
703
- onChange: (event) => onChange("timezone", event.target.value),
1473
+ onChange: (value) => onChange("timezone", value),
704
1474
  }),
705
- h(
706
- "datalist",
707
- { id: fieldId("timezone-list") },
708
- TIMEZONES.map((zone) => h("option", { key: zone, value: zone })),
709
- ),
710
1475
  ),
711
1476
  h(
712
1477
  Field,
@@ -740,21 +1505,13 @@ window.__ModuleLoader__.load({
740
1505
  h(
741
1506
  Field,
742
1507
  { label: "Provider", htmlFor: fieldId("provider"), hint: "Blank uses the current Harness default. You may type an unlisted provider route." },
743
- h("input", {
1508
+ h(ProviderPicker, {
744
1509
  id: fieldId("provider"),
745
- className: "dsh-auto-input",
746
- type: "text",
747
- list: fieldId("provider-list"),
748
1510
  value: draft.provider,
749
- spellCheck: false,
750
- placeholder: "Harness default",
751
- onChange: (event) => onChange("provider", event.target.value),
1511
+ providers,
1512
+ defaultModel: meta?.defaultModel,
1513
+ onChange: (value) => onChange("provider", value),
752
1514
  }),
753
- h(
754
- "datalist",
755
- { id: fieldId("provider-list") },
756
- providers.map((provider) => h("option", { key: provider.id, value: provider.id })),
757
- ),
758
1515
  ),
759
1516
  h(
760
1517
  Field,
@@ -780,27 +1537,24 @@ window.__ModuleLoader__.load({
780
1537
  h(
781
1538
  "datalist",
782
1539
  { id: fieldId("model-list") },
783
- models.map((model) => h("option", { key: model, value: model })),
1540
+ models.map((model) => h("option", {
1541
+ key: model.id,
1542
+ value: model.id,
1543
+ label: model.name === model.id ? undefined : model.name,
1544
+ })),
784
1545
  ),
785
1546
  ),
786
1547
  h(
787
1548
  Field,
788
- { label: "Reasoning effort", htmlFor: fieldId("effort"), hint: "Blank uses the provider default." },
789
- h("input", {
1549
+ { label: "Reasoning effort", htmlFor: fieldId("effort"), hint: "Default follows the selected model. Custom adapter-owned IDs remain supported." },
1550
+ h(ReasoningEffortPicker, {
790
1551
  id: fieldId("effort"),
791
- className: "dsh-auto-input",
792
- type: "text",
793
- list: fieldId("effort-list"),
794
1552
  value: draft.reasoningEffort,
795
- spellCheck: false,
796
- placeholder: "e.g. medium",
797
- onChange: (event) => onChange("reasoningEffort", event.target.value),
1553
+ provider: draft.provider,
1554
+ model: draft.model,
1555
+ defaultModel: meta?.defaultModel,
1556
+ onChange: (value) => onChange("reasoningEffort", value),
798
1557
  }),
799
- h(
800
- "datalist",
801
- { id: fieldId("effort-list") },
802
- REASONING_EFFORTS.map((effort) => h("option", { key: effort, value: effort })),
803
- ),
804
1558
  ),
805
1559
  h(
806
1560
  Field,
@@ -1348,7 +2102,9 @@ window.__ModuleLoader__.load({
1348
2102
  next = Object.assign({}, next, { id: slugifyJobId(value) });
1349
2103
  }
1350
2104
  if (key === "provider") {
1351
- next = Object.assign({}, next, { model: "" });
2105
+ next = Object.assign({}, next, { model: "", reasoningEffort: "" });
2106
+ } else if (key === "model") {
2107
+ next = Object.assign({}, next, { reasoningEffort: "" });
1352
2108
  }
1353
2109
  return next;
1354
2110
  });
@@ -1657,7 +2413,7 @@ window.__ModuleLoader__.load({
1657
2413
  const onKeyDown = (event) => {
1658
2414
  if (event.key !== "Escape" || event.defaultPrevented || event.__dshAutomationsNested) return;
1659
2415
  if (event.target?.closest?.('[role="dialog"],[role="menu"]')) return;
1660
- if (document.querySelector('.dsh-auto-picker-trigger[aria-expanded="true"]')) return;
2416
+ if (document.querySelector('.dsh-auto-picker-trigger[aria-expanded="true"],.dsh-auto-combobox-input[aria-expanded="true"]')) return;
1661
2417
  event.preventDefault();
1662
2418
  disclosure.close();
1663
2419
  };
@@ -1745,7 +2501,34 @@ window.__ModuleLoader__.load({
1745
2501
  ".dsh-auto-picker-chevron-open{transform:rotate(180deg);}",
1746
2502
  ".dsh-auto-picker-item-copy{min-width:0;display:flex;flex-direction:column;white-space:normal;}",
1747
2503
  ".dsh-auto-picker-item-label{color:inherit;font-size:14px;line-height:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
1748
- ".dsh-auto-picker-item-detail{color:var(--dsh-auto-muted);font-size:12px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
2504
+ ".dsh-auto-picker-item-detail{color:var(--dsh-auto-muted,var(--dsw-alias-label-tertiary,#81858c));font-size:12px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
2505
+ ".dsh-auto-combobox-control{width:100%;height:34px;padding:0 8px;display:flex;align-items:center;gap:7px;border:1px solid var(--dsh-auto-border);border-radius:8px;background:var(--dsh-auto-input-bg);color:var(--dsh-auto-text);cursor:text;}",
2506
+ ".dsh-auto-combobox-control:hover{background:var(--dsw-alias-interactive-bg-hover,var(--dsh-auto-input-bg));}",
2507
+ ".dsh-auto-combobox-control:focus-within,.dsh-auto-combobox-control[data-open=\"true\"]{border-color:var(--dsh-auto-accent);box-shadow:0 0 0 2px color-mix(in srgb,var(--dsh-auto-accent) 18%,transparent);}",
2508
+ ".dsh-auto-combobox-control:has(.dsh-auto-combobox-input[aria-invalid=\"true\"]){border-color:var(--dsh-auto-danger);}",
2509
+ ".dsh-auto-combobox-control:has(.dsh-auto-combobox-input:disabled){background:var(--dsh-auto-surface-active);color:var(--dsh-auto-muted);cursor:default;}",
2510
+ ".dsh-auto-combobox-control-icon,.dsh-auto-combobox-chevron{width:16px;height:16px;flex:none;display:inline-flex;align-items:center;justify-content:center;color:var(--dsh-auto-muted);pointer-events:none;}",
2511
+ ".dsh-auto-combobox-input{appearance:none;min-width:0;height:100%;flex:1;padding:0;border:0;outline:0;background:transparent;color:inherit;font:inherit;font-size:13px;line-height:20px;}",
2512
+ ".dsh-auto-combobox-input::placeholder{color:var(--dsh-auto-caption);}",
2513
+ ".dsh-auto-combobox-input:disabled{cursor:default;}",
2514
+ ".dsh-auto-combobox-chevron{transition:transform .12s ease;}",
2515
+ ".dsh-auto-combobox-chevron-open{transform:rotate(180deg);}",
2516
+ ".dsh-auto-combobox-popover{box-sizing:border-box;position:fixed;z-index:1100;max-height:min(360px,calc(100vh - 24px));padding:4px;display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-inverted,var(--dsw-alias-border-l2,rgba(15,17,21,.16)));border-radius:12px;background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2,#fff));box-shadow:var(--dsw-shadow-lv3,0 12px 32px rgba(15,17,21,.16));color:var(--dsw-alias-label-primary,#0f1115);font-family:inherit;}",
2517
+ ".dsh-auto-combobox-popover *,.dsh-auto-combobox-popover *::before,.dsh-auto-combobox-popover *::after{box-sizing:border-box;}",
2518
+ ".dsh-auto-combobox-popover-title{flex:none;padding:7px 10px 5px;color:var(--dsw-alias-label-tertiary,#81858c);font-size:11px;font-weight:600;line-height:16px;text-transform:uppercase;letter-spacing:.04em;}",
2519
+ ".dsh-auto-combobox-notice{flex:none;margin:2px 4px 4px;padding:7px 9px;border-radius:8px;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06)));color:var(--dsw-alias-label-secondary,#4f5661);font-size:12px;line-height:18px;}",
2520
+ ".dsh-auto-combobox-list{min-height:0;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;display:flex;flex-direction:column;--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);}",
2521
+ ".dsh-auto-combobox-option{width:100%;min-height:48px;padding:6px 9px;display:flex;align-items:center;gap:8px;border-radius:9px;cursor:pointer;user-select:none;}",
2522
+ ".dsh-auto-combobox-option:hover,.dsh-auto-combobox-option[data-active=\"true\"]{background:var(--dsw-alias-interactive-bg-hover,rgba(38,49,72,.06));}",
2523
+ ".dsh-auto-combobox-option[data-disabled=\"true\"]{cursor:default;color:var(--dsw-alias-label-dimmed,var(--dsh-auto-caption));}",
2524
+ ".dsh-auto-combobox-option[data-disabled=\"true\"]:hover{background:transparent;}",
2525
+ ".dsh-auto-combobox-option[aria-selected=\"true\"]{color:var(--dsw-alias-state-business-primary,#4176e6);}",
2526
+ ".dsh-auto-combobox-option-icon,.dsh-auto-combobox-option-check{width:16px;height:16px;flex:none;display:inline-flex;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary,#81858c);}",
2527
+ ".dsh-auto-combobox-option>.dsh-auto-picker-item-copy{min-width:0;flex:1;}",
2528
+ ".dsh-auto-combobox-option .dsh-auto-picker-item-detail{color:var(--dsw-alias-label-tertiary,#81858c);}",
2529
+ ".dsh-auto-combobox-option-check{color:var(--dsw-alias-state-business-primary,#4176e6);}",
2530
+ ".dsh-auto-combobox-empty{margin:0;padding:16px 12px;color:var(--dsw-alias-label-secondary,#4f5661);font-size:12px;line-height:18px;text-align:center;}",
2531
+ ".dsh-auto-combobox-popover-hint{flex:none;margin:4px 0 0;padding:7px 10px 5px;border-top:1px solid var(--dsw-alias-border-l2,rgba(15,17,21,.12));color:var(--dsw-alias-label-tertiary,#81858c);font-size:11px;line-height:16px;}",
1749
2532
  ".dsh-auto-permission-glyph{flex:none;}",
1750
2533
  ".dsh-auto-permission-glyph-read{color:var(--dsh-auto-muted);}",
1751
2534
  ".dsh-auto-permission-glyph-write{color:var(--dsh-auto-accent);}",
@@ -1888,7 +2671,21 @@ window.__ModuleLoader__.load({
1888
2671
  exports.agentPresetDisplayLabel = agentPresetDisplayLabel;
1889
2672
  exports.permissionPresetPresentation = permissionPresetPresentation;
1890
2673
  exports.PermissionPresetPicker = PermissionPresetPicker;
1891
- exports.__testing = Object.freeze({ nextFormInstancePrefix });
2674
+ exports.EditableCombobox = EditableCombobox;
2675
+ exports.ProviderPicker = ProviderPicker;
2676
+ exports.ReasoningEffortPicker = ReasoningEffortPicker;
2677
+ exports.TimeZonePicker = TimeZonePicker;
2678
+ exports.__testing = Object.freeze({
2679
+ nextFormInstancePrefix,
2680
+ canonicalTimezone,
2681
+ comboboxSearchResults,
2682
+ normalizedProviders,
2683
+ timezoneOffsetLabel,
2684
+ timezoneNavigationIndex,
2685
+ timezoneOptionPresentation,
2686
+ timezonePreferredValue,
2687
+ timezoneSearchResults,
2688
+ });
1892
2689
  exports.inject = inject;
1893
2690
  exports.apply = apply;
1894
2691
  return module.exports;