@klarkxy/dsh-current-title 0.1.7 → 0.2.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
@@ -1,168 +1,7 @@
1
- function __createDshNodeShims() {
2
- class EventEmitter {
3
- constructor() {
4
- this._e = Object.create(null)
5
- }
6
- on(type, fn) {
7
- (this._e[type] ||= []).push(fn)
8
- return this
9
- }
10
- addListener(type, fn) {
11
- return this.on(type, fn)
12
- }
13
- once(type, fn) {
14
- const wrap = (...args) => {
15
- this.off(type, wrap)
16
- fn(...args)
17
- }
18
- return this.on(type, wrap)
19
- }
20
- off(type, fn) {
21
- const list = this._e[type]
22
- if (list) this._e[type] = list.filter((item) => item !== fn)
23
- return this
24
- }
25
- removeListener(type, fn) {
26
- return this.off(type, fn)
27
- }
28
- emit(type, ...args) {
29
- const list = this._e[type]
30
- if (!list) return false
31
- for (const fn of list.slice()) fn(...args)
32
- return true
33
- }
34
- removeAllListeners(type) {
35
- if (type) delete this._e[type]
36
- else this._e = Object.create(null)
37
- return this
38
- }
39
- listeners(type) {
40
- return (this._e[type] || []).slice()
41
- }
42
- setMaxListeners() {
43
- return this
44
- }
45
- }
46
-
47
- class Buffer extends Uint8Array {
48
- static from(value, encodingOrOffset, length) {
49
- if (typeof value === 'string') return new Buffer(new TextEncoder().encode(value))
50
- if (value instanceof ArrayBuffer) return new Buffer(new Uint8Array(value, encodingOrOffset, length))
51
- if (ArrayBuffer.isView(value)) return new Buffer(new Uint8Array(value.buffer, value.byteOffset, value.byteLength))
52
- if (Array.isArray(value) || value && typeof value.length === 'number') return new Buffer(Uint8Array.from(value))
53
- return new Buffer(0)
54
- }
55
- static alloc(size, fill) {
56
- const buf = new Buffer(size)
57
- if (fill !== undefined && fill !== 0) {
58
- if (typeof fill === 'string') {
59
- const bytes = new TextEncoder().encode(fill)
60
- for (let i = 0; i < size; i += 1) buf[i] = bytes[i % bytes.length]
61
- } else buf.fill(fill)
62
- }
63
- return buf
64
- }
65
- static allocUnsafe(size) {
66
- return new Buffer(size)
67
- }
68
- static allocUnsafeSlow(size) {
69
- return new Buffer(size)
70
- }
71
- static concat(list, totalLength) {
72
- const length = totalLength ?? list.reduce((sum, item) => sum + item.length, 0)
73
- const out = new Buffer(length)
74
- let offset = 0
75
- for (const item of list) {
76
- out.set(item, offset)
77
- offset += item.length
78
- }
79
- return out
80
- }
81
- static isBuffer(value) {
82
- return value instanceof Buffer
83
- }
84
- static byteLength(value) {
85
- if (typeof value === 'string') return new TextEncoder().encode(value).length
86
- return value?.byteLength ?? value?.length ?? 0
87
- }
88
- static isEncoding(encoding) {
89
- return /^(utf8|utf-8|ascii|latin1|binary|hex|base64|base64url)$/i.test(String(encoding || ''))
90
- }
91
- toString(encoding) {
92
- if (encoding === 'hex') return [...this].map((byte) => byte.toString(16).padStart(2, '0')).join('')
93
- if (encoding === 'base64') {
94
- let binary = ''
95
- this.forEach((byte) => {
96
- binary += String.fromCharCode(byte)
97
- })
98
- return btoa(binary)
99
- }
100
- return new TextDecoder().decode(this)
101
- }
102
- slice(start, end) {
103
- return Buffer.from(super.subarray(start, end))
104
- }
105
- copy(target, targetStart = 0, start = 0, end = this.length) {
106
- const sliced = this.subarray(start, end)
107
- target.set(sliced, targetStart)
108
- return sliced.length
109
- }
110
- write(string, offset = 0) {
111
- const bytes = new TextEncoder().encode(string)
112
- const written = Math.min(bytes.length, this.length - offset)
113
- this.set(bytes.subarray(0, written), offset)
114
- return written
115
- }
116
- }
117
-
118
- function inherits(ctor, superCtor) {
119
- ctor.super_ = superCtor
120
- ctor.prototype = Object.create(superCtor.prototype, {
121
- constructor: { value: ctor, writable: true, configurable: true },
122
- })
123
- }
124
-
125
- if (!globalThis.Buffer) globalThis.Buffer = Buffer
126
-
127
- return {
128
- buffer: { Buffer, SlowBuffer: Buffer, INSPECT_MAX_BYTES: 50, kMaxLength: 0x7fffffff },
129
- stream: {
130
- Stream: EventEmitter,
131
- Readable: EventEmitter,
132
- Writable: EventEmitter,
133
- Duplex: EventEmitter,
134
- Transform: EventEmitter,
135
- PassThrough: EventEmitter,
136
- },
137
- events: { EventEmitter },
138
- util: {
139
- deprecate(fn) {
140
- return fn
141
- },
142
- inherits,
143
- inspect() {
144
- return ''
145
- },
146
- format(value) {
147
- return String(value)
148
- },
149
- types: { isBuffer: Buffer.isBuffer },
150
- },
151
- }
152
- }
153
-
154
1
  window.__ModuleLoader__.load({
155
2
  id: "@klarkxy/dsh-current-title",
156
3
  factory: (dshRequire) => {
157
- var __nodeShims;
158
- var process = globalThis.process || { env: {}, nextTick: function (fn) { var args = [].slice.call(arguments, 1); queueMicrotask(function () { fn.apply(null, args); }); } };
159
- var require = function (id) {
160
- if (id === 'buffer' || id === 'stream' || id === 'util' || id === 'events') {
161
- if (!__nodeShims) __nodeShims = __createDshNodeShims();
162
- return __nodeShims[id];
163
- }
164
- return dshRequire(id);
165
- };
4
+ var require = dshRequire;
166
5
  var module = { exports: {} };
167
6
  var exports = module.exports;
168
7
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
@@ -170,6 +9,30 @@ let react = require("react");
170
9
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
171
10
  let react_jsx_runtime = require("react/jsx-runtime");
172
11
  //#region ../dsh-plugin-kit/src/client-utils.ts
12
+ /** Fixed pause for failing background reads; manual work is never replayed. */
13
+ function createPluginReadGate(now = Date.now) {
14
+ let pausedUntil = 0;
15
+ let epoch = 0;
16
+ return {
17
+ canRead: () => now() >= pausedUntil,
18
+ reset: () => {
19
+ epoch++;
20
+ pausedUntil = 0;
21
+ },
22
+ async run(read) {
23
+ const current = epoch;
24
+ try {
25
+ const value = await read();
26
+ if (current === epoch) pausedUntil = 0;
27
+ return value;
28
+ } catch (error) {
29
+ const message = error instanceof Error ? error.message : "";
30
+ if (current === epoch && (/(?:^|\s)transport failure for .+: HTTP (?:404|405|503)\b/.test(message) || error instanceof TypeError && /^(?:Failed to fetch|fetch failed|NetworkError when attempting to fetch resource\.?)$/i.test(message))) pausedUntil = now() + 3e4;
31
+ throw error;
32
+ }
33
+ }
34
+ };
35
+ }
173
36
  /** The Editor retains its manuscript session separately; native DSH exposes the main-view binding. */
174
37
  function selectedSessionId(client) {
175
38
  const workspace = client.uiWorkspace?.current;
@@ -649,8 +512,6 @@ const titleCopy = {
649
512
  loading: "正在读取标题设置…",
650
513
  model: "标题模型",
651
514
  modelDefault: "跟随会话模型",
652
- effort: "思考强度",
653
- effortDefault: "默认",
654
515
  cadence: "更新节奏",
655
516
  cadenceAll: "跟随当前任务(每条消息)",
656
517
  cadenceFirst: "仅首条消息",
@@ -670,8 +531,6 @@ const titleCopy = {
670
531
  loading: "Loading title settings…",
671
532
  model: "Title model",
672
533
  modelDefault: "Use the session model",
673
- effort: "Reasoning effort",
674
- effortDefault: "Default",
675
534
  cadence: "Update cadence",
676
535
  cadenceAll: "Track the current task (every message)",
677
536
  cadenceFirst: "First message only",
@@ -828,28 +687,17 @@ function catalogChoices(value, selected) {
828
687
  const id = typeof item.id === "string" ? item.id : "";
829
688
  if (!id) continue;
830
689
  const name = typeof item.name === "string" && item.name ? item.name : id;
831
- const reasoning = item.reasoning && typeof item.reasoning === "object" ? item.reasoning : void 0;
832
- const efforts = Array.isArray(reasoning?.efforts) ? reasoning.efforts.flatMap((effort) => {
833
- if (!effort || typeof effort !== "object") return [];
834
- const entry = effort;
835
- return typeof entry.id === "string" && entry.id ? [{
836
- id: entry.id,
837
- name: typeof entry.name === "string" && entry.name ? entry.name : entry.id
838
- }] : [];
839
- }) : [];
840
690
  choices.push({
841
691
  provider,
842
692
  model: id,
843
- label: `${providerName} / ${name}`,
844
- efforts
693
+ label: `${providerName} / ${name}`
845
694
  });
846
695
  }
847
696
  }
848
697
  if (selected.provider && selected.model && !choices.some((item) => item.provider === selected.provider && item.model === selected.model)) choices.push({
849
698
  provider: selected.provider,
850
699
  model: selected.model,
851
- label: `${selected.provider} / ${selected.model}`,
852
- efforts: []
700
+ label: `${selected.provider} / ${selected.model}`
853
701
  });
854
702
  return choices;
855
703
  }
@@ -866,15 +714,30 @@ function TitleSettings(props) {
866
714
  const workRef = (0, react.useRef)(void 0);
867
715
  if (!workRef.current) workRef.current = createTitleClientWork();
868
716
  const work = workRef.current;
717
+ const readGateRef = (0, react.useRef)(void 0);
718
+ if (!readGateRef.current) readGateRef.current = createPluginReadGate();
719
+ const readGate = readGateRef.current;
869
720
  const busyRef = (0, react.useRef)(busy);
870
721
  busyRef.current = busy;
871
722
  const call = (endpoint, payload = {}) => props.client.connection.rpc.call(RPC_CHANNEL, endpoint, payload);
723
+ const readCall = (endpoint, payload) => readGate.run(() => call(endpoint, payload));
872
724
  function commitMarker(next) {
873
725
  applyMarkerFromStatus(props.marker, next, true);
874
726
  }
875
727
  (0, react.useEffect)(() => () => {
728
+ readGate.reset();
876
729
  work.dispose();
877
- }, [work]);
730
+ }, [work, readGate]);
731
+ (0, react.useEffect)(() => props.client.connection.generation?.subscribe(() => {
732
+ readGate.reset();
733
+ work.beginLoad();
734
+ busyRef.current = false;
735
+ setBusy(false);
736
+ }), [
737
+ props.client,
738
+ work,
739
+ readGate
740
+ ]);
878
741
  (0, react.useEffect)(() => {
879
742
  const catalog = props.client.remote?.session?.modelCatalog;
880
743
  if (!catalog) return void 0;
@@ -897,12 +760,13 @@ function TitleSettings(props) {
897
760
  ]);
898
761
  (0, react.useEffect)(() => {
899
762
  const generation = work.beginLoad();
763
+ readGate.reset();
900
764
  const isCurrent = () => work.isLoad(generation);
901
765
  setBusy(false);
902
766
  setError("");
903
767
  setNote("");
904
768
  runTitleStatusLoad({
905
- call,
769
+ call: readCall,
906
770
  sessionId: sessionId || void 0,
907
771
  isCurrent,
908
772
  failedMessage: text.failed,
@@ -921,20 +785,22 @@ function TitleSettings(props) {
921
785
  work
922
786
  ]);
923
787
  const refreshStatus = (0, react.useCallback)(() => {
924
- if (shouldSkipTitleRefresh({ busy: busyRef.current }) || work.disposed) return;
925
- const generation = work.captureLoad();
788
+ if (!readGate.canRead() || shouldSkipTitleRefresh({ busy: busyRef.current }) || work.disposed) return;
789
+ readGate.reset();
790
+ const token = work.beginRequest();
926
791
  runTitleStatusLoad({
927
- call,
792
+ call: readCall,
928
793
  sessionId: sessionId || void 0,
929
- isCurrent: () => work.isLoad(generation) && !shouldSkipTitleRefresh({ busy: busyRef.current }),
794
+ isCurrent: () => work.isRequest(token) && !shouldSkipTitleRefresh({ busy: busyRef.current }),
930
795
  failedMessage: text.failed,
931
796
  onStatus: (next) => {
932
797
  if (shouldSkipTitleRefresh({ busy: busyRef.current })) return;
933
798
  setStatus(next);
799
+ setError("");
934
800
  },
935
- onError: () => {},
801
+ onError: setError,
936
802
  onMarker: (next) => {
937
- if (shouldSkipTitleRefresh({ busy: busyRef.current })) return;
803
+ if (!next || shouldSkipTitleRefresh({ busy: busyRef.current })) return;
938
804
  commitMarker(next);
939
805
  }
940
806
  });
@@ -956,14 +822,9 @@ function TitleSettings(props) {
956
822
  const pinned = status?.session?.pinned === true;
957
823
  const weOwn = status?.support.weOwn === true;
958
824
  const canRegenerate = Boolean(sessionId) && weOwn && !busy;
959
- const selectedChoice = choices.find((item) => item.provider === savedModel.provider && item.model === savedModel.model);
960
- const efforts = selectedChoice ? [...selectedChoice.efforts] : [];
961
- if (selectedChoice && savedModel.reasoningEffort && !efforts.some((item) => item.id === savedModel.reasoningEffort)) efforts.push({
962
- id: savedModel.reasoningEffort,
963
- name: savedModel.reasoningEffort
964
- });
965
825
  function saveSettings(patch) {
966
826
  if (!status) return;
827
+ readGate.reset();
967
828
  const token = work.beginRequest();
968
829
  setError("");
969
830
  setNote("");
@@ -1011,11 +872,20 @@ function TitleSettings(props) {
1011
872
  className: "dsh-ui-hint",
1012
873
  children: [text.inactive, status.support.limitation ? ` ${status.support.limitation}` : ""]
1013
874
  }) : null,
1014
- error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
875
+ error ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1015
876
  role: "alert",
1016
877
  className: "dsh-ui-error dsh-ui-wrap",
1017
878
  children: error
1018
- }) : null,
879
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
880
+ variant: "outline",
881
+ size: "sm",
882
+ disabled: busy,
883
+ onClick: () => {
884
+ readGate.reset();
885
+ refreshStatus();
886
+ },
887
+ children: seat.locale === "zh" ? "重试" : "Retry"
888
+ })] }) : null,
1019
889
  note ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1020
890
  role: "status",
1021
891
  className: "dsh-ui-notice",
@@ -1029,6 +899,7 @@ function TitleSettings(props) {
1029
899
  disabled: !canRegenerate,
1030
900
  onClick: () => {
1031
901
  if (!sessionId) return;
902
+ readGate.reset();
1032
903
  const token = work.beginRequest();
1033
904
  setError("");
1034
905
  setNote("");
@@ -1070,6 +941,7 @@ function TitleSettings(props) {
1070
941
  setPrompt(void 0);
1071
942
  return;
1072
943
  }
944
+ readGate.reset();
1073
945
  const token = work.beginRequest();
1074
946
  setError("");
1075
947
  runTitleSettingsSave({
@@ -1127,35 +999,6 @@ function TitleSettings(props) {
1127
999
  }, `${choice.provider}\u001f${choice.model}`))]
1128
1000
  })]
1129
1001
  }),
