dsh-theone 0.3.9 → 0.3.13

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 (44) hide show
  1. package/README.md +77 -53
  2. package/README.zh.md +82 -58
  3. package/cordis.patch.yml +0 -0
  4. package/dist/catalog-types.d.ts +0 -0
  5. package/dist/catalog-types.js +0 -0
  6. package/dist/client-locales.d.ts +20 -0
  7. package/dist/client-locales.js +40 -0
  8. package/dist/client-navigation.d.ts +0 -0
  9. package/dist/client-navigation.js +0 -0
  10. package/dist/client.d.ts +0 -0
  11. package/dist/client.js +375 -11
  12. package/dist/gateway-compaction.d.ts +0 -0
  13. package/dist/gateway-compaction.js +0 -0
  14. package/dist/history-catalog.d.ts +0 -0
  15. package/dist/history-catalog.js +0 -0
  16. package/dist/index.d.ts +17 -3
  17. package/dist/index.js +107 -23
  18. package/dist/linkage.d.ts +0 -0
  19. package/dist/linkage.js +0 -0
  20. package/dist/llm-router.d.ts +0 -0
  21. package/dist/llm-router.js +0 -0
  22. package/dist/model-json.d.ts +0 -0
  23. package/dist/model-json.js +0 -0
  24. package/dist/router.d.ts +0 -0
  25. package/dist/router.js +0 -0
  26. package/dist/routing-policy.d.ts +0 -0
  27. package/dist/routing-policy.js +0 -0
  28. package/dist/run.d.ts +0 -0
  29. package/dist/run.js +0 -0
  30. package/dist/settings-types.d.ts +0 -0
  31. package/dist/settings-types.js +0 -0
  32. package/dist/settings.d.ts +0 -0
  33. package/dist/settings.js +0 -0
  34. package/dist/store.d.ts +0 -0
  35. package/dist/store.js +0 -0
  36. package/dist/types.d.ts +0 -0
  37. package/dist/types.js +0 -0
  38. package/dist/update.d.ts +55 -0
  39. package/dist/update.js +121 -0
  40. package/docs/plugin-v0.1.md +0 -0
  41. package/docs/plugin-v0.1.zh.md +0 -0
  42. package/docs/plugin-v0.2.md +0 -0
  43. package/docs/plugin-v0.2.zh.md +0 -0
  44. package/package.json +1 -1
