@klarkxy/dsh-current-title 0.1.7 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -2
- package/docs/README.zh-CN.md +7 -2
- package/lib/client.inner.cjs +391 -96
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +392 -258
- package/lib/contracts.d.ts +7 -8
- package/lib/contracts.js +1 -22
- package/lib/contracts.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.js +82 -12
- package/lib/index.js.map +1 -1
- package/package.json +18 -11
package/lib/contracts.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ModelRoute, defaultModelRoute, normalizeModelRoute } from "@klarkxy/dsh-model-route";
|
|
1
2
|
import { RpcResult } from "@klarkxy/dsh-plugin-kit/contracts";
|
|
2
3
|
//#region src/contracts.d.ts
|
|
3
4
|
declare const PLUGIN_NAME = "@klarkxy/dsh-current-title";
|
|
@@ -36,12 +37,12 @@ interface TitleSnapshot {
|
|
|
36
37
|
readonly eventSeq: number;
|
|
37
38
|
readonly updatedAt: number;
|
|
38
39
|
}
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Empty provider and model means the shared weak role. Both must be set together.
|
|
42
|
+
* No title-only fields: the stored value is the shared ModelRoute (250/80 caps
|
|
43
|
+
* live in @klarkxy/dsh-model-route). The alias keeps the title-specific name.
|
|
44
|
+
*/
|
|
45
|
+
type TitleModelRoute = ModelRoute;
|
|
45
46
|
interface TitleSettings {
|
|
46
47
|
revision: number;
|
|
47
48
|
locale: TitleLocaleMode;
|
|
@@ -79,11 +80,9 @@ interface GenerateConfig {
|
|
|
79
80
|
readonly timeoutMs: number;
|
|
80
81
|
readonly maxTitleBytes: number;
|
|
81
82
|
}
|
|
82
|
-
declare function defaultModelRoute(): TitleModelRoute;
|
|
83
83
|
declare function defaultSettings(locale?: TitleLocaleMode): TitleSettings;
|
|
84
84
|
declare function normalizeCadence(value: unknown): TitleCadence;
|
|
85
85
|
declare function normalizePrompt(value: unknown): string;
|
|
86
|
-
declare function normalizeModelRoute(value: unknown): TitleModelRoute;
|
|
87
86
|
declare function defaultGenerateConfig(locale?: TitleLocaleMode): GenerateConfig;
|
|
88
87
|
declare function isTitleLocaleMode(value: unknown): value is TitleLocaleMode;
|
|
89
88
|
//#endregion
|
package/lib/contracts.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { defaultModelRoute, normalizeModelRoute } from "@klarkxy/dsh-model-route";
|
|
1
2
|
//#region src/contracts.ts
|
|
2
3
|
const PLUGIN_NAME = "@klarkxy/dsh-current-title";
|
|
3
4
|
const FEATURE_ID = "current-title";
|
|
@@ -17,12 +18,6 @@ const DEFAULT_MAX_INPUT_BYTES = 4096;
|
|
|
17
18
|
const DEFAULT_MAX_OUTPUT_TOKENS = 256;
|
|
18
19
|
const DEFAULT_TIMEOUT_MS = 6e4;
|
|
19
20
|
const DEFAULT_MAX_TITLE_BYTES = 80;
|
|
20
|
-
function defaultModelRoute() {
|
|
21
|
-
return {
|
|
22
|
-
provider: "",
|
|
23
|
-
model: ""
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
21
|
function defaultSettings(locale = "auto") {
|
|
27
22
|
return {
|
|
28
23
|
revision: 0,
|
|
@@ -39,22 +34,6 @@ function normalizePrompt(value) {
|
|
|
39
34
|
if (typeof value !== "string") return "";
|
|
40
35
|
return value.replace(/\u0000/gu, "").trim().slice(0, MAX_PROMPT_CHARS);
|
|
41
36
|
}
|
|
42
|
-
function normalizeModelRoute(value) {
|
|
43
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return defaultModelRoute();
|
|
44
|
-
const route = value;
|
|
45
|
-
const provider = typeof route.provider === "string" ? route.provider.trim() : "";
|
|
46
|
-
const model = typeof route.model === "string" ? route.model.trim() : "";
|
|
47
|
-
const effort = typeof route.reasoningEffort === "string" ? route.reasoningEffort.trim() : "";
|
|
48
|
-
if (!provider || !model || provider.length > 250 || model.length > 250) return defaultModelRoute();
|
|
49
|
-
return effort && effort.length <= 80 ? {
|
|
50
|
-
provider,
|
|
51
|
-
model,
|
|
52
|
-
reasoningEffort: effort
|
|
53
|
-
} : {
|
|
54
|
-
provider,
|
|
55
|
-
model
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
37
|
function defaultGenerateConfig(locale = "auto") {
|
|
59
38
|
return {
|
|
60
39
|
maxRecentMessages: 8,
|
package/lib/contracts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contracts.js","names":[],"sources":["../src/contracts.ts"],"sourcesContent":["/** Browser-safe contracts. Shared RPC/result types stay on @klarkxy/dsh-plugin-kit. */\nexport type { RpcResult } from '@klarkxy/dsh-plugin-kit/contracts'\n\nexport const PLUGIN_NAME = '@klarkxy/dsh-current-title'\nexport const FEATURE_ID = 'current-title'\nexport const PROVIDER_ID = '@klarkxy/dsh-current-title'\nexport const PURPOSE_ID = 'current-title.generate'\nexport const RPC_CHANNEL = '/dsh-current-title'\nexport const TITLE_CLIENT_SERVICE = 'dshCurrentTitleClient'\nexport const PROMPT_VERSION = 'current-title.v2'\nexport const SCHEMA_VERSION = 'title-json.v1'\nexport const CONTRACT_VERSION = 1\nexport const SETTINGS_KEY = 'current'\nexport const MAX_PROMPT_CHARS = 4000\n\nexport const DEFAULT_MAX_RECENT_MESSAGES = 8\nexport const DEFAULT_TARGET_WORDS = 5\nexport const DEFAULT_TARGET_CJK = 10\nexport const DEFAULT_MAX_INPUT_BYTES = 4096\nexport const DEFAULT_MAX_OUTPUT_TOKENS = 256\nexport const DEFAULT_TIMEOUT_MS = 60_000\nexport const DEFAULT_MAX_TITLE_BYTES = 80\n\nexport type TitleLocaleMode = 'auto' | 'zh' | 'en'\nexport type TitleSourceKind = 'fallback' | 'provider' | 'user'\n/** Automatic generation cadence: every eligible message, or only the first. */\nexport type TitleCadence = 'all-prompts' | 'first-prompt'\n\nexport interface TitleMessage {\n readonly seq: number\n readonly text: string\n}\n\nexport interface TitleSnapshot {\n readonly title: string\n readonly messageSeqs: readonly number[]\n readonly source: { readonly kind: TitleSourceKind; readonly provider?: string }\n readonly eventSeq: number\n readonly updatedAt: number\n}\n\n
|
|
1
|
+
{"version":3,"file":"contracts.js","names":[],"sources":["../src/contracts.ts"],"sourcesContent":["import { defaultModelRoute, normalizeModelRoute, type ModelRoute } from '@klarkxy/dsh-model-route'\nexport { defaultModelRoute, normalizeModelRoute }\n\n/** Browser-safe contracts. Shared RPC/result types stay on @klarkxy/dsh-plugin-kit. */\nexport type { RpcResult } from '@klarkxy/dsh-plugin-kit/contracts'\n\nexport const PLUGIN_NAME = '@klarkxy/dsh-current-title'\nexport const FEATURE_ID = 'current-title'\nexport const PROVIDER_ID = '@klarkxy/dsh-current-title'\nexport const PURPOSE_ID = 'current-title.generate'\nexport const RPC_CHANNEL = '/dsh-current-title'\nexport const TITLE_CLIENT_SERVICE = 'dshCurrentTitleClient'\nexport const PROMPT_VERSION = 'current-title.v2'\nexport const SCHEMA_VERSION = 'title-json.v1'\nexport const CONTRACT_VERSION = 1\nexport const SETTINGS_KEY = 'current'\nexport const MAX_PROMPT_CHARS = 4000\n\nexport const DEFAULT_MAX_RECENT_MESSAGES = 8\nexport const DEFAULT_TARGET_WORDS = 5\nexport const DEFAULT_TARGET_CJK = 10\nexport const DEFAULT_MAX_INPUT_BYTES = 4096\nexport const DEFAULT_MAX_OUTPUT_TOKENS = 256\nexport const DEFAULT_TIMEOUT_MS = 60_000\nexport const DEFAULT_MAX_TITLE_BYTES = 80\n\nexport type TitleLocaleMode = 'auto' | 'zh' | 'en'\nexport type TitleSourceKind = 'fallback' | 'provider' | 'user'\n/** Automatic generation cadence: every eligible message, or only the first. */\nexport type TitleCadence = 'all-prompts' | 'first-prompt'\n\nexport interface TitleMessage {\n readonly seq: number\n readonly text: string\n}\n\nexport interface TitleSnapshot {\n readonly title: string\n readonly messageSeqs: readonly number[]\n readonly source: { readonly kind: TitleSourceKind; readonly provider?: string }\n readonly eventSeq: number\n readonly updatedAt: number\n}\n\n/**\n * Empty provider and model means the shared weak role. Both must be set together.\n * No title-only fields: the stored value is the shared ModelRoute (250/80 caps\n * live in @klarkxy/dsh-model-route). The alias keeps the title-specific name.\n */\nexport type TitleModelRoute = ModelRoute\n\nexport interface TitleSettings {\n revision: number\n locale: TitleLocaleMode\n /** Empty keeps the built-in title instruction. */\n prompt: string\n model: TitleModelRoute\n cadence: TitleCadence\n}\n\nexport interface TitleSupport {\n nativeOwner: string | null\n weOwn: boolean\n limitation?: string\n}\n\nexport interface TitleStatus {\n settings: TitleSettings\n support: TitleSupport\n session?: {\n sessionId: string\n title?: string\n sourceKind?: TitleSourceKind\n generating: boolean\n pinned: boolean\n }\n}\n\nexport interface TitleClientMarker {\n active: boolean\n}\n\nexport interface GenerateConfig {\n readonly maxRecentMessages: number\n readonly locale: TitleLocaleMode\n readonly targetWords: number\n readonly targetCjkCharacters: number\n readonly maxInputBytes: number\n readonly maxOutputTokens: number\n readonly timeoutMs: number\n readonly maxTitleBytes: number\n}\n\nexport function defaultSettings(locale: TitleLocaleMode = 'auto'): TitleSettings {\n return { revision: 0, locale, prompt: '', model: defaultModelRoute(), cadence: 'all-prompts' }\n}\n\nexport function normalizeCadence(value: unknown): TitleCadence {\n return value === 'first-prompt' ? 'first-prompt' : 'all-prompts'\n}\n\nexport function normalizePrompt(value: unknown): string {\n if (typeof value !== 'string') return ''\n return value.replace(/\\u0000/gu, '').trim().slice(0, MAX_PROMPT_CHARS)\n}\n\nexport function defaultGenerateConfig(locale: TitleLocaleMode = 'auto'): GenerateConfig {\n return {\n maxRecentMessages: DEFAULT_MAX_RECENT_MESSAGES,\n locale,\n targetWords: DEFAULT_TARGET_WORDS,\n targetCjkCharacters: DEFAULT_TARGET_CJK,\n maxInputBytes: DEFAULT_MAX_INPUT_BYTES,\n maxOutputTokens: DEFAULT_MAX_OUTPUT_TOKENS,\n timeoutMs: DEFAULT_TIMEOUT_MS,\n maxTitleBytes: DEFAULT_MAX_TITLE_BYTES,\n }\n}\n\nexport function isTitleLocaleMode(value: unknown): value is TitleLocaleMode {\n return value === 'auto' || value === 'zh' || value === 'en'\n}\n"],"mappings":";;AAMA,MAAa,cAAc;AAC3B,MAAa,aAAa;AAC1B,MAAa,cAAc;AAC3B,MAAa,aAAa;AAC1B,MAAa,cAAc;AAC3B,MAAa,uBAAuB;AACpC,MAAa,iBAAiB;AAC9B,MAAa,iBAAiB;AAC9B,MAAa,mBAAmB;AAChC,MAAa,eAAe;AAC5B,MAAa,mBAAmB;AAEhC,MAAa,8BAA8B;AAC3C,MAAa,uBAAuB;AACpC,MAAa,qBAAqB;AAClC,MAAa,0BAA0B;AACvC,MAAa,4BAA4B;AACzC,MAAa,qBAAqB;AAClC,MAAa,0BAA0B;AAqEvC,SAAgB,gBAAgB,SAA0B,QAAuB;CAC/E,OAAO;EAAE,UAAU;EAAG;EAAQ,QAAQ;EAAI,OAAO,kBAAkB;EAAG,SAAS;CAAc;AAC/F;AAEA,SAAgB,iBAAiB,OAA8B;CAC7D,OAAO,UAAU,iBAAiB,iBAAiB;AACrD;AAEA,SAAgB,gBAAgB,OAAwB;CACtD,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,OAAO,MAAM,QAAQ,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,gBAAgB;AACvE;AAEA,SAAgB,sBAAsB,SAA0B,QAAwB;CACtF,OAAO;EACL,mBAAA;EACA;EACA,aAAA;EACA,qBAAA;EACA,eAAe;EACf,iBAAA;EACA,WAAW;EACX,eAAA;CACF;AACF;AAEA,SAAgB,kBAAkB,OAA0C;CAC1E,OAAO,UAAU,UAAU,UAAU,QAAQ,UAAU;AACzD"}
|
package/lib/index.d.ts
CHANGED
|
@@ -137,6 +137,7 @@ declare class CurrentTitleService {
|
|
|
137
137
|
localePreference?: () => string | undefined;
|
|
138
138
|
initialLocale?: TitleLocaleMode;
|
|
139
139
|
now?: () => Date;
|
|
140
|
+
onSettingsChanged?: (revision: number) => void;
|
|
140
141
|
});
|
|
141
142
|
start(): Promise<void>;
|
|
142
143
|
dispose(): Promise<void>;
|
|
@@ -162,7 +163,7 @@ declare function selectRecentMessages(messages: readonly TitleMessage[], maxRece
|
|
|
162
163
|
//#endregion
|
|
163
164
|
//#region src/index.d.ts
|
|
164
165
|
declare const name = "@klarkxy/dsh-current-title";
|
|
165
|
-
declare const inject: readonly ["sessionTitle", "llm", "sessions", "loader", "storageDomain", "connection", "webServer"];
|
|
166
|
+
declare const inject: readonly ["sessionTitle", "llm", "sessions", "loader", "storageDomain", "connection", "webServer", "agents", "sessionProjections", "agentDefaultModel"];
|
|
166
167
|
declare module '@deepseek-ai/cordis' {
|
|
167
168
|
interface Context {
|
|
168
169
|
currentTitle: CurrentTitleService;
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { PLUGIN_NAME, PROVIDER_ID, RPC_CHANNEL, SETTINGS_KEY, defaultGenerateConfig, defaultModelRoute, isTitleLocaleMode, normalizeCadence, normalizeModelRoute, normalizePrompt } from "./contracts.js";
|
|
1
|
+
import { DEFAULT_TIMEOUT_MS, PLUGIN_NAME, PROVIDER_ID, RPC_CHANNEL, SETTINGS_KEY, defaultGenerateConfig, defaultModelRoute, isTitleLocaleMode, normalizeCadence, normalizeModelRoute, normalizePrompt } from "./contracts.js";
|
|
2
2
|
import { registerHostRpc } from "@klarkxy/dsh-plugin-kit/host-rpc";
|
|
3
|
+
import { notifyModelSettingsChanged, registerModelSettings } from "@klarkxy/dsh-plugin-kit/model-settings";
|
|
4
|
+
import "@klarkxy/dsh-model-route";
|
|
3
5
|
import { isSubagentSession } from "@klarkxy/dsh-plugin-kit/contracts";
|
|
4
6
|
import { callLlmText, resolveFeatureModel } from "@klarkxy/dsh-plugin-kit";
|
|
5
7
|
import { defineDomain, domainTable } from "@deepseek-ai/dsh-storage-domain";
|
|
@@ -214,6 +216,12 @@ function systemPrompt(config, customPrompt = "") {
|
|
|
214
216
|
`Aim for at most ${config.targetWords} non-CJK words or ${config.targetCjkCharacters} CJK characters.`
|
|
215
217
|
].join("\n");
|
|
216
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* Saved override passed to resolveFeatureModel. Unlike modelRouteOverride,
|
|
221
|
+
* this does not trim or re-apply the 250/80 caps: stored rows are already
|
|
222
|
+
* normalized, and a whitespace-only id must stay a miss (not a trimmed hit)
|
|
223
|
+
* so the host-default fallback is unchanged.
|
|
224
|
+
*/
|
|
217
225
|
function titleModelRoute(route) {
|
|
218
226
|
if (!route?.provider || !route.model) return void 0;
|
|
219
227
|
return route.reasoningEffort ? {
|
|
@@ -239,17 +247,37 @@ async function generateCurrentTitle(options) {
|
|
|
239
247
|
const framedInput = frameMessages(selected);
|
|
240
248
|
const locale = resolveTitleLocale(options.localeMode ?? options.config.locale, selected.map((message) => message.text), options.localePreference);
|
|
241
249
|
const sourceVersion = sourceVersionOf(options.sessionId, selected);
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
signal
|
|
250
|
-
|
|
251
|
-
isCurrent: options.isCurrent
|
|
250
|
+
const deadline = new AbortController();
|
|
251
|
+
const timeoutMs = Number.isFinite(options.config.timeoutMs) && options.config.timeoutMs > 0 ? options.config.timeoutMs : DEFAULT_TIMEOUT_MS;
|
|
252
|
+
const timer = setTimeout(() => deadline.abort(new DOMException("Title generation timed out.", "TimeoutError")), timeoutMs);
|
|
253
|
+
const signal = AbortSignal.any([options.signal, deadline.signal]);
|
|
254
|
+
let abort = () => {};
|
|
255
|
+
const cancelled = new Promise((_resolve, reject) => {
|
|
256
|
+
abort = () => reject(signal.reason);
|
|
257
|
+
if (signal.aborted) abort();
|
|
258
|
+
else signal.addEventListener("abort", abort, { once: true });
|
|
252
259
|
});
|
|
260
|
+
let result;
|
|
261
|
+
try {
|
|
262
|
+
result = await Promise.race([callLlmText(options.llm, {
|
|
263
|
+
plugin: PLUGIN_NAME,
|
|
264
|
+
route: callRoute,
|
|
265
|
+
system: systemPrompt(options.config, options.prompt),
|
|
266
|
+
text: framedInput,
|
|
267
|
+
maxTokens: options.config.maxOutputTokens,
|
|
268
|
+
purpose: "session-title",
|
|
269
|
+
signal,
|
|
270
|
+
sessionId: options.sessionId,
|
|
271
|
+
isCurrent: options.isCurrent
|
|
272
|
+
}), cancelled]);
|
|
273
|
+
signal.throwIfAborted();
|
|
274
|
+
} catch (error) {
|
|
275
|
+
signal.throwIfAborted();
|
|
276
|
+
throw error;
|
|
277
|
+
} finally {
|
|
278
|
+
clearTimeout(timer);
|
|
279
|
+
signal.removeEventListener("abort", abort);
|
|
280
|
+
}
|
|
253
281
|
options.signal.throwIfAborted();
|
|
254
282
|
if (!options.isCurrent()) throw Object.assign(/* @__PURE__ */ new Error("dsh-current-title: generation is no longer current"), { code: "superseded" });
|
|
255
283
|
const title = normalizeSessionTitle(formatTitle(parseModelTitle(result.text, options.config.targetWords, options.config.targetCjkCharacters), locale, options.now ?? /* @__PURE__ */ new Date()), options.config.maxTitleBytes);
|
|
@@ -544,6 +572,9 @@ var CurrentTitleService = class {
|
|
|
544
572
|
};
|
|
545
573
|
if (this.options.store) await this.options.store.save(next);
|
|
546
574
|
this.settings = next;
|
|
575
|
+
try {
|
|
576
|
+
this.options.onSettingsChanged?.(next.revision);
|
|
577
|
+
} catch {}
|
|
547
578
|
this.config = {
|
|
548
579
|
...this.config,
|
|
549
580
|
locale: "auto"
|
|
@@ -764,7 +795,10 @@ const inject = [
|
|
|
764
795
|
"loader",
|
|
765
796
|
"storageDomain",
|
|
766
797
|
"connection",
|
|
767
|
-
"webServer"
|
|
798
|
+
"webServer",
|
|
799
|
+
"agents",
|
|
800
|
+
"sessionProjections",
|
|
801
|
+
"agentDefaultModel"
|
|
768
802
|
];
|
|
769
803
|
async function apply(ctx, config = {}) {
|
|
770
804
|
const host = ctx;
|
|
@@ -773,6 +807,7 @@ async function apply(ctx, config = {}) {
|
|
|
773
807
|
const locale = isTitleLocaleMode(config.locale) ? config.locale : "auto";
|
|
774
808
|
const service = new CurrentTitleService({
|
|
775
809
|
plugin: PLUGIN_NAME,
|
|
810
|
+
onSettingsChanged: (revision) => notifyModelSettingsChanged(ctx, PLUGIN_NAME, revision),
|
|
776
811
|
llm: host.llm,
|
|
777
812
|
host,
|
|
778
813
|
sessionTitle: host.sessionTitle,
|
|
@@ -792,6 +827,41 @@ async function apply(ctx, config = {}) {
|
|
|
792
827
|
await domain.close();
|
|
793
828
|
}, "current-title.lifecycle");
|
|
794
829
|
ctx.effect(() => registerHostRpc(host, RPC_CHANNEL, (endpoint, payload, signal) => handleRpc(service, endpoint, payload, signal)), "current-title.rpc");
|
|
830
|
+
ctx.effect(() => registerModelSettings(ctx, {
|
|
831
|
+
ns: PLUGIN_NAME,
|
|
832
|
+
title: {
|
|
833
|
+
zh: "自动标题",
|
|
834
|
+
en: "Auto Title"
|
|
835
|
+
},
|
|
836
|
+
read(signal) {
|
|
837
|
+
signal.throwIfAborted();
|
|
838
|
+
const settings = service.status().settings;
|
|
839
|
+
return {
|
|
840
|
+
revision: settings.revision,
|
|
841
|
+
fields: [{
|
|
842
|
+
path: ["model"],
|
|
843
|
+
label: {
|
|
844
|
+
zh: "标题模型",
|
|
845
|
+
en: "Title model"
|
|
846
|
+
},
|
|
847
|
+
current: settings.model,
|
|
848
|
+
followLabel: {
|
|
849
|
+
zh: "当前会话 / 宿主默认模型",
|
|
850
|
+
en: "Current conversation / host default"
|
|
851
|
+
},
|
|
852
|
+
allowFollow: true
|
|
853
|
+
}]
|
|
854
|
+
};
|
|
855
|
+
},
|
|
856
|
+
async write(path, route, expectedRevision, signal) {
|
|
857
|
+
if (path.length !== 1 || path[0] !== "model") throw new Error("Unknown Current Title model setting");
|
|
858
|
+
signal.throwIfAborted();
|
|
859
|
+
return service.updateSettings({
|
|
860
|
+
model: route,
|
|
861
|
+
expectedRevision
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
}), "current-title.model-settings");
|
|
795
865
|
}
|
|
796
866
|
//#endregion
|
|
797
867
|
export { CurrentTitleService, apply, formatTitle, frameMessages, inject, name, parseModelTitle, resolveTitleLocale, selectRecentMessages };
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/input.ts","../src/output.ts","../src/messages.ts","../src/generate.ts","../src/ownership.ts","../src/native-slot.ts","../src/storage.ts","../src/service.ts","../src/index.ts"],"sourcesContent":["import type { TitleMessage } from './contracts.ts'\n\nexport const FRAME_PREFIX =\n 'Choose the current task from these recent human messages, ordered oldest to newest:\\n'\n\nexport function frameMessages(messages: readonly TitleMessage[]): string {\n return `${FRAME_PREFIX}${JSON.stringify(messages)}`\n}\n\nexport function utf8Bytes(value: string): number {\n return Buffer.byteLength(value, 'utf8')\n}\n\nfunction middleSlice(codePoints: readonly string[], kept: number): string {\n if (kept >= codePoints.length) return codePoints.join('')\n if (kept <= 0) return ''\n if (kept === 1) return '…'\n\n const contentPoints = kept - 1\n const headCount = Math.ceil(contentPoints / 2)\n const tailCount = Math.floor(contentPoints / 2)\n return `${codePoints.slice(0, headCount).join('')}…${\n tailCount === 0 ? '' : codePoints.slice(-tailCount).join('')\n }`\n}\n\nexport function fitSingleMessage(message: TitleMessage, maxInputBytes: number): TitleMessage {\n const empty = { ...message, text: '' }\n if (utf8Bytes(frameMessages([empty])) > maxInputBytes) {\n throw new Error('dsh-current-title: maxInputBytes is too small for the input envelope')\n }\n if (utf8Bytes(frameMessages([message])) <= maxInputBytes) return message\n\n const points = Array.from(message.text)\n let low = 0\n let high = points.length\n let best = ''\n\n while (low <= high) {\n const kept = Math.floor((low + high) / 2)\n const text = middleSlice(points, kept)\n if (utf8Bytes(frameMessages([{ ...message, text }])) <= maxInputBytes) {\n best = text\n low = kept + 1\n } else {\n high = kept - 1\n }\n }\n\n return { ...message, text: best }\n}\n\nexport function selectRecentMessages(\n messages: readonly TitleMessage[],\n maxRecentMessages: number,\n maxInputBytes: number,\n): TitleMessage[] {\n if (messages.length === 0) return []\n\n const selected = messages.slice(-maxRecentMessages).map(message => ({ ...message }))\n while (selected.length > 1 && utf8Bytes(frameMessages(selected)) > maxInputBytes) {\n selected.shift()\n }\n\n if (utf8Bytes(frameMessages(selected)) > maxInputBytes) {\n selected[0] = fitSingleMessage(selected[0]!, maxInputBytes)\n }\n\n return selected\n}\n","export const TITLE_TYPE_KEYS = [\n 'feature',\n 'fix',\n 'optimize',\n 'refactor',\n 'test',\n 'docs',\n 'release',\n 'config',\n 'explore',\n 'discuss',\n] as const\n\nexport const TITLE_LOCALES = ['zh', 'en'] as const\nexport const TITLE_LOCALE_MODES = ['auto', ...TITLE_LOCALES] as const\n\nexport type TitleType = (typeof TITLE_TYPE_KEYS)[number]\nexport type TitleLocale = (typeof TITLE_LOCALES)[number]\nexport type TitleLocaleMode = (typeof TITLE_LOCALE_MODES)[number]\n\nconst TITLE_TYPE_LABELS: Record<TitleLocale, Record<TitleType, string>> = {\n zh: {\n feature: '功能',\n fix: '修复',\n optimize: '优化',\n refactor: '重构',\n test: '测试',\n docs: '文档',\n release: '发布',\n config: '配置',\n explore: '探索',\n discuss: '讨论',\n },\n en: {\n feature: 'Feature',\n fix: 'Fix',\n optimize: 'Optimize',\n refactor: 'Refactor',\n test: 'Test',\n docs: 'Docs',\n release: 'Release',\n config: 'Config',\n explore: 'Explore',\n discuss: 'Discussion',\n },\n}\n\nexport interface ParsedTitle {\n type: TitleType\n summary: string\n}\n\nconst OSC_SEQUENCE = /(?:\\u001B\\]|\\u009D)(?:(?!\\u0007|\\u001B\\\\)[\\s\\S])*(?:\\u0007|\\u001B\\\\|$)/gu\nconst CSI_SEQUENCE = /(?:\\u001B\\[|\\u009B)[0-?]*[ -/]*[@-~]/gu\nconst ESC_SEQUENCE = /\\u001B[@-_]/gu\nconst CONTROL_CHARACTER = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u009F]/gu\nconst DIRECTIONAL_CONTROL = /[\\u200B\\u200E\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u206F\\uFEFF]/gu\n\nfunction cleanTitleText(input: string): string {\n return input\n .replace(OSC_SEQUENCE, '')\n .replace(CSI_SEQUENCE, '')\n .replace(ESC_SEQUENCE, '')\n .replace(CONTROL_CHARACTER, '')\n .replace(DIRECTIONAL_CONTROL, '')\n .replace(/\\s+/gu, ' ')\n .trim()\n}\n\nexport function truncateTitleUtf8(input: string, maxBytes: number): string {\n if (!Number.isInteger(maxBytes) || maxBytes <= 0) throw new Error('maxBytes must be a positive integer')\n if (Buffer.byteLength(input, 'utf8') <= maxBytes) return input\n let used = 0\n let output = ''\n for (const character of input) {\n const bytes = Buffer.byteLength(character, 'utf8')\n if (used + bytes > maxBytes) break\n output += character\n used += bytes\n }\n return output\n}\n\n/** Same sanitizing rules as DSH 0.1.7-rc.2 `normalizeSessionTitle`. */\nexport function normalizeSessionTitle(input: string, maxBytes: number): string {\n return truncateTitleUtf8(cleanTitleText(input), maxBytes).trimEnd()\n}\n\nfunction isTitleType(value: string): value is TitleType {\n return (TITLE_TYPE_KEYS as readonly string[]).includes(value)\n}\n\nexport function resolveTitleLocale(\n mode: TitleLocaleMode,\n messages: readonly string[],\n preference?: string,\n): TitleLocale {\n if (mode !== 'auto') return mode\n\n const preferred = preference?.trim().toLowerCase().split('-')[0]\n if (preferred === 'zh' || preferred === 'en') return preferred\n\n const text = messages.join('\\n')\n if (/[\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]/u.test(text)) return 'en'\n return /\\p{Script=Han}/u.test(text) ? 'zh' : 'en'\n}\n\nfunction removeTitleSyntax(value: string): string {\n return value.replace(/[|`*_~#\\[\\]\"“”]/gu, ' ')\n}\n\nfunction truncateSummary(value: string, targetWords: number, targetCjkCharacters: number): string {\n const containsCjk = /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]/u.test(value)\n if (containsCjk) {\n return Array.from(value.replace(/\\s+/gu, '')).slice(0, targetCjkCharacters).join('')\n }\n return value.split(/\\s+/u).filter(Boolean).slice(0, targetWords).join(' ')\n}\n\nfunction unwrapModelText(raw: string): string {\n const trimmed = raw.trim()\n const fenced = /^```(?:json)?\\s*([\\s\\S]*?)\\s*```$/iu.exec(trimmed)\n return (fenced?.[1] ?? trimmed).trim()\n}\n\nfunction readJsonObject(raw: string): Record<string, unknown> | undefined {\n const text = unwrapModelText(raw)\n const start = text.indexOf('{')\n const end = text.lastIndexOf('}')\n if (start === -1 || end <= start) return undefined\n try {\n const value = JSON.parse(text.slice(start, end + 1))\n if (value && typeof value === 'object' && !Array.isArray(value)) return value as Record<string, unknown>\n } catch {\n return undefined\n }\n return undefined\n}\n\n/**\n * Accept the required JSON shape, including a code fence or a short preface.\n * Extra keys are ignored. A plain one-line title still becomes a title so a\n * chatty model does not leave the session on the built-in fallback.\n */\nexport function parseModelTitle(\n raw: string,\n targetWords: number,\n targetCjkCharacters: number,\n): ParsedTitle {\n const candidate = readJsonObject(raw)\n const typeValue = typeof candidate?.type === 'string' ? candidate.type.trim().toLowerCase() : ''\n const summaryValue = typeof candidate?.summary === 'string' ? candidate.summary : ''\n const type = isTitleType(typeValue) ? typeValue : 'discuss'\n const source = summaryValue || (candidate ? '' : unwrapModelText(raw).replace(/^[\"'`]+|[\"'`]+$/gu, ''))\n const normalized = normalizeSessionTitle(removeTitleSyntax(source), Number.MAX_SAFE_INTEGER)\n const summary = truncateSummary(normalized, targetWords, targetCjkCharacters)\n if (summary.length === 0) {\n throw new Error('dsh-current-title: title model returned an empty summary')\n }\n return { type, summary }\n}\n\nexport function localMonthDay(now: Date): string {\n const month = String(now.getMonth() + 1).padStart(2, '0')\n const day = String(now.getDate()).padStart(2, '0')\n return `${month}${day}`\n}\n\nexport function formatTitle(parsed: ParsedTitle, locale: TitleLocale, now = new Date()): string {\n return `${localMonthDay(now)} | ${TITLE_TYPE_LABELS[locale][parsed.type]} | ${parsed.summary}`\n}\n","import { normalizeSessionTitle } from './output.ts'\nimport type { TitleMessage, TitleSnapshot, TitleSourceKind } from './contracts.ts'\n\nexport interface SessionEventLike {\n readonly type: string\n readonly seq: number\n readonly time?: number\n readonly data?: {\n readonly source?: { readonly kind?: string; readonly plugin?: string; readonly provider?: string }\n readonly content?: ReadonlyArray<{ readonly type?: string; readonly text?: string }>\n readonly title?: string\n readonly messageSeqs?: readonly number[]\n }\n}\n\nexport interface SessionLike {\n readonly id: string\n readonly seq?: number\n snapshotEvents(): readonly SessionEventLike[]\n append?(type: string, data: unknown): unknown\n}\n\nfunction textFromContent(content: ReadonlyArray<{ readonly type?: string; readonly text?: string }> | undefined): string {\n if (!Array.isArray(content)) return ''\n return content\n .filter(block => block && block.type === 'text' && typeof block.text === 'string')\n .map(block => block.text as string)\n .join('\\n')\n}\n\n/** Eligible human prompts only. Plugin/tool/model user-role rows are excluded. */\nexport function collectHumanMessages(events: readonly SessionEventLike[], throughSeq?: number): TitleMessage[] {\n const messages: TitleMessage[] = []\n for (const event of events) {\n if (throughSeq !== undefined && event.seq > throughSeq) break\n if (event.type !== 'user/message') continue\n if (event.data?.source?.kind !== 'user') continue\n const text = textFromContent(event.data.content)\n if (normalizeSessionTitle(text, Number.MAX_SAFE_INTEGER).length === 0) continue\n messages.push({ seq: event.seq, text })\n }\n return messages\n}\n\nexport function foldTitle(events: readonly SessionEventLike[]): TitleSnapshot | undefined {\n let latest: SessionEventLike | undefined\n for (const event of events) {\n if (event.type === 'session/title') latest = event\n }\n if (!latest?.data || typeof latest.data.title !== 'string') return undefined\n const kind = latest.data.source?.kind\n const sourceKind: TitleSourceKind = kind === 'user' || kind === 'fallback' || kind === 'provider' ? kind : 'provider'\n return {\n title: latest.data.title,\n messageSeqs: latest.data.messageSeqs ? [...latest.data.messageSeqs] : [],\n source: { kind: sourceKind, ...(typeof latest.data.source?.provider === 'string' ? { provider: latest.data.source.provider } : {}) },\n eventSeq: latest.seq,\n updatedAt: latest.time ?? 0,\n }\n}\n\nexport function userRenameNewerThan(events: readonly SessionEventLike[], watermarkSeq: number): boolean {\n const current = foldTitle(events)\n return Boolean(current && current.source.kind === 'user' && current.eventSeq > watermarkSeq)\n}\n\nexport function sourceVersionOf(sessionId: string, messages: readonly TitleMessage[]): string {\n const last = messages.at(-1)\n return `${sessionId}:${last?.seq ?? -1}:${messages.length}`\n}\n","import { callLlmText, resolveFeatureModel, type LlmTextCaller } from '@klarkxy/dsh-plugin-kit'\nimport type { ModelRoute } from '@klarkxy/dsh-plugin-kit/contracts'\nimport type { GenerateConfig, TitleMessage, TitleModelRoute } from './contracts.ts'\nimport { PLUGIN_NAME } from './contracts.ts'\nimport { frameMessages, selectRecentMessages } from './input.ts'\nimport { formatTitle, normalizeSessionTitle, parseModelTitle, resolveTitleLocale, type TitleLocaleMode } from './output.ts'\nimport { sourceVersionOf } from './messages.ts'\n\nexport interface GeneratedTitle {\n readonly title: string\n readonly messageSeqs: readonly number[]\n readonly model?: { readonly provider: string; readonly model: string }\n readonly sourceVersion: string\n}\n\nexport function systemPrompt(config: GenerateConfig, customPrompt = ''): string {\n const instruction = customPrompt.trim()\n return [\n instruction || 'Name the current task in an AI coding-assistant session.',\n 'Prioritize the newest human messages. Older messages are context only and must not keep a completed task in the title.',\n 'Allowed type values: feature, fix, optimize, refactor, test, docs, release, config, explore, discuss.',\n 'Return exactly one JSON object: {\"type\":\"<type>\",\"summary\":\"<summary>\"}.',\n 'Return no Markdown, code fence, explanation, date, or extra key.',\n 'Keep the summary in the language of the recent messages.',\n `Aim for at most ${config.targetWords} non-CJK words or ${config.targetCjkCharacters} CJK characters.`,\n ].join('\\n')\n}\n\nexport function titleModelRoute(route: TitleModelRoute | undefined): ModelRoute | undefined {\n if (!route?.provider || !route.model) return undefined\n return route.reasoningEffort\n ? { provider: route.provider, model: route.model, reasoningEffort: route.reasoningEffort }\n : { provider: route.provider, model: route.model }\n}\n\nexport async function generateCurrentTitle(options: {\n llm: LlmTextCaller\n host: unknown\n config: GenerateConfig\n sessionId: string\n messages: readonly TitleMessage[]\n prompt?: string\n model?: TitleModelRoute\n localePreference?: string\n localeMode?: TitleLocaleMode\n signal: AbortSignal\n isCurrent: () => boolean\n now?: Date\n}): Promise<GeneratedTitle> {\n options.signal.throwIfAborted()\n if (!options.isCurrent()) {\n throw Object.assign(new Error('dsh-current-title: generation is no longer current'), { code: 'superseded' })\n }\n const selected = selectRecentMessages(\n options.messages,\n options.config.maxRecentMessages,\n options.config.maxInputBytes,\n )\n if (selected.length === 0) throw new Error('dsh-current-title: at least one source message is required')\n const route = resolveFeatureModel(options.host, titleModelRoute(options.model), options.sessionId)\n if (!route) throw new Error('dsh-current-title: 请在插件页选择模型,或设置宿主默认对话模型。')\n // Titles never think: reasoning effort is never forwarded (matching the\n // bundled dsh-session-title-llm policy), so a thinking model cannot burn the\n // small token cap on hidden reasoning and leave an empty reply.\n const callRoute = { provider: route.provider, model: route.model }\n\n const framedInput = frameMessages(selected)\n const locale = resolveTitleLocale(\n options.localeMode ?? options.config.locale,\n selected.map(message => message.text),\n options.localePreference,\n )\n const sourceVersion = sourceVersionOf(options.sessionId, selected)\n const result = await callLlmText(options.llm, {\n plugin: PLUGIN_NAME,\n route: callRoute,\n system: systemPrompt(options.config, options.prompt),\n text: framedInput,\n maxTokens: options.config.maxOutputTokens,\n purpose: 'session-title',\n signal: options.signal,\n sessionId: options.sessionId,\n isCurrent: options.isCurrent,\n })\n options.signal.throwIfAborted()\n if (!options.isCurrent()) {\n throw Object.assign(new Error('dsh-current-title: generation is no longer current'), { code: 'superseded' })\n }\n const parsed = parseModelTitle(result.text, options.config.targetWords, options.config.targetCjkCharacters)\n const title = normalizeSessionTitle(formatTitle(parsed, locale, options.now ?? new Date()), options.config.maxTitleBytes)\n if (!title) throw new Error('dsh-current-title: title model returned an empty summary')\n return {\n title,\n messageSeqs: selected.map(message => message.seq),\n model: { provider: result.provider, model: result.model },\n sourceVersion,\n }\n}\n","export interface OwnershipClaim {\n readonly id: string\n readonly dispose: () => void | Promise<void>\n}\n\nexport interface TitleSlot {\n owner(): string | undefined\n occupy(id: string): Promise<() => void | Promise<void>>\n}\n\n/** Exclusive title-provider ownership. Enable claims the slot; disable releases only while this id still holds it. */\nexport async function claimTitleSlot(slot: TitleSlot, id: string): Promise<OwnershipClaim> {\n const dispose = await slot.occupy(id)\n return { id, dispose }\n}\n\nexport async function releaseTitleSlot(\n slot: TitleSlot,\n claim: OwnershipClaim | undefined,\n): Promise<'released' | 'left-other-owner' | 'idle'> {\n if (!claim) return 'idle'\n if (slot.owner() !== claim.id) return 'left-other-owner'\n await claim.dispose()\n if (slot.owner() && slot.owner() !== claim.id) return 'left-other-owner'\n return 'released'\n}\n\nexport function occupiedProviderId(error: unknown): string | undefined {\n const message = error instanceof Error ? error.message : String(error)\n return /session-title provider \"([^\"]+)\" is already registered/.exec(message)?.[1]\n}\n","import { PROVIDER_ID } from './contracts.ts'\nimport { occupiedProviderId, type TitleSlot } from './ownership.ts'\n\nexport interface SessionTitleProvider {\n readonly id: string\n readonly automatic: 'first-prompt' | 'all-prompts'\n generate(request: {\n session: { id: string; snapshotEvents(): readonly unknown[] }\n messages: ReadonlyArray<{ seq: number; text: string }>\n route?: { provider: string; model: string }\n signal: AbortSignal\n }): Promise<{ title: string; messageSeqs: readonly number[]; model?: { provider: string; model: string } }>\n}\n\nexport interface SessionTitleServiceLike {\n register(provider: SessionTitleProvider): () => void | Promise<void>\n get(session: unknown): { title: string; source?: { kind?: string; provider?: string }; eventSeq?: number; updatedAt?: number; messageSeqs?: readonly number[] } | undefined\n refresh(session: unknown, signal?: AbortSignal): Promise<unknown>\n}\n\nexport interface LoaderEntryOptions {\n id?: string\n name?: string\n disabled?: boolean | null\n config?: unknown\n inject?: unknown\n group?: boolean | null\n}\n\nexport interface LoaderEntry {\n id: string\n options?: LoaderEntryOptions\n}\n\nexport interface LoaderFace {\n entries(): Iterable<LoaderEntry>\n update(id: string, options: { disabled?: boolean | null }): Promise<unknown> | unknown\n}\n\nexport interface Displacement {\n readonly id: string\n readonly name: string\n readonly identity: string\n}\n\nfunction catalogLookupId(entryId: string): string {\n const colon = entryId.indexOf(':')\n return colon === -1 ? entryId : entryId.slice(colon + 1)\n}\n\nfunction entryName(entry: LoaderEntry): string {\n return String(entry.options?.name ?? '')\n}\n\nexport function entryDisabled(entry: LoaderEntry): boolean {\n return entry.options?.disabled === true\n}\n\nexport function displacementIdentity(entry: LoaderEntry): string {\n const options = entry.options ?? {}\n const { disabled: _disabled, ...rest } = options\n return JSON.stringify({ id: entry.id, name: entryName(entry), rest })\n}\n\nexport function captureDisplacement(entry: LoaderEntry): Displacement {\n return { id: entry.id, name: entryName(entry), identity: displacementIdentity(entry) }\n}\n\nexport function displacementUnchanged(snapshot: Displacement, entry: LoaderEntry): boolean {\n return entry.id === snapshot.id\n && entryName(entry) === snapshot.name\n && displacementIdentity(entry) === snapshot.identity\n}\n\nexport function findEntryById(loader: LoaderFace, id: string): LoaderEntry | undefined {\n for (const entry of loader.entries()) {\n if (entry.id === id) return entry\n }\n return undefined\n}\n\nexport function findLoaderEntry(loader: LoaderFace, providerId: string): LoaderEntry | undefined {\n for (const entry of loader.entries()) {\n const name = entryName(entry)\n const ids = [entry.id, entry.options?.id, name, catalogLookupId(entry.id)]\n if (ids.some(value => value === providerId)) return entry\n if (name.endsWith(`/${providerId}`) || name.endsWith(providerId)) return entry\n if (providerId === 'session-title-first-prompt-llm' && catalogLookupId(entry.id) === 'session-title-llm') return entry\n }\n return undefined\n}\n\nexport async function restoreOwnDisplacement(loader: LoaderFace | undefined, snapshot: Displacement | undefined): Promise<'restored' | 'skipped'> {\n if (!loader || !snapshot) return 'skipped'\n const current = findEntryById(loader, snapshot.id)\n if (!current || !entryDisabled(current) || !displacementUnchanged(snapshot, current)) return 'skipped'\n await loader.update(snapshot.id, { disabled: false })\n return 'restored'\n}\n\nexport function createNativeTitleSlot(options: {\n sessionTitle: SessionTitleServiceLike\n loader: LoaderFace\n /** Read at each occupy so a settings change can re-register with a new cadence. */\n automatic: () => SessionTitleProvider['automatic']\n generate: SessionTitleProvider['generate']\n}): TitleSlot & { displacement?: Displacement } {\n let owner: string | undefined\n const slot: TitleSlot & { displacement?: Displacement } = {\n owner: () => owner,\n async occupy(id: string) {\n const provider: SessionTitleProvider = {\n id: PROVIDER_ID,\n automatic: options.automatic(),\n generate: options.generate,\n }\n try {\n const dispose = options.sessionTitle.register(provider)\n owner = id\n return async () => {\n await dispose()\n if (owner === id) owner = undefined\n }\n } catch (error) {\n const occupied = occupiedProviderId(error)\n if (!occupied) throw error\n const entry = findLoaderEntry(options.loader, occupied)\n if (!entry || entryDisabled(entry)) throw error\n const snapshot = captureDisplacement(entry)\n await options.loader.update(entry.id, { disabled: true })\n const afterDisable = findEntryById(options.loader, entry.id)\n if (!afterDisable || !displacementUnchanged(snapshot, afterDisable)) throw error\n try {\n const dispose = options.sessionTitle.register(provider)\n slot.displacement = snapshot\n owner = id\n return async () => {\n await dispose()\n if (owner === id) owner = undefined\n }\n } catch (second) {\n if (findEntryById(options.loader, snapshot.id) && displacementUnchanged(snapshot, findEntryById(options.loader, snapshot.id)!)) {\n await options.loader.update(snapshot.id, { disabled: false })\n }\n throw second\n }\n }\n },\n }\n return slot\n}\n","import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain'\nimport { z } from 'zod'\nimport {\n SETTINGS_KEY, defaultModelRoute, isTitleLocaleMode, normalizeCadence, normalizeModelRoute, normalizePrompt,\n type TitleModelRoute, type TitleSettings,\n} from './contracts.ts'\n\nexport { SETTINGS_KEY }\n\nconst localeSchema = z.enum(['auto', 'zh', 'en'])\n\n/** Version 1 stored only the locale. Prompt and model are filled as defaults. */\nconst legacySettingsSchema = z.object({\n revision: z.number().int().nonnegative(),\n locale: localeSchema,\n}).strict()\n\nconst modelRouteSchema = z.object({\n provider: z.string(),\n model: z.string(),\n reasoningEffort: z.string().optional(),\n}).strict()\n\nexport const titleSettingsSchema = z.object({\n revision: z.number().int().nonnegative(),\n locale: localeSchema,\n prompt: z.string(),\n model: modelRouteSchema,\n /** Defaulted so rows stored before the cadence switch existed still parse. */\n cadence: z.enum(['all-prompts', 'first-prompt']).default('all-prompts'),\n}).strict()\n\nexport const titleDomain = defineDomain({\n name: 'dsh_editor_current_title',\n version: 2,\n tables: {\n settings: domainTable<string, TitleSettings>(titleSettingsSchema),\n },\n})\n\nexport function parseTitleSettings(value: unknown): TitleSettings | undefined {\n const current = titleSettingsSchema.safeParse(value)\n if (current.success) {\n return {\n revision: current.data.revision,\n locale: isTitleLocaleMode(current.data.locale) ? current.data.locale : 'auto',\n prompt: normalizePrompt(current.data.prompt),\n model: normalizeModelRoute(current.data.model),\n cadence: normalizeCadence(current.data.cadence),\n }\n }\n const legacy = legacySettingsSchema.safeParse(value)\n if (!legacy.success || !isTitleLocaleMode(legacy.data.locale)) return undefined\n return {\n revision: legacy.data.revision,\n locale: legacy.data.locale,\n prompt: '',\n model: defaultModelRoute(),\n cadence: 'all-prompts',\n }\n}\n\nexport function storedSettings(value: unknown, _fallbackLocale: TitleSettings['locale'] = 'auto'): TitleSettings {\n const parsed = parseTitleSettings(value)\n return {\n revision: parsed?.revision ?? 0,\n locale: 'auto',\n prompt: parsed?.prompt ?? '',\n model: parsed?.model ?? defaultModelRoute(),\n cadence: parsed?.cadence ?? 'all-prompts',\n }\n}\n\nexport function settingsPatch(current: TitleSettings, patch: {\n prompt?: unknown\n model?: unknown\n cadence?: unknown\n}): Pick<TitleSettings, 'prompt' | 'model' | 'cadence'> & { locale: 'auto' } {\n return {\n locale: 'auto',\n prompt: patch.prompt === undefined ? current.prompt : normalizePrompt(patch.prompt),\n model: patch.model === undefined ? current.model : normalizeModelRoute(patch.model),\n cadence: patch.cadence === undefined ? current.cadence : normalizeCadence(patch.cadence),\n }\n}\n\nexport function modelRouteKey(route: TitleModelRoute): string {\n return route.provider && route.model ? `${route.provider}\\u001f${route.model}` : ''\n}\n","import type { RpcResult } from '@klarkxy/dsh-plugin-kit/contracts'\nimport { isSubagentSession } from '@klarkxy/dsh-plugin-kit/contracts'\nimport type { LlmTextCaller } from '@klarkxy/dsh-plugin-kit'\nimport type { GenerateConfig, TitleSettings, TitleSnapshot, TitleStatus, TitleSupport } from './contracts.ts'\nimport { PROVIDER_ID, defaultGenerateConfig } from './contracts.ts'\nimport { generateCurrentTitle } from './generate.ts'\nimport { collectHumanMessages, type SessionLike } from './messages.ts'\nimport {\n createNativeTitleSlot, restoreOwnDisplacement, type LoaderFace, type SessionTitleServiceLike,\n} from './native-slot.ts'\nimport { claimTitleSlot, releaseTitleSlot, type OwnershipClaim } from './ownership.ts'\nimport { settingsPatch, storedSettings } from './storage.ts'\nimport type { TitleLocaleMode } from './output.ts'\n\nexport interface TitleSettingsStore {\n load(): TitleSettings | undefined\n save(settings: TitleSettings): Promise<void>\n}\n\nexport interface CurrentTitleHost {\n get(name: string): unknown\n}\n\nexport class CurrentTitleService {\n private settings: TitleSettings\n private config: GenerateConfig\n private claim?: OwnershipClaim\n private nativeSlot?: ReturnType<typeof createNativeTitleSlot>\n private generation = 0\n private settingsPending: Promise<void> = Promise.resolve()\n private disposed = false\n private readonly inFlight = new Map<string, AbortController>()\n readonly support: TitleSupport\n\n constructor(private readonly options: {\n plugin: string\n llm?: LlmTextCaller\n host?: unknown\n sessionTitle?: SessionTitleServiceLike\n loader?: LoaderFace\n sessions?: { get(id: string): SessionLike | undefined }\n store?: TitleSettingsStore\n localePreference?: () => string | undefined\n initialLocale?: TitleLocaleMode\n now?: () => Date\n }) {\n const stored = storedSettings(options.store?.load(), options.initialLocale ?? 'auto')\n this.settings = stored\n this.config = defaultGenerateConfig(this.settings.locale)\n this.support = { nativeOwner: null, weOwn: false }\n }\n\n async start(): Promise<void> {\n const { llm, sessionTitle, loader, sessions } = this.options\n if (!llm || !sessionTitle?.register || !sessions || !loader) {\n this.support.weOwn = false\n this.support.limitation = 'required host services are missing'\n return\n }\n this.nativeSlot = createNativeTitleSlot({\n sessionTitle,\n loader,\n automatic: () => this.settings.cadence,\n generate: request => this.nativeGenerate(request),\n })\n try {\n this.claim = await claimTitleSlot(this.nativeSlot, PROVIDER_ID)\n this.support.weOwn = true\n this.support.nativeOwner = PROVIDER_ID\n delete this.support.limitation\n } catch (error) {\n this.support.weOwn = false\n this.support.nativeOwner = this.nativeSlot.owner() ?? null\n this.support.limitation = error instanceof Error ? error.message : 'native title provider slot is occupied'\n }\n }\n\n async dispose(): Promise<void> {\n this.disposed = true\n this.generation += 1\n for (const controller of this.inFlight.values()) controller.abort(new Error('current-title disabled'))\n this.inFlight.clear()\n await this.settingsPending\n const snapshot = this.nativeSlot?.displacement\n const outcome = await releaseTitleSlot(this.nativeSlot ?? { owner: () => undefined, occupy: async () => () => {} }, this.claim)\n if (outcome === 'released') await restoreOwnDisplacement(this.options.loader, snapshot)\n this.claim = undefined\n this.support.weOwn = false\n this.support.nativeOwner = this.nativeSlot?.owner() ?? null\n if (outcome === 'left-other-owner') {\n this.support.limitation = 'another title provider owns the slot; previous owner was not restored'\n }\n }\n\n status(sessionId?: string): TitleStatus {\n const session = sessionId && this.options.sessions ? this.options.sessions.get(sessionId) : undefined\n const snapshot = session ? this.read(session) : undefined\n return {\n settings: { ...this.settings, model: { ...this.settings.model } },\n support: { ...this.support, nativeOwner: this.nativeSlot?.owner() ?? this.support.nativeOwner },\n ...(sessionId ? {\n session: {\n sessionId,\n title: snapshot?.title,\n sourceKind: snapshot?.source.kind,\n generating: this.inFlight.has(sessionId),\n pinned: snapshot?.source.kind === 'user',\n },\n } : {}),\n }\n }\n\n async updateSettings(patch: {\n prompt?: unknown\n model?: unknown\n cadence?: unknown\n expectedRevision: number\n }): Promise<TitleSettings> {\n patch = structuredClone(patch)\n const result = this.settingsPending.then(async () => {\n if (this.disposed) throw Object.assign(new Error('current-title disabled'), { code: 'unavailable' })\n if (this.settings.revision !== patch.expectedRevision) {\n throw Object.assign(new Error('current-title settings changed'), { code: 'revision' })\n }\n const previous = this.settings\n const next: TitleSettings = {\n revision: this.settings.revision + 1,\n ...settingsPatch(this.settings, patch),\n }\n if (this.options.store) await this.options.store.save(next)\n this.settings = next\n this.config = { ...this.config, locale: 'auto' }\n if (!this.disposed && next.cadence !== previous.cadence) await this.reclaimWithCadence()\n return {\n revision: next.revision,\n locale: next.locale,\n prompt: next.prompt,\n model: { ...next.model },\n cadence: next.cadence,\n }\n })\n this.settingsPending = result.then(() => {}, () => {})\n return result\n }\n\n /** Re-register the provider so a cadence change takes effect without a restart. */\n private async reclaimWithCadence(): Promise<void> {\n if (!this.claim || !this.nativeSlot) return\n const stale = this.claim\n this.claim = undefined\n await stale.dispose()\n try {\n this.claim = await claimTitleSlot(this.nativeSlot, PROVIDER_ID)\n this.support.weOwn = true\n this.support.nativeOwner = PROVIDER_ID\n delete this.support.limitation\n } catch (error) {\n this.support.weOwn = false\n this.support.nativeOwner = this.nativeSlot.owner() ?? null\n this.support.limitation = error instanceof Error ? error.message : 'native title provider slot is occupied'\n }\n }\n\n /** @deprecated Locale is always auto. Kept so older clients still save a revision. */\n async updateLocale(_locale: TitleLocaleMode, expectedRevision: number): Promise<TitleSettings> {\n return this.updateSettings({ expectedRevision })\n }\n\n async regenerate(sessionId: string, signal: AbortSignal): Promise<TitleSnapshot | undefined> {\n if (!this.support.weOwn) throw Object.assign(new Error('title provider is inactive'), { code: 'unavailable' })\n const session = this.options.sessions?.get(sessionId)\n if (!session) throw Object.assign(new Error('session not found'), { code: 'not-found' })\n await this.options.sessionTitle?.refresh(session, signal)\n return this.read(session)\n }\n\n read(session: SessionLike): TitleSnapshot | undefined {\n const snapshot = this.options.sessionTitle?.get(session)\n if (!snapshot?.title) return undefined\n const kind = snapshot.source?.kind\n return {\n title: snapshot.title,\n messageSeqs: snapshot.messageSeqs ? [...snapshot.messageSeqs] : [],\n source: {\n kind: kind === 'user' || kind === 'fallback' || kind === 'provider' ? kind : 'provider',\n ...(typeof snapshot.source?.provider === 'string' ? { provider: snapshot.source.provider } : {}),\n },\n eventSeq: Number(snapshot.eventSeq ?? 0),\n updatedAt: snapshot.updatedAt ?? 0,\n }\n }\n\n private async nativeGenerate(request: {\n session: { id: string; snapshotEvents(): readonly unknown[] }\n messages: ReadonlyArray<{ seq: number; text: string }>\n signal: AbortSignal\n }) {\n const llm = this.options.llm\n if (!llm) throw new Error('dsh-current-title: llm is not available')\n const generation = this.generation\n const controller = new AbortController()\n this.inFlight.get(request.session.id)?.abort(new Error('newer title generation superseded older work'))\n this.inFlight.set(request.session.id, controller)\n try {\n const combined = AbortSignal.any([request.signal, controller.signal])\n const session = this.options.sessions?.get(request.session.id)\n // The host writes a free deterministic fallback title before it consults a\n // provider, and this provider contract has no \"decline\" result. Declining is\n // therefore the only way to keep delegated child threads off the model: their\n // dispatch prompt arrives as the child's own user message, so `source.kind`\n // cannot exclude them. The host logs the rejection and keeps the fallback.\n if (isSubagentSession(session)) {\n throw Object.assign(new Error('dsh-current-title: delegated child thread keeps the host fallback title'), { code: 'subagent-session' })\n }\n const messages = collectHumanMessages(\n request.messages.map(message => ({\n type: 'user/message',\n seq: message.seq,\n data: { source: { kind: 'user' }, content: [{ type: 'text', text: message.text }] },\n })),\n )\n const generated = await generateCurrentTitle({\n llm,\n host: this.options.host ?? {},\n config: this.config,\n sessionId: request.session.id,\n messages: messages.length > 0 ? messages : [...request.messages],\n prompt: this.settings.prompt,\n model: this.settings.model,\n localePreference: this.options.localePreference?.(),\n signal: combined,\n isCurrent: () => this.generation === generation && this.support.weOwn\n && (!session || this.options.sessions?.get(request.session.id) === session),\n now: this.options.now?.(),\n })\n return { title: generated.title, messageSeqs: generated.messageSeqs, model: generated.model }\n } finally {\n if (this.inFlight.get(request.session.id) === controller) this.inFlight.delete(request.session.id)\n }\n }\n}\n\nexport function hostLocalePreference(host: CurrentTitleHost): string | undefined {\n try {\n const settings = host.get('settings') as { get?(namespace: string): unknown } | undefined\n const value = settings?.get?.('locale')\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined\n const preference = (value as Record<string, unknown>).preference\n return typeof preference === 'string' ? preference : undefined\n } catch {\n return undefined\n }\n}\n\nexport async function handleRpc(service: CurrentTitleService, endpoint: string, payload: unknown, signal: AbortSignal): Promise<RpcResult> {\n signal.throwIfAborted()\n const body = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload as Record<string, unknown> : {}\n if (endpoint === 'status') {\n return { ok: true, value: service.status(typeof body.sessionId === 'string' ? body.sessionId : undefined) }\n }\n if (endpoint === 'settings') {\n if (typeof body.expectedRevision !== 'number') {\n return { ok: false, error: { code: 'bad-request', message: '标题设置无效。' } }\n }\n try {\n return {\n ok: true,\n value: await service.updateSettings({\n ...(body.prompt !== undefined ? { prompt: body.prompt } : {}),\n ...(body.model !== undefined ? { model: body.model } : {}),\n ...(body.cadence !== undefined ? { cadence: body.cadence } : {}),\n expectedRevision: body.expectedRevision,\n }),\n }\n } catch (error) {\n const code = error instanceof Error && 'code' in error ? String((error as { code?: string }).code) : 'failed'\n if (code === 'revision') return { ok: false, error: { code, message: '设置已更新,请刷新后重试。' } }\n return { ok: false, error: { code: 'failed', message: '无法保存标题设置。' } }\n }\n }\n if (endpoint === 'regenerate') {\n if (typeof body.sessionId !== 'string' || !body.sessionId) {\n return { ok: false, error: { code: 'bad-request', message: '缺少会话。' } }\n }\n try {\n const snapshot = await service.regenerate(body.sessionId, signal)\n return { ok: true, value: snapshot ?? null }\n } catch (error) {\n const code = error instanceof Error && 'code' in error ? String((error as { code?: string }).code) : 'failed'\n if (code === 'not-found') return { ok: false, error: { code, message: '找不到该会话。' } }\n if (code === 'unavailable') return { ok: false, error: { code, message: '标题服务未就绪。' } }\n if (signal.aborted) return { ok: false, error: { code: 'cancelled', message: '已取消。' } }\n return { ok: false, error: { code: 'failed', message: '标题生成失败。' } }\n }\n }\n return { ok: false, error: { code: 'bad-request', message: '未知操作。' } }\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport type { LlmTextCaller } from '@klarkxy/dsh-plugin-kit'\nimport { registerHostRpc, type HostRpcContext } from '@klarkxy/dsh-plugin-kit/host-rpc'\nimport { isTitleLocaleMode, PLUGIN_NAME, RPC_CHANNEL, SETTINGS_KEY, type TitleLocaleMode } from './contracts.ts'\nimport { CurrentTitleService, handleRpc, hostLocalePreference } from './service.ts'\nimport type { LoaderFace, SessionTitleServiceLike } from './native-slot.ts'\nimport type { SessionLike } from './messages.ts'\nimport { parseTitleSettings, titleDomain } from './storage.ts'\n\nexport const name = PLUGIN_NAME\nexport const inject = ['sessionTitle', 'llm', 'sessions', 'loader', 'storageDomain', 'connection', 'webServer'] as const\nexport { CurrentTitleService }\nexport { selectRecentMessages, frameMessages } from './input.ts'\nexport { parseModelTitle, formatTitle, resolveTitleLocale } from './output.ts'\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context { currentTitle: CurrentTitleService }\n}\n\ntype DomainHandle = {\n table(name: 'settings'): {\n get(key: string): unknown\n put(key: string, value: unknown): Promise<void>\n }\n close(): Promise<void>\n}\n\ntype Host = Context & HostRpcContext & {\n sessionTitle: SessionTitleServiceLike\n llm: LlmTextCaller\n sessions: { get(id: string): SessionLike | undefined }\n loader: LoaderFace\n storageDomain: { open: (spec: typeof titleDomain) => Promise<DomainHandle> }\n}\n\nexport async function apply(ctx: Context, config: { locale?: TitleLocaleMode } = {}): Promise<void> {\n const host = ctx as Host\n const domain = await host.storageDomain.open(titleDomain)\n const table = domain.table('settings')\n const locale = isTitleLocaleMode(config.locale) ? config.locale : 'auto'\n const service = new CurrentTitleService({\n plugin: PLUGIN_NAME,\n llm: host.llm,\n host,\n sessionTitle: host.sessionTitle,\n loader: host.loader,\n sessions: host.sessions,\n store: {\n load: () => parseTitleSettings(table.get(SETTINGS_KEY)),\n save: settings => table.put(SETTINGS_KEY, settings),\n },\n initialLocale: locale,\n localePreference: () => hostLocalePreference(host),\n })\n await service.start()\n ctx.provide('currentTitle', service)\n ctx.effect(() => async () => { await service.dispose(); await domain.close() }, 'current-title.lifecycle')\n ctx.effect(() => registerHostRpc(host, RPC_CHANNEL, (endpoint, payload, signal) => handleRpc(service, endpoint, payload, signal)), 'current-title.rpc')\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/input.ts","../src/output.ts","../src/messages.ts","../src/generate.ts","../src/ownership.ts","../src/native-slot.ts","../src/storage.ts","../src/service.ts","../src/index.ts"],"sourcesContent":["import type { TitleMessage } from './contracts.ts'\n\nexport const FRAME_PREFIX =\n 'Choose the current task from these recent human messages, ordered oldest to newest:\\n'\n\nexport function frameMessages(messages: readonly TitleMessage[]): string {\n return `${FRAME_PREFIX}${JSON.stringify(messages)}`\n}\n\nexport function utf8Bytes(value: string): number {\n return Buffer.byteLength(value, 'utf8')\n}\n\nfunction middleSlice(codePoints: readonly string[], kept: number): string {\n if (kept >= codePoints.length) return codePoints.join('')\n if (kept <= 0) return ''\n if (kept === 1) return '…'\n\n const contentPoints = kept - 1\n const headCount = Math.ceil(contentPoints / 2)\n const tailCount = Math.floor(contentPoints / 2)\n return `${codePoints.slice(0, headCount).join('')}…${\n tailCount === 0 ? '' : codePoints.slice(-tailCount).join('')\n }`\n}\n\nexport function fitSingleMessage(message: TitleMessage, maxInputBytes: number): TitleMessage {\n const empty = { ...message, text: '' }\n if (utf8Bytes(frameMessages([empty])) > maxInputBytes) {\n throw new Error('dsh-current-title: maxInputBytes is too small for the input envelope')\n }\n if (utf8Bytes(frameMessages([message])) <= maxInputBytes) return message\n\n const points = Array.from(message.text)\n let low = 0\n let high = points.length\n let best = ''\n\n while (low <= high) {\n const kept = Math.floor((low + high) / 2)\n const text = middleSlice(points, kept)\n if (utf8Bytes(frameMessages([{ ...message, text }])) <= maxInputBytes) {\n best = text\n low = kept + 1\n } else {\n high = kept - 1\n }\n }\n\n return { ...message, text: best }\n}\n\nexport function selectRecentMessages(\n messages: readonly TitleMessage[],\n maxRecentMessages: number,\n maxInputBytes: number,\n): TitleMessage[] {\n if (messages.length === 0) return []\n\n const selected = messages.slice(-maxRecentMessages).map(message => ({ ...message }))\n while (selected.length > 1 && utf8Bytes(frameMessages(selected)) > maxInputBytes) {\n selected.shift()\n }\n\n if (utf8Bytes(frameMessages(selected)) > maxInputBytes) {\n selected[0] = fitSingleMessage(selected[0]!, maxInputBytes)\n }\n\n return selected\n}\n","export const TITLE_TYPE_KEYS = [\n 'feature',\n 'fix',\n 'optimize',\n 'refactor',\n 'test',\n 'docs',\n 'release',\n 'config',\n 'explore',\n 'discuss',\n] as const\n\nexport const TITLE_LOCALES = ['zh', 'en'] as const\nexport const TITLE_LOCALE_MODES = ['auto', ...TITLE_LOCALES] as const\n\nexport type TitleType = (typeof TITLE_TYPE_KEYS)[number]\nexport type TitleLocale = (typeof TITLE_LOCALES)[number]\nexport type TitleLocaleMode = (typeof TITLE_LOCALE_MODES)[number]\n\nconst TITLE_TYPE_LABELS: Record<TitleLocale, Record<TitleType, string>> = {\n zh: {\n feature: '功能',\n fix: '修复',\n optimize: '优化',\n refactor: '重构',\n test: '测试',\n docs: '文档',\n release: '发布',\n config: '配置',\n explore: '探索',\n discuss: '讨论',\n },\n en: {\n feature: 'Feature',\n fix: 'Fix',\n optimize: 'Optimize',\n refactor: 'Refactor',\n test: 'Test',\n docs: 'Docs',\n release: 'Release',\n config: 'Config',\n explore: 'Explore',\n discuss: 'Discussion',\n },\n}\n\nexport interface ParsedTitle {\n type: TitleType\n summary: string\n}\n\nconst OSC_SEQUENCE = /(?:\\u001B\\]|\\u009D)(?:(?!\\u0007|\\u001B\\\\)[\\s\\S])*(?:\\u0007|\\u001B\\\\|$)/gu\nconst CSI_SEQUENCE = /(?:\\u001B\\[|\\u009B)[0-?]*[ -/]*[@-~]/gu\nconst ESC_SEQUENCE = /\\u001B[@-_]/gu\nconst CONTROL_CHARACTER = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u009F]/gu\nconst DIRECTIONAL_CONTROL = /[\\u200B\\u200E\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u206F\\uFEFF]/gu\n\nfunction cleanTitleText(input: string): string {\n return input\n .replace(OSC_SEQUENCE, '')\n .replace(CSI_SEQUENCE, '')\n .replace(ESC_SEQUENCE, '')\n .replace(CONTROL_CHARACTER, '')\n .replace(DIRECTIONAL_CONTROL, '')\n .replace(/\\s+/gu, ' ')\n .trim()\n}\n\nexport function truncateTitleUtf8(input: string, maxBytes: number): string {\n if (!Number.isInteger(maxBytes) || maxBytes <= 0) throw new Error('maxBytes must be a positive integer')\n if (Buffer.byteLength(input, 'utf8') <= maxBytes) return input\n let used = 0\n let output = ''\n for (const character of input) {\n const bytes = Buffer.byteLength(character, 'utf8')\n if (used + bytes > maxBytes) break\n output += character\n used += bytes\n }\n return output\n}\n\n/** Same sanitizing rules as DSH 0.1.7-rc.2 `normalizeSessionTitle`. */\nexport function normalizeSessionTitle(input: string, maxBytes: number): string {\n return truncateTitleUtf8(cleanTitleText(input), maxBytes).trimEnd()\n}\n\nfunction isTitleType(value: string): value is TitleType {\n return (TITLE_TYPE_KEYS as readonly string[]).includes(value)\n}\n\nexport function resolveTitleLocale(\n mode: TitleLocaleMode,\n messages: readonly string[],\n preference?: string,\n): TitleLocale {\n if (mode !== 'auto') return mode\n\n const preferred = preference?.trim().toLowerCase().split('-')[0]\n if (preferred === 'zh' || preferred === 'en') return preferred\n\n const text = messages.join('\\n')\n if (/[\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]/u.test(text)) return 'en'\n return /\\p{Script=Han}/u.test(text) ? 'zh' : 'en'\n}\n\nfunction removeTitleSyntax(value: string): string {\n return value.replace(/[|`*_~#\\[\\]\"“”]/gu, ' ')\n}\n\nfunction truncateSummary(value: string, targetWords: number, targetCjkCharacters: number): string {\n const containsCjk = /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]/u.test(value)\n if (containsCjk) {\n return Array.from(value.replace(/\\s+/gu, '')).slice(0, targetCjkCharacters).join('')\n }\n return value.split(/\\s+/u).filter(Boolean).slice(0, targetWords).join(' ')\n}\n\nfunction unwrapModelText(raw: string): string {\n const trimmed = raw.trim()\n const fenced = /^```(?:json)?\\s*([\\s\\S]*?)\\s*```$/iu.exec(trimmed)\n return (fenced?.[1] ?? trimmed).trim()\n}\n\nfunction readJsonObject(raw: string): Record<string, unknown> | undefined {\n const text = unwrapModelText(raw)\n const start = text.indexOf('{')\n const end = text.lastIndexOf('}')\n if (start === -1 || end <= start) return undefined\n try {\n const value = JSON.parse(text.slice(start, end + 1))\n if (value && typeof value === 'object' && !Array.isArray(value)) return value as Record<string, unknown>\n } catch {\n return undefined\n }\n return undefined\n}\n\n/**\n * Accept the required JSON shape, including a code fence or a short preface.\n * Extra keys are ignored. A plain one-line title still becomes a title so a\n * chatty model does not leave the session on the built-in fallback.\n */\nexport function parseModelTitle(\n raw: string,\n targetWords: number,\n targetCjkCharacters: number,\n): ParsedTitle {\n const candidate = readJsonObject(raw)\n const typeValue = typeof candidate?.type === 'string' ? candidate.type.trim().toLowerCase() : ''\n const summaryValue = typeof candidate?.summary === 'string' ? candidate.summary : ''\n const type = isTitleType(typeValue) ? typeValue : 'discuss'\n const source = summaryValue || (candidate ? '' : unwrapModelText(raw).replace(/^[\"'`]+|[\"'`]+$/gu, ''))\n const normalized = normalizeSessionTitle(removeTitleSyntax(source), Number.MAX_SAFE_INTEGER)\n const summary = truncateSummary(normalized, targetWords, targetCjkCharacters)\n if (summary.length === 0) {\n throw new Error('dsh-current-title: title model returned an empty summary')\n }\n return { type, summary }\n}\n\nexport function localMonthDay(now: Date): string {\n const month = String(now.getMonth() + 1).padStart(2, '0')\n const day = String(now.getDate()).padStart(2, '0')\n return `${month}${day}`\n}\n\nexport function formatTitle(parsed: ParsedTitle, locale: TitleLocale, now = new Date()): string {\n return `${localMonthDay(now)} | ${TITLE_TYPE_LABELS[locale][parsed.type]} | ${parsed.summary}`\n}\n","import { normalizeSessionTitle } from './output.ts'\nimport type { TitleMessage, TitleSnapshot, TitleSourceKind } from './contracts.ts'\n\nexport interface SessionEventLike {\n readonly type: string\n readonly seq: number\n readonly time?: number\n readonly data?: {\n readonly source?: { readonly kind?: string; readonly plugin?: string; readonly provider?: string }\n readonly content?: ReadonlyArray<{ readonly type?: string; readonly text?: string }>\n readonly title?: string\n readonly messageSeqs?: readonly number[]\n }\n}\n\nexport interface SessionLike {\n readonly id: string\n readonly seq?: number\n snapshotEvents(): readonly SessionEventLike[]\n append?(type: string, data: unknown): unknown\n}\n\nfunction textFromContent(content: ReadonlyArray<{ readonly type?: string; readonly text?: string }> | undefined): string {\n if (!Array.isArray(content)) return ''\n return content\n .filter(block => block && block.type === 'text' && typeof block.text === 'string')\n .map(block => block.text as string)\n .join('\\n')\n}\n\n/** Eligible human prompts only. Plugin/tool/model user-role rows are excluded. */\nexport function collectHumanMessages(events: readonly SessionEventLike[], throughSeq?: number): TitleMessage[] {\n const messages: TitleMessage[] = []\n for (const event of events) {\n if (throughSeq !== undefined && event.seq > throughSeq) break\n if (event.type !== 'user/message') continue\n if (event.data?.source?.kind !== 'user') continue\n const text = textFromContent(event.data.content)\n if (normalizeSessionTitle(text, Number.MAX_SAFE_INTEGER).length === 0) continue\n messages.push({ seq: event.seq, text })\n }\n return messages\n}\n\nexport function foldTitle(events: readonly SessionEventLike[]): TitleSnapshot | undefined {\n let latest: SessionEventLike | undefined\n for (const event of events) {\n if (event.type === 'session/title') latest = event\n }\n if (!latest?.data || typeof latest.data.title !== 'string') return undefined\n const kind = latest.data.source?.kind\n const sourceKind: TitleSourceKind = kind === 'user' || kind === 'fallback' || kind === 'provider' ? kind : 'provider'\n return {\n title: latest.data.title,\n messageSeqs: latest.data.messageSeqs ? [...latest.data.messageSeqs] : [],\n source: { kind: sourceKind, ...(typeof latest.data.source?.provider === 'string' ? { provider: latest.data.source.provider } : {}) },\n eventSeq: latest.seq,\n updatedAt: latest.time ?? 0,\n }\n}\n\nexport function userRenameNewerThan(events: readonly SessionEventLike[], watermarkSeq: number): boolean {\n const current = foldTitle(events)\n return Boolean(current && current.source.kind === 'user' && current.eventSeq > watermarkSeq)\n}\n\nexport function sourceVersionOf(sessionId: string, messages: readonly TitleMessage[]): string {\n const last = messages.at(-1)\n return `${sessionId}:${last?.seq ?? -1}:${messages.length}`\n}\n","import { callLlmText, resolveFeatureModel, type LlmTextCaller } from '@klarkxy/dsh-plugin-kit'\nimport type { ModelRoute } from '@klarkxy/dsh-model-route'\nimport type { GenerateConfig, TitleMessage, TitleModelRoute } from './contracts.ts'\nimport { DEFAULT_TIMEOUT_MS, PLUGIN_NAME } from './contracts.ts'\nimport { frameMessages, selectRecentMessages } from './input.ts'\nimport { formatTitle, normalizeSessionTitle, parseModelTitle, resolveTitleLocale, type TitleLocaleMode } from './output.ts'\nimport { sourceVersionOf } from './messages.ts'\n\nexport interface GeneratedTitle {\n readonly title: string\n readonly messageSeqs: readonly number[]\n readonly model?: { readonly provider: string; readonly model: string }\n readonly sourceVersion: string\n}\n\nexport function systemPrompt(config: GenerateConfig, customPrompt = ''): string {\n const instruction = customPrompt.trim()\n return [\n instruction || 'Name the current task in an AI coding-assistant session.',\n 'Prioritize the newest human messages. Older messages are context only and must not keep a completed task in the title.',\n 'Allowed type values: feature, fix, optimize, refactor, test, docs, release, config, explore, discuss.',\n 'Return exactly one JSON object: {\"type\":\"<type>\",\"summary\":\"<summary>\"}.',\n 'Return no Markdown, code fence, explanation, date, or extra key.',\n 'Keep the summary in the language of the recent messages.',\n `Aim for at most ${config.targetWords} non-CJK words or ${config.targetCjkCharacters} CJK characters.`,\n ].join('\\n')\n}\n\n/**\n * Saved override passed to resolveFeatureModel. Unlike modelRouteOverride,\n * this does not trim or re-apply the 250/80 caps: stored rows are already\n * normalized, and a whitespace-only id must stay a miss (not a trimmed hit)\n * so the host-default fallback is unchanged.\n */\nexport function titleModelRoute(route: TitleModelRoute | undefined): ModelRoute | undefined {\n if (!route?.provider || !route.model) return undefined\n return route.reasoningEffort\n ? { provider: route.provider, model: route.model, reasoningEffort: route.reasoningEffort }\n : { provider: route.provider, model: route.model }\n}\n\nexport async function generateCurrentTitle(options: {\n llm: LlmTextCaller\n host: unknown\n config: GenerateConfig\n sessionId: string\n messages: readonly TitleMessage[]\n prompt?: string\n model?: TitleModelRoute\n localePreference?: string\n localeMode?: TitleLocaleMode\n signal: AbortSignal\n isCurrent: () => boolean\n now?: Date\n}): Promise<GeneratedTitle> {\n options.signal.throwIfAborted()\n if (!options.isCurrent()) {\n throw Object.assign(new Error('dsh-current-title: generation is no longer current'), { code: 'superseded' })\n }\n const selected = selectRecentMessages(\n options.messages,\n options.config.maxRecentMessages,\n options.config.maxInputBytes,\n )\n if (selected.length === 0) throw new Error('dsh-current-title: at least one source message is required')\n const route = resolveFeatureModel(options.host, titleModelRoute(options.model), options.sessionId)\n if (!route) throw new Error('dsh-current-title: 请在插件页选择模型,或设置宿主默认对话模型。')\n // Titles never think: reasoning effort is never forwarded (matching the\n // bundled dsh-session-title-llm policy), so a thinking model cannot burn the\n // small token cap on hidden reasoning and leave an empty reply.\n const callRoute = { provider: route.provider, model: route.model }\n\n const framedInput = frameMessages(selected)\n const locale = resolveTitleLocale(\n options.localeMode ?? options.config.locale,\n selected.map(message => message.text),\n options.localePreference,\n )\n const sourceVersion = sourceVersionOf(options.sessionId, selected)\n const deadline = new AbortController()\n const timeoutMs = Number.isFinite(options.config.timeoutMs) && options.config.timeoutMs > 0\n ? options.config.timeoutMs : DEFAULT_TIMEOUT_MS\n const timer = setTimeout(() => deadline.abort(new DOMException('Title generation timed out.', 'TimeoutError')), timeoutMs)\n const signal = AbortSignal.any([options.signal, deadline.signal])\n let abort = () => {}\n const cancelled = new Promise<never>((_resolve, reject) => {\n abort = () => reject(signal.reason)\n if (signal.aborted) abort()\n else signal.addEventListener('abort', abort, { once: true })\n })\n let result\n try {\n result = await Promise.race([callLlmText(options.llm, {\n plugin: PLUGIN_NAME,\n route: callRoute,\n system: systemPrompt(options.config, options.prompt),\n text: framedInput,\n maxTokens: options.config.maxOutputTokens,\n purpose: 'session-title',\n signal,\n sessionId: options.sessionId,\n isCurrent: options.isCurrent,\n }), cancelled])\n signal.throwIfAborted()\n } catch (error) {\n // The shared caller translates stream cancellation to AbortError. Retain\n // the originating deadline or lifecycle reason at this feature boundary.\n signal.throwIfAborted()\n throw error\n } finally {\n clearTimeout(timer)\n signal.removeEventListener('abort', abort)\n }\n options.signal.throwIfAborted()\n if (!options.isCurrent()) {\n throw Object.assign(new Error('dsh-current-title: generation is no longer current'), { code: 'superseded' })\n }\n const parsed = parseModelTitle(result.text, options.config.targetWords, options.config.targetCjkCharacters)\n const title = normalizeSessionTitle(formatTitle(parsed, locale, options.now ?? new Date()), options.config.maxTitleBytes)\n if (!title) throw new Error('dsh-current-title: title model returned an empty summary')\n return {\n title,\n messageSeqs: selected.map(message => message.seq),\n model: { provider: result.provider, model: result.model },\n sourceVersion,\n }\n}\n","export interface OwnershipClaim {\n readonly id: string\n readonly dispose: () => void | Promise<void>\n}\n\nexport interface TitleSlot {\n owner(): string | undefined\n occupy(id: string): Promise<() => void | Promise<void>>\n}\n\n/** Exclusive title-provider ownership. Enable claims the slot; disable releases only while this id still holds it. */\nexport async function claimTitleSlot(slot: TitleSlot, id: string): Promise<OwnershipClaim> {\n const dispose = await slot.occupy(id)\n return { id, dispose }\n}\n\nexport async function releaseTitleSlot(\n slot: TitleSlot,\n claim: OwnershipClaim | undefined,\n): Promise<'released' | 'left-other-owner' | 'idle'> {\n if (!claim) return 'idle'\n if (slot.owner() !== claim.id) return 'left-other-owner'\n await claim.dispose()\n if (slot.owner() && slot.owner() !== claim.id) return 'left-other-owner'\n return 'released'\n}\n\nexport function occupiedProviderId(error: unknown): string | undefined {\n const message = error instanceof Error ? error.message : String(error)\n return /session-title provider \"([^\"]+)\" is already registered/.exec(message)?.[1]\n}\n","import { PROVIDER_ID } from './contracts.ts'\nimport { occupiedProviderId, type TitleSlot } from './ownership.ts'\n\nexport interface SessionTitleProvider {\n readonly id: string\n readonly automatic: 'first-prompt' | 'all-prompts'\n generate(request: {\n session: { id: string; snapshotEvents(): readonly unknown[] }\n messages: ReadonlyArray<{ seq: number; text: string }>\n route?: { provider: string; model: string }\n signal: AbortSignal\n }): Promise<{ title: string; messageSeqs: readonly number[]; model?: { provider: string; model: string } }>\n}\n\nexport interface SessionTitleServiceLike {\n register(provider: SessionTitleProvider): () => void | Promise<void>\n get(session: unknown): { title: string; source?: { kind?: string; provider?: string }; eventSeq?: number; updatedAt?: number; messageSeqs?: readonly number[] } | undefined\n refresh(session: unknown, signal?: AbortSignal): Promise<unknown>\n}\n\nexport interface LoaderEntryOptions {\n id?: string\n name?: string\n disabled?: boolean | null\n config?: unknown\n inject?: unknown\n group?: boolean | null\n}\n\nexport interface LoaderEntry {\n id: string\n options?: LoaderEntryOptions\n}\n\nexport interface LoaderFace {\n entries(): Iterable<LoaderEntry>\n update(id: string, options: { disabled?: boolean | null }): Promise<unknown> | unknown\n}\n\nexport interface Displacement {\n readonly id: string\n readonly name: string\n readonly identity: string\n}\n\nfunction catalogLookupId(entryId: string): string {\n const colon = entryId.indexOf(':')\n return colon === -1 ? entryId : entryId.slice(colon + 1)\n}\n\nfunction entryName(entry: LoaderEntry): string {\n return String(entry.options?.name ?? '')\n}\n\nexport function entryDisabled(entry: LoaderEntry): boolean {\n return entry.options?.disabled === true\n}\n\nexport function displacementIdentity(entry: LoaderEntry): string {\n const options = entry.options ?? {}\n const { disabled: _disabled, ...rest } = options\n return JSON.stringify({ id: entry.id, name: entryName(entry), rest })\n}\n\nexport function captureDisplacement(entry: LoaderEntry): Displacement {\n return { id: entry.id, name: entryName(entry), identity: displacementIdentity(entry) }\n}\n\nexport function displacementUnchanged(snapshot: Displacement, entry: LoaderEntry): boolean {\n return entry.id === snapshot.id\n && entryName(entry) === snapshot.name\n && displacementIdentity(entry) === snapshot.identity\n}\n\nexport function findEntryById(loader: LoaderFace, id: string): LoaderEntry | undefined {\n for (const entry of loader.entries()) {\n if (entry.id === id) return entry\n }\n return undefined\n}\n\nexport function findLoaderEntry(loader: LoaderFace, providerId: string): LoaderEntry | undefined {\n for (const entry of loader.entries()) {\n const name = entryName(entry)\n const ids = [entry.id, entry.options?.id, name, catalogLookupId(entry.id)]\n if (ids.some(value => value === providerId)) return entry\n if (name.endsWith(`/${providerId}`) || name.endsWith(providerId)) return entry\n if (providerId === 'session-title-first-prompt-llm' && catalogLookupId(entry.id) === 'session-title-llm') return entry\n }\n return undefined\n}\n\nexport async function restoreOwnDisplacement(loader: LoaderFace | undefined, snapshot: Displacement | undefined): Promise<'restored' | 'skipped'> {\n if (!loader || !snapshot) return 'skipped'\n const current = findEntryById(loader, snapshot.id)\n if (!current || !entryDisabled(current) || !displacementUnchanged(snapshot, current)) return 'skipped'\n await loader.update(snapshot.id, { disabled: false })\n return 'restored'\n}\n\nexport function createNativeTitleSlot(options: {\n sessionTitle: SessionTitleServiceLike\n loader: LoaderFace\n /** Read at each occupy so a settings change can re-register with a new cadence. */\n automatic: () => SessionTitleProvider['automatic']\n generate: SessionTitleProvider['generate']\n}): TitleSlot & { displacement?: Displacement } {\n let owner: string | undefined\n const slot: TitleSlot & { displacement?: Displacement } = {\n owner: () => owner,\n async occupy(id: string) {\n const provider: SessionTitleProvider = {\n id: PROVIDER_ID,\n automatic: options.automatic(),\n generate: options.generate,\n }\n try {\n const dispose = options.sessionTitle.register(provider)\n owner = id\n return async () => {\n await dispose()\n if (owner === id) owner = undefined\n }\n } catch (error) {\n const occupied = occupiedProviderId(error)\n if (!occupied) throw error\n const entry = findLoaderEntry(options.loader, occupied)\n if (!entry || entryDisabled(entry)) throw error\n const snapshot = captureDisplacement(entry)\n await options.loader.update(entry.id, { disabled: true })\n const afterDisable = findEntryById(options.loader, entry.id)\n if (!afterDisable || !displacementUnchanged(snapshot, afterDisable)) throw error\n try {\n const dispose = options.sessionTitle.register(provider)\n slot.displacement = snapshot\n owner = id\n return async () => {\n await dispose()\n if (owner === id) owner = undefined\n }\n } catch (second) {\n if (findEntryById(options.loader, snapshot.id) && displacementUnchanged(snapshot, findEntryById(options.loader, snapshot.id)!)) {\n await options.loader.update(snapshot.id, { disabled: false })\n }\n throw second\n }\n }\n },\n }\n return slot\n}\n","import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain'\nimport { modelRouteKey as sharedModelRouteKey } from '@klarkxy/dsh-model-route'\nimport { z } from 'zod'\nimport {\n SETTINGS_KEY, defaultModelRoute, isTitleLocaleMode, normalizeCadence, normalizeModelRoute, normalizePrompt,\n type TitleModelRoute, type TitleSettings,\n} from './contracts.ts'\n\nexport { SETTINGS_KEY }\n\nconst localeSchema = z.enum(['auto', 'zh', 'en'])\n\n/** Version 1 stored only the locale. Prompt and model are filled as defaults. */\nconst legacySettingsSchema = z.object({\n revision: z.number().int().nonnegative(),\n locale: localeSchema,\n}).strict()\n\nconst modelRouteSchema = z.object({\n provider: z.string(),\n model: z.string(),\n reasoningEffort: z.string().optional(),\n}).strict()\n\nexport const titleSettingsSchema = z.object({\n revision: z.number().int().nonnegative(),\n locale: localeSchema,\n prompt: z.string(),\n model: modelRouteSchema,\n /** Defaulted so rows stored before the cadence switch existed still parse. */\n cadence: z.enum(['all-prompts', 'first-prompt']).default('all-prompts'),\n}).strict()\n\nexport const titleDomain = defineDomain({\n name: 'dsh_editor_current_title',\n version: 2,\n tables: {\n settings: domainTable<string, TitleSettings>(titleSettingsSchema),\n },\n})\n\nexport function parseTitleSettings(value: unknown): TitleSettings | undefined {\n const current = titleSettingsSchema.safeParse(value)\n if (current.success) {\n return {\n revision: current.data.revision,\n locale: isTitleLocaleMode(current.data.locale) ? current.data.locale : 'auto',\n prompt: normalizePrompt(current.data.prompt),\n model: normalizeModelRoute(current.data.model),\n cadence: normalizeCadence(current.data.cadence),\n }\n }\n const legacy = legacySettingsSchema.safeParse(value)\n if (!legacy.success || !isTitleLocaleMode(legacy.data.locale)) return undefined\n return {\n revision: legacy.data.revision,\n locale: legacy.data.locale,\n prompt: '',\n model: defaultModelRoute(),\n cadence: 'all-prompts',\n }\n}\n\nexport function storedSettings(value: unknown, _fallbackLocale: TitleSettings['locale'] = 'auto'): TitleSettings {\n const parsed = parseTitleSettings(value)\n return {\n revision: parsed?.revision ?? 0,\n locale: 'auto',\n prompt: parsed?.prompt ?? '',\n model: parsed?.model ?? defaultModelRoute(),\n cadence: parsed?.cadence ?? 'all-prompts',\n }\n}\n\nexport function settingsPatch(current: TitleSettings, patch: {\n prompt?: unknown\n model?: unknown\n cadence?: unknown\n}): Pick<TitleSettings, 'prompt' | 'model' | 'cadence'> & { locale: 'auto' } {\n return {\n locale: 'auto',\n prompt: patch.prompt === undefined ? current.prompt : normalizePrompt(patch.prompt),\n model: patch.model === undefined ? current.model : normalizeModelRoute(patch.model),\n cadence: patch.cadence === undefined ? current.cadence : normalizeCadence(patch.cadence),\n }\n}\n\n/** Same separator and empty-id rule as @klarkxy/dsh-model-route. No in-tree callers. */\nexport function modelRouteKey(route: TitleModelRoute): string {\n return sharedModelRouteKey(route.provider, route.model)\n}\n","import type { RpcResult } from '@klarkxy/dsh-plugin-kit/contracts'\nimport { isSubagentSession } from '@klarkxy/dsh-plugin-kit/contracts'\nimport type { LlmTextCaller } from '@klarkxy/dsh-plugin-kit'\nimport type { GenerateConfig, TitleSettings, TitleSnapshot, TitleStatus, TitleSupport } from './contracts.ts'\nimport { PROVIDER_ID, defaultGenerateConfig } from './contracts.ts'\nimport { generateCurrentTitle } from './generate.ts'\nimport { collectHumanMessages, type SessionLike } from './messages.ts'\nimport {\n createNativeTitleSlot, restoreOwnDisplacement, type LoaderFace, type SessionTitleServiceLike,\n} from './native-slot.ts'\nimport { claimTitleSlot, releaseTitleSlot, type OwnershipClaim } from './ownership.ts'\nimport { settingsPatch, storedSettings } from './storage.ts'\nimport type { TitleLocaleMode } from './output.ts'\n\nexport interface TitleSettingsStore {\n load(): TitleSettings | undefined\n save(settings: TitleSettings): Promise<void>\n}\n\nexport interface CurrentTitleHost {\n get(name: string): unknown\n}\n\nexport class CurrentTitleService {\n private settings: TitleSettings\n private config: GenerateConfig\n private claim?: OwnershipClaim\n private nativeSlot?: ReturnType<typeof createNativeTitleSlot>\n private generation = 0\n private settingsPending: Promise<void> = Promise.resolve()\n private disposed = false\n private readonly inFlight = new Map<string, AbortController>()\n readonly support: TitleSupport\n\n constructor(private readonly options: {\n plugin: string\n llm?: LlmTextCaller\n host?: unknown\n sessionTitle?: SessionTitleServiceLike\n loader?: LoaderFace\n sessions?: { get(id: string): SessionLike | undefined }\n store?: TitleSettingsStore\n localePreference?: () => string | undefined\n initialLocale?: TitleLocaleMode\n now?: () => Date\n onSettingsChanged?: (revision: number) => void\n }) {\n const stored = storedSettings(options.store?.load(), options.initialLocale ?? 'auto')\n this.settings = stored\n this.config = defaultGenerateConfig(this.settings.locale)\n this.support = { nativeOwner: null, weOwn: false }\n }\n\n async start(): Promise<void> {\n const { llm, sessionTitle, loader, sessions } = this.options\n if (!llm || !sessionTitle?.register || !sessions || !loader) {\n this.support.weOwn = false\n this.support.limitation = 'required host services are missing'\n return\n }\n this.nativeSlot = createNativeTitleSlot({\n sessionTitle,\n loader,\n automatic: () => this.settings.cadence,\n generate: request => this.nativeGenerate(request),\n })\n try {\n this.claim = await claimTitleSlot(this.nativeSlot, PROVIDER_ID)\n this.support.weOwn = true\n this.support.nativeOwner = PROVIDER_ID\n delete this.support.limitation\n } catch (error) {\n this.support.weOwn = false\n this.support.nativeOwner = this.nativeSlot.owner() ?? null\n this.support.limitation = error instanceof Error ? error.message : 'native title provider slot is occupied'\n }\n }\n\n async dispose(): Promise<void> {\n this.disposed = true\n this.generation += 1\n for (const controller of this.inFlight.values()) controller.abort(new Error('current-title disabled'))\n this.inFlight.clear()\n await this.settingsPending\n const snapshot = this.nativeSlot?.displacement\n const outcome = await releaseTitleSlot(this.nativeSlot ?? { owner: () => undefined, occupy: async () => () => {} }, this.claim)\n if (outcome === 'released') await restoreOwnDisplacement(this.options.loader, snapshot)\n this.claim = undefined\n this.support.weOwn = false\n this.support.nativeOwner = this.nativeSlot?.owner() ?? null\n if (outcome === 'left-other-owner') {\n this.support.limitation = 'another title provider owns the slot; previous owner was not restored'\n }\n }\n\n status(sessionId?: string): TitleStatus {\n const session = sessionId && this.options.sessions ? this.options.sessions.get(sessionId) : undefined\n const snapshot = session ? this.read(session) : undefined\n return {\n settings: { ...this.settings, model: { ...this.settings.model } },\n support: { ...this.support, nativeOwner: this.nativeSlot?.owner() ?? this.support.nativeOwner },\n ...(sessionId ? {\n session: {\n sessionId,\n title: snapshot?.title,\n sourceKind: snapshot?.source.kind,\n generating: this.inFlight.has(sessionId),\n pinned: snapshot?.source.kind === 'user',\n },\n } : {}),\n }\n }\n\n async updateSettings(patch: {\n prompt?: unknown\n model?: unknown\n cadence?: unknown\n expectedRevision: number\n }): Promise<TitleSettings> {\n patch = structuredClone(patch)\n const result = this.settingsPending.then(async () => {\n if (this.disposed) throw Object.assign(new Error('current-title disabled'), { code: 'unavailable' })\n if (this.settings.revision !== patch.expectedRevision) {\n throw Object.assign(new Error('current-title settings changed'), { code: 'revision' })\n }\n const previous = this.settings\n const next: TitleSettings = {\n revision: this.settings.revision + 1,\n ...settingsPatch(this.settings, patch),\n }\n if (this.options.store) await this.options.store.save(next)\n this.settings = next\n // Observer failures cannot roll back a committed settings write.\n try { this.options.onSettingsChanged?.(next.revision) } catch {}\n this.config = { ...this.config, locale: 'auto' }\n if (!this.disposed && next.cadence !== previous.cadence) await this.reclaimWithCadence()\n return {\n revision: next.revision,\n locale: next.locale,\n prompt: next.prompt,\n model: { ...next.model },\n cadence: next.cadence,\n }\n })\n this.settingsPending = result.then(() => {}, () => {})\n return result\n }\n\n /** Re-register the provider so a cadence change takes effect without a restart. */\n private async reclaimWithCadence(): Promise<void> {\n if (!this.claim || !this.nativeSlot) return\n const stale = this.claim\n this.claim = undefined\n await stale.dispose()\n try {\n this.claim = await claimTitleSlot(this.nativeSlot, PROVIDER_ID)\n this.support.weOwn = true\n this.support.nativeOwner = PROVIDER_ID\n delete this.support.limitation\n } catch (error) {\n this.support.weOwn = false\n this.support.nativeOwner = this.nativeSlot.owner() ?? null\n this.support.limitation = error instanceof Error ? error.message : 'native title provider slot is occupied'\n }\n }\n\n /** @deprecated Locale is always auto. Kept so older clients still save a revision. */\n async updateLocale(_locale: TitleLocaleMode, expectedRevision: number): Promise<TitleSettings> {\n return this.updateSettings({ expectedRevision })\n }\n\n async regenerate(sessionId: string, signal: AbortSignal): Promise<TitleSnapshot | undefined> {\n if (!this.support.weOwn) throw Object.assign(new Error('title provider is inactive'), { code: 'unavailable' })\n const session = this.options.sessions?.get(sessionId)\n if (!session) throw Object.assign(new Error('session not found'), { code: 'not-found' })\n await this.options.sessionTitle?.refresh(session, signal)\n return this.read(session)\n }\n\n read(session: SessionLike): TitleSnapshot | undefined {\n const snapshot = this.options.sessionTitle?.get(session)\n if (!snapshot?.title) return undefined\n const kind = snapshot.source?.kind\n return {\n title: snapshot.title,\n messageSeqs: snapshot.messageSeqs ? [...snapshot.messageSeqs] : [],\n source: {\n kind: kind === 'user' || kind === 'fallback' || kind === 'provider' ? kind : 'provider',\n ...(typeof snapshot.source?.provider === 'string' ? { provider: snapshot.source.provider } : {}),\n },\n eventSeq: Number(snapshot.eventSeq ?? 0),\n updatedAt: snapshot.updatedAt ?? 0,\n }\n }\n\n private async nativeGenerate(request: {\n session: { id: string; snapshotEvents(): readonly unknown[] }\n messages: ReadonlyArray<{ seq: number; text: string }>\n signal: AbortSignal\n }) {\n const llm = this.options.llm\n if (!llm) throw new Error('dsh-current-title: llm is not available')\n const generation = this.generation\n const controller = new AbortController()\n this.inFlight.get(request.session.id)?.abort(new Error('newer title generation superseded older work'))\n this.inFlight.set(request.session.id, controller)\n try {\n const combined = AbortSignal.any([request.signal, controller.signal])\n const session = this.options.sessions?.get(request.session.id)\n // The host writes a free deterministic fallback title before it consults a\n // provider, and this provider contract has no \"decline\" result. Declining is\n // therefore the only way to keep delegated child threads off the model: their\n // dispatch prompt arrives as the child's own user message, so `source.kind`\n // cannot exclude them. The host logs the rejection and keeps the fallback.\n if (isSubagentSession(session)) {\n throw Object.assign(new Error('dsh-current-title: delegated child thread keeps the host fallback title'), { code: 'subagent-session' })\n }\n const messages = collectHumanMessages(\n request.messages.map(message => ({\n type: 'user/message',\n seq: message.seq,\n data: { source: { kind: 'user' }, content: [{ type: 'text', text: message.text }] },\n })),\n )\n const generated = await generateCurrentTitle({\n llm,\n host: this.options.host ?? {},\n config: this.config,\n sessionId: request.session.id,\n messages: messages.length > 0 ? messages : [...request.messages],\n prompt: this.settings.prompt,\n model: this.settings.model,\n localePreference: this.options.localePreference?.(),\n signal: combined,\n isCurrent: () => this.generation === generation && this.support.weOwn\n && (!session || this.options.sessions?.get(request.session.id) === session),\n now: this.options.now?.(),\n })\n return { title: generated.title, messageSeqs: generated.messageSeqs, model: generated.model }\n } finally {\n if (this.inFlight.get(request.session.id) === controller) this.inFlight.delete(request.session.id)\n }\n }\n}\n\nexport function hostLocalePreference(host: CurrentTitleHost): string | undefined {\n try {\n const settings = host.get('settings') as { get?(namespace: string): unknown } | undefined\n const value = settings?.get?.('locale')\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined\n const preference = (value as Record<string, unknown>).preference\n return typeof preference === 'string' ? preference : undefined\n } catch {\n return undefined\n }\n}\n\nexport async function handleRpc(service: CurrentTitleService, endpoint: string, payload: unknown, signal: AbortSignal): Promise<RpcResult> {\n signal.throwIfAborted()\n const body = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload as Record<string, unknown> : {}\n if (endpoint === 'status') {\n return { ok: true, value: service.status(typeof body.sessionId === 'string' ? body.sessionId : undefined) }\n }\n if (endpoint === 'settings') {\n if (typeof body.expectedRevision !== 'number') {\n return { ok: false, error: { code: 'bad-request', message: '标题设置无效。' } }\n }\n try {\n return {\n ok: true,\n value: await service.updateSettings({\n ...(body.prompt !== undefined ? { prompt: body.prompt } : {}),\n ...(body.model !== undefined ? { model: body.model } : {}),\n ...(body.cadence !== undefined ? { cadence: body.cadence } : {}),\n expectedRevision: body.expectedRevision,\n }),\n }\n } catch (error) {\n const code = error instanceof Error && 'code' in error ? String((error as { code?: string }).code) : 'failed'\n if (code === 'revision') return { ok: false, error: { code, message: '设置已更新,请刷新后重试。' } }\n return { ok: false, error: { code: 'failed', message: '无法保存标题设置。' } }\n }\n }\n if (endpoint === 'regenerate') {\n if (typeof body.sessionId !== 'string' || !body.sessionId) {\n return { ok: false, error: { code: 'bad-request', message: '缺少会话。' } }\n }\n try {\n const snapshot = await service.regenerate(body.sessionId, signal)\n return { ok: true, value: snapshot ?? null }\n } catch (error) {\n const code = error instanceof Error && 'code' in error ? String((error as { code?: string }).code) : 'failed'\n if (code === 'not-found') return { ok: false, error: { code, message: '找不到该会话。' } }\n if (code === 'unavailable') return { ok: false, error: { code, message: '标题服务未就绪。' } }\n if (signal.aborted) return { ok: false, error: { code: 'cancelled', message: '已取消。' } }\n return { ok: false, error: { code: 'failed', message: '标题生成失败。' } }\n }\n }\n return { ok: false, error: { code: 'bad-request', message: '未知操作。' } }\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport type { LlmTextCaller } from '@klarkxy/dsh-plugin-kit'\nimport { registerHostRpc, type HostRpcContext } from '@klarkxy/dsh-plugin-kit/host-rpc'\nimport { registerModelSettings, notifyModelSettingsChanged, type ModelSettingsEvents } from '@klarkxy/dsh-plugin-kit/model-settings'\nimport { isTitleLocaleMode, PLUGIN_NAME, RPC_CHANNEL, SETTINGS_KEY, type TitleLocaleMode } from './contracts.ts'\nimport { CurrentTitleService, handleRpc, hostLocalePreference } from './service.ts'\nimport type { LoaderFace, SessionTitleServiceLike } from './native-slot.ts'\nimport type { SessionLike } from './messages.ts'\nimport { parseTitleSettings, titleDomain } from './storage.ts'\n\nexport const name = PLUGIN_NAME\nexport const inject = ['sessionTitle', 'llm', 'sessions', 'loader', 'storageDomain', 'connection', 'webServer', 'agents', 'sessionProjections', 'agentDefaultModel'] as const\nexport { CurrentTitleService }\nexport { selectRecentMessages, frameMessages } from './input.ts'\nexport { parseModelTitle, formatTitle, resolveTitleLocale } from './output.ts'\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context { currentTitle: CurrentTitleService }\n}\n\ntype DomainHandle = {\n table(name: 'settings'): {\n get(key: string): unknown\n put(key: string, value: unknown): Promise<void>\n }\n close(): Promise<void>\n}\n\ntype Host = Context & HostRpcContext & {\n sessionTitle: SessionTitleServiceLike\n llm: LlmTextCaller\n sessions: { get(id: string): SessionLike | undefined }\n loader: LoaderFace\n storageDomain: { open: (spec: typeof titleDomain) => Promise<DomainHandle> }\n}\n\nexport async function apply(ctx: Context, config: { locale?: TitleLocaleMode } = {}): Promise<void> {\n const host = ctx as Host\n const domain = await host.storageDomain.open(titleDomain)\n const table = domain.table('settings')\n const locale = isTitleLocaleMode(config.locale) ? config.locale : 'auto'\n const service = new CurrentTitleService({\n plugin: PLUGIN_NAME,\n onSettingsChanged: revision => notifyModelSettingsChanged(ctx, PLUGIN_NAME, revision),\n llm: host.llm,\n host,\n sessionTitle: host.sessionTitle,\n loader: host.loader,\n sessions: host.sessions,\n store: {\n load: () => parseTitleSettings(table.get(SETTINGS_KEY)),\n save: settings => table.put(SETTINGS_KEY, settings),\n },\n initialLocale: locale,\n localePreference: () => hostLocalePreference(host),\n })\n await service.start()\n ctx.provide('currentTitle', service)\n ctx.effect(() => async () => { await service.dispose(); await domain.close() }, 'current-title.lifecycle')\n ctx.effect(() => registerHostRpc(host, RPC_CHANNEL, (endpoint, payload, signal) => handleRpc(service, endpoint, payload, signal)), 'current-title.rpc')\n ctx.effect(() => registerModelSettings(ctx as unknown as ModelSettingsEvents, {\n ns: PLUGIN_NAME, title: { zh: '自动标题', en: 'Auto Title' },\n read(signal) {\n signal.throwIfAborted()\n const settings = service.status().settings\n return { revision: settings.revision, fields: [{ path: ['model'], label: { zh: '标题模型', en: 'Title model' }, current: settings.model,\n followLabel: { zh: '当前会话 / 宿主默认模型', en: 'Current conversation / host default' }, allowFollow: true }] }\n },\n async write(path, route, expectedRevision, signal) {\n if (path.length !== 1 || path[0] !== 'model') throw new Error('Unknown Current Title model setting')\n signal.throwIfAborted()\n return service.updateSettings({ model: route, expectedRevision })\n },\n }), 'current-title.model-settings')\n}\n"],"mappings":"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"}
|