1130
- selectedChoice && efforts.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1131
- className: "dsh-ui-field",
1132
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1133
- className: "dsh-ui-label",
1134
- children: text.effort
1135
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1136
- className: "dsh-ui-select",
1137
- value: savedModel.reasoningEffort ?? "",
1138
- disabled: busy || !status,
1139
- onChange: (event) => {
1140
- const effort = event.target.value;
1141
- const model = {
1142
- provider: savedModel.provider,
1143
- model: savedModel.model
1144
- };
1145
- saveSettings({ model: effort ? {
1146
- ...model,
1147
- reasoningEffort: effort
1148
- } : model });
1149
- },
1150
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1151
- value: "",
1152
- children: text.effortDefault
1153
- }), efforts.map((effort) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1154
- value: effort.id,
1155
- children: effort.name
1156
- }, effort.id))]
1157
- })]
1158
- }) : null,
1159
1002
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1160
1003
  className: "dsh-ui-field",
1161
1004
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
package/lib/index.d.ts CHANGED
@@ -162,7 +162,7 @@ declare function selectRecentMessages(messages: readonly TitleMessage[], maxRece
162
162
  //#endregion
163
163
  //#region src/index.d.ts
164
164
  declare const name = "@klarkxy/dsh-current-title";
165
- declare const inject: readonly ["sessionTitle", "llm", "sessions", "loader", "storageDomain", "connection", "webServer"];
165
+ declare const inject: readonly ["sessionTitle", "llm", "sessions", "loader", "storageDomain", "connection", "webServer", "agents", "sessionProjections", "agentDefaultModel"];
166
166
  declare module '@deepseek-ai/cordis' {
167
167
  interface Context {
168
168
  currentTitle: CurrentTitleService;
package/lib/index.js CHANGED
@@ -764,7 +764,10 @@ const inject = [
764
764
  "loader",
765
765
  "storageDomain",
766
766
  "connection",
767
- "webServer"
767
+ "webServer",
768
+ "agents",
769
+ "sessionProjections",
770
+ "agentDefaultModel"
768
771
  ];
769
772
  async function apply(ctx, config = {}) {
770
773
  const host = ctx;