package/dist/client.js CHANGED
@@ -88,6 +88,26 @@ var zh = {
88
88
  "gateway.opening": "\u6B63\u5728\u6253\u5F00 TheOne \u4E3B\u804A\u5929\u2026",
89
89
  "gateway.error": "\u4E3B\u804A\u5929\u6682\u65F6\u65E0\u6CD5\u6253\u5F00\uFF0C\u8BF7\u68C0\u67E5 DSH \u8FDE\u63A5\u548C TheOne \u63D2\u4EF6\u72B6\u6001\u3002",
90
90
  "retry": "\u91CD\u8BD5",
91
+ "bg.label": "\u540E\u53F0",
92
+ "bg.follow": "\u9ED8\u8BA4",
93
+ "bg.title": "\u540E\u53F0\u6A21\u578B\uFF1A\u5206\u914D\u8BDD\u9898\u548C\u5E72\u6D3B\u90FD\u7528\u5B83",
94
+ "bg.followItem": "\u8DDF\u968F DSH \u9ED8\u8BA4\u6A21\u578B",
95
+ "bg.hint": "\u540E\u53F0\u6A21\u578B\uFF1A{model}\u3002TheOne \u5206\u914D\u8BDD\u9898\u548C\u540E\u53F0\u5E72\u6D3B\u90FD\u7528\u8FD9\u4E2A\u6A21\u578B\u3002",
96
+ "bg.hintFollow": "\u540E\u53F0\u6A21\u578B\uFF1A{model}\uFF08\u8DDF\u968F DSH \u9ED8\u8BA4\u6A21\u578B\uFF09\u3002\u70B9\u51FB\u53EF\u4EE5\u56FA\u5B9A\u4E3A\u5176\u4ED6\u6A21\u578B\u3002",
97
+ "bg.search": "\u641C\u7D22\u6A21\u578B",
98
+ "bg.none": "\u6CA1\u6709\u5339\u914D\u7684\u6A21\u578B",
99
+ "bg.error": "\u540E\u53F0\u6A21\u578B\u6CA1\u6709\u5207\u6362\u6210\u529F\uFF0C\u8BF7\u91CD\u8BD5\u3002",
100
+ "update.available": "\u66F4\u65B0",
101
+ "update.installing": "\u66F4\u65B0\u4E2D\u2026",
102
+ "update.restart": "\u91CD\u542F\u751F\u6548",
103
+ "update.reloading": "\u6B63\u5728\u91CD\u65B0\u52A0\u8F7D\u2026",
104
+ "update.reloadingHint": "TheOne \u6B63\u5728\u7528\u65B0\u7248\u672C\u91CD\u65B0\u52A0\u8F7D\uFF0CDSH \u4E0D\u9700\u8981\u91CD\u542F\uFF1B\u5B8C\u6210\u540E\u9875\u9762\u4F1A\u81EA\u52A8\u5237\u65B0\u3002",
105
+ "update.busy": "\u6709\u56DE\u590D\u6B63\u5728\u8FDB\u884C\uFF0C\u7B49\u5B83\u7ED3\u675F\u540E\u518D\u70B9\u66F4\u65B0\u3002",
106
+ "update.failed": "\u66F4\u65B0\u5931\u8D25",
107
+ "update.hint": "\u5F53\u524D {current}\uFF0C\u53EF\u66F4\u65B0\u5230 {latest}\u3002\u70B9\u51FB\u4E00\u952E\u66F4\u65B0\uFF0C\u4E0D\u7528\u91CD\u542F DSH\u3002",
108
+ "update.manualHint": "\u5F53\u524D {current}\uFF0C\u6700\u65B0 {latest}\u3002\u8FD9\u4EFD\u63D2\u4EF6\u4E0D\u662F\u4ECE GitHub \u6216 npm \u5B89\u88C5\u7684\uFF0C\u8BF7\u5728\u300C\u63D2\u4EF6\u300D\u9875\u9762\u91CD\u65B0\u5B89\u88C5\u3002",
109
+ "update.restartHint": "\u5DF2\u66F4\u65B0\uFF0C\u91CD\u542F DSH \u540E\u751F\u6548\u3002",
110
+ "update.failedHint": "\u66F4\u65B0\u6CA1\u6709\u5B8C\u6210\uFF08{error}\uFF09\u3002\u70B9\u51FB\u91CD\u8BD5\uFF0C\u6216\u5728\u300C\u63D2\u4EF6\u300D\u9875\u9762\u91CD\u65B0\u5B89\u88C5\u3002",
91
111
  "settings.title": "TheOne \u8BBE\u7F6E",
92
112
  "settings.menu": "\u8BBE\u7F6E",
93
113
  "settings.subtitle": "\u67E5\u770B\u5F53\u524D\u751F\u6548\u7684\u914D\u7F6E\u548C\u5404\u9879\u7528\u9014\u3002",
@@ -257,6 +277,26 @@ var en = {
257
277
  "gateway.opening": "Opening TheOne main chat\u2026",
258
278
  "gateway.error": "Main chat could not open. Check your DSH connection and TheOne plugin status.",
259
279
  "retry": "Retry",
280
+ "bg.label": "Background",
281
+ "bg.follow": "Default",
282
+ "bg.title": "Background model: routes and does the work",
283
+ "bg.followItem": "Follow DSH's default model",
284
+ "bg.hint": "Background model: {model}. TheOne routes and does the background work with it.",
285
+ "bg.hintFollow": "Background model: {model} (following DSH's default). Click to pin another model.",
286
+ "bg.search": "Search models",
287
+ "bg.none": "No matching models",
288
+ "bg.error": "The background model was not switched. Try again.",
289
+ "update.available": "Update",
290
+ "update.installing": "Updating\u2026",
291
+ "update.restart": "Restart to apply",
292
+ "update.reloading": "Reloading\u2026",
293
+ "update.reloadingHint": "TheOne is reloading with the new version; DSH keeps running, and the page refreshes when it is done.",
294
+ "update.busy": "A reply is in progress. Update once it finishes.",
295
+ "update.failed": "Update failed",
296
+ "update.hint": "You have {current}; {latest} is available. Click to update without restarting DSH.",
297
+ "update.manualHint": "You have {current}; {latest} is available. This copy was not installed from GitHub or npm, so reinstall it from the Plugins page.",
298
+ "update.restartHint": "Updated. Restart DSH to apply it.",
299
+ "update.failedHint": "The update did not finish ({error}). Click to retry, or reinstall from the Plugins page.",
260
300
  "settings.title": "TheOne settings",
261
301
  "settings.menu": "Settings",
262
302
  "settings.subtitle": "Review active configuration and what each option does.",
@@ -497,9 +537,94 @@ function apply(ctx) {
497
537
  });
498
538
  });
499
539
  });
540
+ let update;
541
+ const updateListeners = /* @__PURE__ */ new Set();
542
+ const setUpdate = (value) => {
543
+ update = value;
544
+ for (const listener of updateListeners) listener();
545
+ };
546
+ const subscribeUpdate = (listener) => {
547
+ updateListeners.add(listener);
548
+ return () => {
549
+ updateListeners.delete(listener);
550
+ };
551
+ };
552
+ const readUpdate = async (method = "GET") => {
553
+ try {
554
+ const response = await fetch("/api/theone/update", { method, signal: lifetime.signal, cache: "no-store" });
555
+ if (response.ok || response.status === 409) setUpdate(await response.json());
556
+ if (update?.state === "reloading") void awaitReload(update.current);
557
+ } catch {
558
+ }
559
+ };
560
+ async function awaitReload(previous) {
561
+ for (const started = Date.now(); Date.now() - started < 12e4; ) {
562
+ await new Promise((resolve) => setTimeout(resolve, 1500));
563
+ try {
564
+ const response = await fetch("/api/theone/update", { cache: "no-store" });
565
+ if (response.ok && (await response.json()).current !== previous) {
566
+ window.location.reload();
567
+ return;
568
+ }
569
+ } catch {
570
+ }
571
+ }
572
+ if (update) setUpdate({ ...update, state: "restart" });
573
+ }
574
+ ctx.effect(() => {
575
+ void readUpdate();
576
+ const timer = setInterval(() => {
577
+ void readUpdate();
578
+ }, 6 * 36e5);
579
+ return () => clearInterval(timer);
580
+ });
581
+ function UpdateButton() {
582
+ const t2 = useText();
583
+ const status = (0, import_react.useSyncExternalStore)(subscribeUpdate, () => update);
584
+ if (!status || !status.available && !status.state) return null;
585
+ const label = status.state === "installing" ? t2("update.installing") : status.state === "reloading" ? t2("update.reloading") : status.state === "restart" ? t2("update.restart") : status.state === "failed" ? t2("update.failed") : t2("update.available");
586
+ const title = status.error === "GATEWAY_BUSY" ? t2("update.busy") : status.state === "reloading" ? t2("update.reloadingHint") : status.state === "restart" ? t2("update.restartHint") : status.state === "failed" ? t2("update.failedHint", { error: status.error ?? "" }) : status.installable ? t2("update.hint", { current: status.current, latest: status.latest ?? "" }) : t2("update.manualHint", { current: status.current, latest: status.latest ?? "" });
587
+ const act = (event) => {
588
+ event.preventDefault();
589
+ event.stopPropagation();
590
+ if (status.state === "installing" || status.state === "reloading" || status.state === "restart") return;
591
+ if (!status.installable) {
592
+ window.open("https://github.com/YunongDai2005/dsh-theone#readme", "_blank", "noopener");
593
+ return;
594
+ }
595
+ setUpdate({ ...status, state: "installing" });
596
+ void readUpdate("POST");
597
+ };
598
+ return (0, import_react.createElement)(
599
+ "span",
600
+ {
601
+ className: "theone-update",
602
+ role: "button",
603
+ tabIndex: 0,
604
+ title,
605
+ "aria-label": title,
606
+ "data-state": status.state ?? "available",
607
+ onClick: act,
608
+ onPointerDown: (event) => event.stopPropagation(),
609
+ onKeyDown: (event) => {
610
+ if (event.key === "Enter" || event.key === " ") act(event);
611
+ }
612
+ },
613
+ status.state === "installing" || status.state === "reloading" ? (0, import_react.createElement)("span", { className: "theone-update-spin", "aria-hidden": true }) : null,
614
+ label,
615
+ !status.state && status.latest ? (0, import_react.createElement)("small", null, `v${status.latest}`) : null
616
+ );
617
+ }
618
+ const symbol = () => (0, import_react.createElement)(
619
+ "svg",
620
+ { className: "theone-symbol", viewBox: "0 0 20 20", width: 20, height: 20, "aria-hidden": true, focusable: false },
621
+ (0, import_react.createElement)("circle", { cx: 10, cy: 10, r: 8.6, fill: "none", stroke: "currentColor", strokeWidth: 1.4 }),
622
+ (0, import_react.createElement)("circle", { cx: 10, cy: 10, r: 2.4, fill: "currentColor" })
623
+ );
500
624
  function SidebarEntry({ size }) {
501
625
  const t2 = useText();
502
626
  const marker = (0, import_react.useRef)(null);
627
+ const pending = (0, import_react.useSyncExternalStore)(subscribeUpdate, () => !!update?.available && !update.state);
503
628
  const id = (0, import_react.useSyncExternalStore)(navigation.subscribe, navigation.getSnapshot);
504
629
  const sessions = (0, import_react.useSyncExternalStore)(ctx.sessions.list.subscribe, ctx.sessions.list.getSnapshot);
505
630
  const panel = (0, import_react.useSyncExternalStore)(ctx.layout.panelInfo.subscribe, ctx.layout.panelInfo.getSnapshot);
@@ -606,7 +731,7 @@ function apply(ctx) {
606
731
  return (0, import_react.createElement)(
607
732
  "span",
608
733
  { ref: marker, className: "theone-nav", translate: "no", "data-wide": size === 16, "data-active": active },
609
- (0, import_react.createElement)("span", { className: "theone-symbol" }),
734
+ (0, import_react.createElement)("span", { className: "theone-symbol-wrap" }, symbol(), size !== 16 && pending ? (0, import_react.createElement)("span", { className: "theone-update-dot", "aria-hidden": true }) : null),
610
735
  size === 16 && (0, import_react.createElement)(
611
736
  "span",
612
737
  { className: "theone-entry-copy" },
@@ -622,9 +747,214 @@ function apply(ctx) {
622
747
  (0, import_react.createElement)("span", { className: "theone-entry-label" }, t2("gateway.label"))
623
748
  ),
624
749
  (0, import_react.createElement)("span", { className: "theone-entry-sub" }, t2("gateway.subtitle"))
625
- )
750
+ ),
751
+ size === 16 && (0, import_react.createElement)(UpdateButton)
626
752
  );
627
753
  }
754
+ const readSettings = async () => {
755
+ const response = await fetch("/api/theone/settings", { signal: lifetime.signal, cache: "no-store" });
756
+ if (!response.ok) throw new Error("Settings unavailable");
757
+ return await response.json();
758
+ };
759
+ const saveBackgroundModel = async (choice) => {
760
+ for (let attempt = 0; attempt < 2; attempt++) {
761
+ const current = await readSettings();
762
+ const response = await fetch("/api/theone/settings", {
763
+ method: "PUT",
764
+ signal: lifetime.signal,
765
+ cache: "no-store",
766
+ headers: { "content-type": "application/json" },
767
+ body: JSON.stringify({ revision: current.revision, values: { ...current.savedValues, workerProvider: choice?.provider ?? null, workerModel: choice?.model ?? null } })
768
+ });
769
+ if (response.ok) return await response.json();
770
+ if (response.status !== 409) break;
771
+ }
772
+ throw new Error("Background model not saved");
773
+ };
774
+ function BackgroundModel({ directory }) {
775
+ const t2 = useText();
776
+ const state = (0, import_react.useSyncExternalStore)((listener) => directory.subscribe(listener), () => directory.getSnapshot());
777
+ const selection = state.pending ?? state.current;
778
+ const theone = selection?.provider === "theone";
779
+ const [settings, setSettings] = (0, import_react.useState)();
780
+ const [failed, setFailed] = (0, import_react.useState)(false);
781
+ const [saving, setSaving] = (0, import_react.useState)(false);
782
+ const button = (0, import_react.useRef)(null);
783
+ const closeMenu = (0, import_react.useRef)();
784
+ (0, import_react.useEffect)(() => {
785
+ if (!theone) return;
786
+ let alive = true;
787
+ readSettings().then((value) => {
788
+ if (alive) setSettings(value);
789
+ }, () => {
790
+ });
791
+ return () => {
792
+ alive = false;
793
+ closeMenu.current?.();
794
+ };
795
+ }, [theone]);
796
+ if (!theone) return null;
797
+ const pinned = settings?.values.workerProvider && settings.values.workerModel ? { provider: settings.values.workerProvider, model: settings.values.workerModel } : null;
798
+ const nameOf = (provider, model) => settings?.models.find((item) => item.provider === provider && item.id === model)?.name ?? model;
799
+ const effective = settings?.model ? nameOf(settings.model.provider, settings.model.model) : void 0;
800
+ const label = pinned ? nameOf(pinned.provider, pinned.model) : effective ?? t2("bg.follow");
801
+ const choose = async (choice) => {
802
+ closeMenu.current?.();
803
+ setSaving(true);
804
+ setFailed(false);
805
+ try {
806
+ setSettings(await saveBackgroundModel(choice));
807
+ } catch {
808
+ setFailed(true);
809
+ } finally {
810
+ setSaving(false);
811
+ }
812
+ };
813
+ const open = () => {
814
+ const anchor = button.current;
815
+ if (!anchor || closeMenu.current) {
816
+ closeMenu.current?.();
817
+ return;
818
+ }
819
+ const menu = document.createElement("div");
820
+ menu.className = "theone-bg-menu";
821
+ menu.setAttribute("role", "menu");
822
+ menu.setAttribute("translate", "no");
823
+ menu.setAttribute("aria-label", t2("bg.title"));
824
+ const heading = document.createElement("div");
825
+ heading.className = "theone-bg-heading";
826
+ heading.textContent = t2("bg.title");
827
+ const list = document.createElement("div");
828
+ list.className = "theone-bg-list";
829
+ const models = (settings?.models ?? []).filter((item2) => item2.provider !== "theone");
830
+ const item = (text, sub, checked, pick) => {
831
+ const row = document.createElement("button");
832
+ row.type = "button";
833
+ row.setAttribute("role", "menuitemradio");
834
+ row.setAttribute("aria-checked", String(checked));
835
+ row.className = "theone-bg-item";
836
+ const name = document.createElement("span");
837
+ name.textContent = text;
838
+ row.append(name);
839
+ if (sub) {
840
+ const small = document.createElement("small");
841
+ small.textContent = sub;
842
+ row.append(small);
843
+ }
844
+ const mark = document.createElement("span");
845
+ mark.className = "theone-bg-check";
846
+ mark.textContent = checked ? "\u2713" : "";
847
+ row.append(mark);
848
+ row.addEventListener("click", pick);
849
+ return row;
850
+ };
851
+ const render = (query) => {
852
+ list.replaceChildren(item(t2("bg.followItem"), !pinned && effective ? effective : void 0, !pinned, () => {
853
+ void choose(null);
854
+ }));
855
+ const needle = query.trim().toLowerCase();
856
+ const matched = models.filter((model) => !needle || `${model.name} ${model.id} ${model.provider}`.toLowerCase().includes(needle));
857
+ for (const provider of [...new Set(matched.map((model) => model.provider))]) {
858
+ const group = document.createElement("div");
859
+ group.className = "theone-bg-group";
860
+ group.textContent = provider;
861
+ list.append(group);
862
+ for (const model of matched.filter((entry) => entry.provider === provider))
863
+ list.append(item(model.name, model.name !== model.id ? model.id : void 0, pinned?.provider === model.provider && pinned.model === model.id, () => {
864
+ void choose({ provider: model.provider, model: model.id });
865
+ }));
866
+ }
867
+ if (!matched.length && needle) {
868
+ const empty = document.createElement("p");
869
+ empty.className = "theone-bg-empty";
870
+ empty.textContent = t2("bg.none");
871
+ list.append(empty);
872
+ }
873
+ };
874
+ menu.append(heading);
875
+ let search;
876
+ if (models.length > 8) {
877
+ search = document.createElement("input");
878
+ search.type = "search";
879
+ search.className = "theone-bg-search";
880
+ search.placeholder = t2("bg.search");
881
+ search.addEventListener("input", () => render(search.value));
882
+ menu.append(search);
883
+ }
884
+ menu.append(list);
885
+ render("");
886
+ document.body.append(menu);
887
+ anchor.setAttribute("aria-expanded", "true");
888
+ const place = () => {
889
+ const bounds = anchor.getBoundingClientRect(), size = menu.getBoundingClientRect();
890
+ menu.style.left = `${Math.max(8, Math.min(bounds.right - size.width, window.innerWidth - size.width - 8))}px`;
891
+ menu.style.top = `${Math.max(8, bounds.top - size.height - 8)}px`;
892
+ };
893
+ place();
894
+ const rows = () => [...menu.querySelectorAll(".theone-bg-item")];
895
+ const outside = (event) => {
896
+ if (!menu.contains(event.target) && !anchor.contains(event.target)) close();
897
+ };
898
+ const keydown = (event) => {
899
+ if (event.key === "Escape") {
900
+ event.preventDefault();
901
+ close();
902
+ anchor.focus();
903
+ } else if (event.key === "ArrowDown" || event.key === "ArrowUp") {
904
+ event.preventDefault();
905
+ const all = rows(), index = all.indexOf(document.activeElement);
906
+ all[(index + (event.key === "ArrowDown" ? 1 : all.length - 1) + (index < 0 && event.key === "ArrowUp" ? 1 : 0)) % all.length]?.focus();
907
+ }
908
+ };
909
+ const close = () => {
910
+ menu.remove();
911
+ closeMenu.current = void 0;
912
+ anchor.setAttribute("aria-expanded", "false");
913
+ document.removeEventListener("pointerdown", outside, true);
914
+ document.removeEventListener("keydown", keydown, true);
915
+ window.removeEventListener("resize", close);
916
+ window.removeEventListener("blur", close);
917
+ };
918
+ closeMenu.current = close;
919
+ document.addEventListener("pointerdown", outside, true);
920
+ document.addEventListener("keydown", keydown, true);
921
+ window.addEventListener("resize", close);
922
+ window.addEventListener("blur", close);
923
+ (search ?? rows().find((row) => row.getAttribute("aria-checked") === "true") ?? rows()[0])?.focus();
924
+ };
925
+ const title = failed ? t2("bg.error") : t2(pinned ? "bg.hint" : "bg.hintFollow", { model: label });
926
+ return (0, import_react.createElement)(
927
+ "button",
928
+ {
929
+ ref: button,
930
+ type: "button",
931
+ className: "theone-bg",
932
+ title,
933
+ "aria-label": title,
934
+ "aria-haspopup": "menu",
935
+ "data-failed": failed,
936
+ disabled: saving,
937
+ onClick: () => {
938
+ if (settings) open();
939
+ else readSettings().then((value) => {
940
+ setSettings(value);
941
+ }, () => setFailed(true));
942
+ }
943
+ },
944
+ (0, import_react.createElement)("span", { className: "theone-bg-caption" }, t2("bg.label")),
945
+ (0, import_react.createElement)("span", { className: "theone-bg-text" }, label),
946
+ (0, import_react.createElement)("span", { className: "theone-bg-chevron", "aria-hidden": true }, saving ? "\u2026" : "\u2303")
947
+ );
948
+ }
949
+ ctx.inject(["slots", "modelDirectories"], (scope) => {
950
+ const slots = scope.slots;
951
+ slots.inject("conversation.input.right", () => slots.register({
952
+ name: "conversation.input.right",
953
+ id: "theone.background-model",
954
+ order: 100,
955
+ inject: (sessionId) => ({ directory: scope.modelDirectories.directoryFor(sessionId).store })
956
+ }, BackgroundModel));
957
+ });
628
958
  function GatewayPanel() {
629
959
  const t2 = useText();
630
960
  const [error, setError] = (0, import_react.useState)();
@@ -1307,7 +1637,7 @@ function apply(ctx) {
1307
1637
  ctx.effect(() => {
1308
1638
  const style = document.createElement("style");
1309
1639
  style.dataset.plugin = "dsh-theone";
1310
- style.textContent = sidebarCss + catalogCss + settingsCss;
1640
+ style.textContent = sidebarCss + catalogCss + settingsCss + composerCss;
1311
1641
  document.head.append(style);
1312
1642
  return () => {
1313
1643
  lifetime.abort();
@@ -1317,17 +1647,17 @@ function apply(ctx) {
1317
1647
  ctx.effect(() => {
1318
1648
  const style = document.createElement("style");
1319
1649
  style.dataset.plugin = "dsh-theone-gateway-row";
1320
- const update = () => {
1650
+ const update2 = () => {
1321
1651
  const id = navigation.getSnapshot();
1322
1652
  style.textContent = id ? `[role="treeitem"][data-row-key="${CSS.escape(`session:${id}`)}"]{display:none!important}` : "";
1323
1653
  };
1324
- update();
1325
- const unsubscribe = navigation.subscribe(update);
1326
- window.addEventListener("storage", update);
1654
+ update2();
1655
+ const unsubscribe = navigation.subscribe(update2);
1656
+ window.addEventListener("storage", update2);
1327
1657
  document.head.append(style);
1328
1658
  return () => {
1329
1659
  unsubscribe();
1330
- window.removeEventListener("storage", update);
1660
+ window.removeEventListener("storage", update2);
1331
1661
  style.remove();
1332
1662
  };
1333
1663
  });
@@ -1355,9 +1685,21 @@ button:has(.theone-nav[data-wide=true])>span:not(:has(.theone-nav)){display:none
1355
1685
  button:has(.theone-nav):hover{background:var(--one-tint);border-color:color-mix(in srgb,var(--one-accent) 40%,var(--one-line))}
1356
1686
  button:has(.theone-nav[data-active=true]){border-color:color-mix(in srgb,var(--one-accent) 40%,var(--one-line))}
1357
1687
  .theone-nav{display:flex;align-items:center;gap:10px;color:var(--one-accent);font-family:inherit;position:relative;z-index:1}
1358
- .theone-symbol{width:18px;height:18px;border:1px solid currentColor;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;flex:none}
1359
- .theone-symbol:after{content:'';width:4px;height:4px;border-radius:50%;background:currentColor}
1360
- .theone-nav[data-wide=true] .theone-symbol{margin:0 3px}
1688
+ .theone-symbol-wrap{position:relative;display:inline-flex;flex:none;width:20px;height:20px}
1689
+ .theone-nav .theone-symbol{display:block;width:20px;height:20px;flex:none;overflow:visible;border:0;border-radius:0;background:none}
1690
+ .theone-nav[data-wide=true] .theone-symbol-wrap{margin:0 2px}
1691
+ .theone-update-dot{position:absolute;top:-2px;right:-2px;width:7px;height:7px;border-radius:50%;background:#e8590c;box-shadow:0 0 0 2px var(--one-tint)}
1692
+ button:has(.theone-nav[data-wide=true])>span:has(.theone-nav){flex:1;min-width:0}
1693
+ .theone-nav[data-wide=true]{width:100%}
1694
+ .theone-update{margin-left:auto;align-self:center;display:inline-flex;align-items:center;gap:5px;padding:5px 10px;border-radius:999px;border:1px solid color-mix(in srgb,var(--one-accent) 45%,transparent);background:color-mix(in srgb,var(--one-accent) 12%,transparent);color:var(--one-accent);font-size:12px;line-height:16px;white-space:nowrap;cursor:pointer;transition:background 150ms ease}
1695
+ .theone-update:hover{background:color-mix(in srgb,var(--one-accent) 22%,transparent)}
1696
+ .theone-update:focus-visible{outline:2px solid var(--one-accent);outline-offset:2px}
1697
+ .theone-update small{font-size:11px;opacity:.75}
1698
+ .theone-update[data-state=installing],.theone-update[data-state=reloading],.theone-update[data-state=restart]{cursor:default}
1699
+ .theone-update[data-state=failed]{color:#d9480f;border-color:#d9480f66;background:#d9480f14}
1700
+ .theone-update-spin{width:10px;height:10px;border-radius:50%;border:1.5px solid currentColor;border-right-color:transparent;animation:theone-spin 800ms linear infinite}
1701
+ @keyframes theone-spin{to{transform:rotate(360deg)}}
1702
+ @media(prefers-reduced-motion:reduce){.theone-update-spin{animation:none}}
1361
1703
  .theone-entry-copy{display:flex;flex-direction:column;align-items:flex-start;gap:2px}
1362
1704
  .theone-entry-title{display:flex;align-items:center;gap:9px;line-height:22px}
1363
1705
  .theone-wordmark{display:inline-flex;align-items:baseline;gap:1px;white-space:nowrap}
@@ -1374,6 +1716,28 @@ button:has(.theone-catalog-entry)>span:not(:has(.theone-catalog-entry)){display:
1374
1716
  .theone-catalog{padding:32px;max-width:1180px;margin:auto;box-sizing:border-box;height:100%;overflow:auto;color:var(--dsw-alias-label-primary)}
1375
1717
  .theone-catalog-header{display:flex;justify-content:space-between;align-items:center;gap:16px}.theone-catalog h1{font-size:24px;margin:0 0 8px}.theone-catalog-header p,.theone-catalog-status,.theone-group-summary{opacity:.65;margin:0 0 18px;line-height:1.6}.theone-catalog button{border:1px solid #8883;border-radius:9px;padding:8px 13px;background:transparent;color:inherit;cursor:pointer;font:inherit;white-space:nowrap}.theone-catalog button:disabled{opacity:.5;cursor:default}.theone-catalog-groups{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,310px),1fr));gap:20px}.theone-topic-group{border:1px solid #8882;border-radius:16px;padding:20px;background:#88805}.theone-topic-group h2{font-size:18px;margin:0 0 8px}.theone-topic-group h2 span{font-size:13px;opacity:.5}.theone-topic-card{border-top:1px solid #8882;padding:16px 0}.theone-topic-card:last-child{padding-bottom:0}.theone-topic-card h3{font-size:15px;line-height:1.5;margin:0 0 7px}.theone-topic-card p{font-size:13px;line-height:1.7;opacity:.75;margin:0 0 12px;display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden}.theone-topic-actions{display:flex;gap:8px;flex-wrap:wrap}.theone-topic-actions button{font-size:12px}.theone-topic-actions .theone-source-link{border-color:transparent;opacity:.6}.theone-catalog-warning{background:#ff900011;padding:12px;border-radius:10px;font-size:13px}.theone-catalog-entry{display:flex;align-items:center;gap:10px;font-size:14px}.theone-catalog-empty{padding:40px 0;opacity:.65;line-height:1.8}.theone-catalog-tools{display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end}.theone-topic-links{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:0 0 12px;font-size:12px}.theone-links-label,.theone-links-empty{opacity:.6}.theone-link-chip{display:inline-flex;align-items:center;gap:2px;border:1px solid #8883;border-radius:999px;padding:2px 4px 2px 9px}.theone-catalog .theone-link-chip button{border:0;padding:0 5px;opacity:.6;font-size:13px;line-height:1}.theone-topic-links select{font:inherit;font-size:12px;color:inherit;background:transparent;border:1px solid #8883;border-radius:8px;padding:2px 6px}.theone-link-private{display:inline-flex;align-items:center;gap:4px;opacity:.75;cursor:pointer}.theone-topic-card .theone-topic-state{font-size:12px;opacity:.8;-webkit-line-clamp:3}.theone-topic-state span{opacity:.6;margin-right:4px}.theone-manage{display:flex;flex-direction:column;gap:10px;margin-top:12px;padding:14px;border:1px solid #8883;border-radius:12px;font-size:12px}.theone-create{flex-direction:row;flex-wrap:wrap;align-items:center;margin:0 0 18px}.theone-create input{flex:1;min-width:180px}.theone-manage-field{display:flex;flex-direction:column;gap:5px}.theone-manage-field>span{opacity:.65}.theone-manage input,.theone-manage textarea,.theone-manage select,.theone-routes select{font:inherit;font-size:12px;color:inherit;background:transparent;border:1px solid #8883;border-radius:8px;padding:6px 8px;box-sizing:border-box;min-width:0}.theone-manage textarea{resize:vertical;width:100%}.theone-manage-row{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.theone-manage-row select,.theone-manage-row input{flex:1;min-width:140px}.theone-manage-hint{opacity:.6;font-size:12px;line-height:1.6;margin:0}.theone-catalog .theone-danger{color:#c4402f;border-color:#c4402f55}.theone-routes{border:1px solid #8882;border-radius:16px;padding:14px 20px;margin:0 0 20px}.theone-routes summary{cursor:pointer;font-weight:500}.theone-routes summary span{opacity:.5;font-size:13px}.theone-routes ol{list-style:none;margin:12px 0 0;padding:0;display:flex;flex-direction:column;gap:10px}.theone-routes li{border-top:1px solid #8882;padding-top:10px;font-size:12px;display:flex;flex-direction:column;gap:5px}.theone-route-head{display:flex;gap:10px;min-width:0}.theone-route-head time{opacity:.55;flex:none}.theone-route-head q{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.theone-route-body{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.theone-route-body small{opacity:.6}.theone-route-fixed{opacity:.75}@media(max-width:640px){.theone-catalog{padding:20px}.theone-catalog-header{align-items:flex-start}.theone-catalog-header h1{font-size:21px}}
1376
1718
  `;
1719
+ var composerCss = `
1720
+ .theone-bg{display:inline-flex;align-items:center;gap:5px;height:32px;padding:0 10px;border:0;border-radius:10px;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:14px;white-space:nowrap;cursor:pointer;max-width:220px}
1721
+ .theone-bg:hover,.theone-bg[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover)}
1722
+ .theone-bg:focus-visible{outline:2px solid var(--dsw-focus-ring-color,#4a7fb5);outline-offset:1px}
1723
+ .theone-bg:disabled{opacity:.6;cursor:default}
1724
+ .theone-bg-caption,.theone-bg-chevron{color:var(--dsw-alias-label-secondary)}
1725
+ .theone-bg-text{overflow:hidden;text-overflow:ellipsis;display:var(--dsh-composer-model-text-display,inline)}
1726
+ .theone-bg-chevron{font-size:11px}
1727
+ .theone-bg[data-failed=true] .theone-bg-caption{color:#d9480f}
1728
+ .theone-bg-menu{position:fixed;z-index:10000;width:280px;max-height:min(420px,70vh);display:flex;flex-direction:column;padding:6px;border:1px solid var(--dsw-alias-border-l2);border-radius:14px;background:var(--dsw-specific-sidebar-fill);color:var(--dsw-alias-label-primary);box-shadow:0 10px 32px #0003;font:inherit;font-size:14px;box-sizing:border-box}
1729
+ .theone-bg-heading{padding:8px 10px 6px;font-size:12px;color:var(--dsw-alias-label-secondary)}
1730
+ .theone-bg-search{margin:0 4px 6px;padding:7px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:transparent;color:inherit;font:inherit;font-size:13px;outline:none}
1731
+ .theone-bg-search:focus{border-color:var(--dsw-focus-ring-color,#4a7fb5)}
1732
+ .theone-bg-list{overflow:auto;min-height:0}
1733
+ .theone-bg-group{padding:8px 10px 4px;font-size:12px;color:var(--dsw-alias-label-secondary)}
1734
+ .theone-bg-item{display:flex;align-items:center;gap:8px;width:100%;padding:9px 10px;border:0;border-radius:8px;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer}
1735
+ .theone-bg-item:hover,.theone-bg-item:focus-visible{outline:none;background:var(--dsw-alias-interactive-bg-hover)}
1736
+ .theone-bg-item span:first-child{flex:0 1 auto;min-width:40px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
1737
+ .theone-bg-item small{flex:0 100 auto;color:var(--dsw-alias-label-secondary);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
1738
+ .theone-bg-check{margin-left:auto;flex:none;width:14px;text-align:center;color:var(--dsw-alias-label-primary)}
1739
+ .theone-bg-empty{margin:8px 10px;font-size:13px;color:var(--dsw-alias-label-secondary)}
1740
+ `;
1377
1741
  var settingsCss = `
1378
1742
  .theone-settings{--one-settings-accent:#a75b1e}[data-ds-dark-theme] .theone-settings{--one-settings-accent:#93c8f3}
1379
1743
  .theone-settings input,.theone-settings select{width:100%;box-sizing:border-box;min-height:38px;padding:8px 11px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;font:inherit;color:inherit;background:var(--dsw-specific-sidebar-fill)}.theone-settings input:focus-visible,.theone-settings select:focus-visible{outline:2px solid var(--one-settings-accent);outline-offset:2px}.theone-settings input:disabled,.theone-settings select:disabled,.theone-settings button:disabled{opacity:.5;cursor:default}.theone-settings small{display:block;margin-top:5px;color:var(--dsw-alias-label-secondary);font-size:12px}
File without changes
File without changes
File without changes
File without changes
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Context, Service } from '@deepseek-ai/cordis';
2
2
  import z from '@deepseek-ai/schemastery';
3
- import type { GenerateOptions, LlmResolvedModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm';
3
+ import type { GenerateOptions, LlmModelInfo, LlmResolvedModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm';
4
+ import type { ModelSelection } from '@deepseek-ai/dsh-agent';
4
5
  import type { SessionEventWindow } from '@deepseek-ai/dsh-session-query';
5
6
  import { HistoryCatalog } from './history-catalog.ts';
6
7
  import { ContextStore } from './store.ts';
@@ -8,6 +9,7 @@ import type { RouterReceipt } from './llm-router.ts';
8
9
  import type { LinkageSnapshot } from './catalog-types.ts';
9
10
  import { type SettingsSnapshot } from './settings-types.ts';
10
11
  import { type LinkScope } from './linkage.ts';
12
+ import { Updater } from './update.ts';
11
13
  export interface Config {
12
14
  databasePath?: string;
13
15
  contextsPath?: string;
@@ -54,6 +56,8 @@ declare module '@deepseek-ai/dsh-llm' {
54
56
  };
55
57
  }
56
58
  }
59
+ export declare function viaModel(selection: ModelSelection): string;
60
+ export declare function parseVia(id: string | undefined): ModelSelection | undefined;
57
61
  export default class TheOne extends Service {
58
62
  private config;
59
63
  static inject: string[];
@@ -72,6 +76,9 @@ export default class TheOne extends Service {
72
76
  private readonly closing;
73
77
  /** A model the user picked in main chat's own model selector; it answers through the Workers. */
74
78
  private pickedModel?;
79
+ private adapter?;
80
+ /** Update checks and one-click install through DSH's plugin manager. */
81
+ readonly updater: Updater;
75
82
  /** Sessions whose current step answers through TheOne; only these refuse to run tools themselves. */
76
83
  private readonly throughTheOne;
77
84
  constructor(ctx: Context, config: Config);
@@ -89,8 +96,15 @@ export default class TheOne extends Service {
89
96
  /** Capture before Web saves the gateway itself as DSH's new default. */
90
97
  captureDefaultModel(): void;
91
98
  private backingModel;
92
- /** The entry has exactly the configured backing model's capacity, including DSH overrides. */
93
- gatewayModelInfo(provider?: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
99
+ /** Every model DSH offers besides TheOne; a provider that cannot list its models in time offers none. */
100
+ private offeredModels;
101
+ /**
102
+ * Main chat's model menu has one TheOne entry; the background model is chosen with the button
103
+ * beside it. Selections of the older "TheOne · <model>" entries still resolve.
104
+ */
105
+ gatewayModels(provider: string): Promise<LlmModelInfo[]>;
106
+ /** The entry has exactly the backing model's capacity, including DSH overrides. */
107
+ gatewayModelInfo(provider?: string, signal?: AbortSignal, model?: string): Promise<LlmResolvedModelInfo>;
94
108
  /** This main chat's recent text turns, each attributed to the topic it was routed to. */
95
109
  private recentMainChat;
96
110
  /** Recover bounded routing context from DSH references after the Gateway is rebuilt. */