dsh-session-title-stamp 0.0.0-stage → 0.4.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/LICENSE +21 -0
- package/README.md +267 -2
- package/README.zh.md +200 -0
- package/client.js +276 -0
- package/cordis.patch.yml +29 -0
- package/icon.svg +45 -0
- package/lib/format.js +636 -0
- package/lib/index.js +544 -0
- package/market/index.json +74 -0
- package/package.json +67 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xuedipiaofei
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,268 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-session-title-stamp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/dsh-session-title-stamp)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
6
|
+
|
|
7
|
+
English | [中文](./README.zh.md)
|
|
8
|
+
|
|
9
|
+
A DeepSeek Harness title provider for a configurable `DATE | CATEGORY | NAME`
|
|
10
|
+
title. Beyond automatic naming:
|
|
11
|
+
|
|
12
|
+
- **Auto refresh** — after a completed turn, with a 2-minute cooldown and a
|
|
13
|
+
100-message freeze; forks and user-pinned titles are skipped.
|
|
14
|
+
- **Manual refresh** — the conversation-header ↻ button, the session row's
|
|
15
|
+
**Regenerate title** menu item, or `/retitle`.
|
|
16
|
+
- **Hover stats** — elapsed time, estimated CNY cost, and token count on the
|
|
17
|
+
session hover card.
|
|
18
|
+
- **Category language** — `categoryPreset: zh | en`, or your own `categories`.
|
|
19
|
+
- **Failure-safe** — a failed retitle keeps the current title.
|
|
20
|
+
- **Zero build** — pure Host logic plus a small hand-written browser half.
|
|
21
|
+
|
|
22
|
+
Default format:
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
DATE | CATEGORY | NAME
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
For example, with `categoryPreset: en`:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
1028 | Analysis | Short-drama platform verification
|
|
32
|
+
1027 | Fix | Four-device acceptance rework
|
|
33
|
+
1026 | Deploy | Ubuntu environment setup
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Dates are computed by the plugin from the session's creation time, never by the
|
|
37
|
+
model. Categories are chosen from a small configurable taxonomy and validated
|
|
38
|
+
locally. The model only proposes the category and the name; the provider owns
|
|
39
|
+
the final format. The default taxonomy is Chinese; set `categoryPreset: en` for
|
|
40
|
+
the English labels above.
|
|
41
|
+
|
|
42
|
+
## Why
|
|
43
|
+
|
|
44
|
+
Long-lived workspaces collect dozens of sessions whose titles are either the
|
|
45
|
+
first few words of the first message or an auto-summary that drifts. A fixed
|
|
46
|
+
`date | category | name` convention makes the sidebar scannable across projects,
|
|
47
|
+
and mirrors conventions used by other coding agents.
|
|
48
|
+
|
|
49
|
+
## Requirements
|
|
50
|
+
|
|
51
|
+
- DeepSeek Harness `>=0.2.1-rc.3 <0.3.0`.
|
|
52
|
+
- A working model route for the session; the title request uses the session's
|
|
53
|
+
current provider/model unless `provider`/`model` are configured.
|
|
54
|
+
|
|
55
|
+
## Install
|
|
56
|
+
|
|
57
|
+
### DSH Desktop / Web with Discover
|
|
58
|
+
|
|
59
|
+
Add this market index source in **Plugins → Discover → Index sources**:
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
https://cdn.jsdelivr.net/gh/xuedipiaofei/dsh-session-title-stamp@v0.4.1/market/index.json
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Then install **Session title stamp** from the list. The plugin installs as a
|
|
66
|
+
profile bundle and appears in the Plugins page, where it can be enabled or
|
|
67
|
+
disabled like any other bundle.
|
|
68
|
+
|
|
69
|
+
### CLI
|
|
70
|
+
|
|
71
|
+
From GitHub:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
dsh plugin --profile <profile> add github:xuedipiaofei/dsh-session-title-stamp
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
From npm:
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
dsh plugin --profile <profile> add dsh-session-title-stamp
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
For a local checkout:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
dsh plugin --profile <profile> add ./dsh-session-title-stamp
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The Electron Desktop profile is managed by the application; use its Plugins
|
|
90
|
+
page rather than `dsh plugin --profile desktop`.
|
|
91
|
+
|
|
92
|
+
The bundle patch disables the shipped `session-title-llm` row and inserts this
|
|
93
|
+
provider. Do not enable the official
|
|
94
|
+
`@deepseek-ai/dsh-experimental-session-titles-bundle` at the same time: both
|
|
95
|
+
replace the same provider slot, and the title service accepts only one provider.
|
|
96
|
+
If another provider wins the registration race, this plugin logs a warning and
|
|
97
|
+
stays inactive instead of failing startup.
|
|
98
|
+
|
|
99
|
+
## How it works
|
|
100
|
+
|
|
101
|
+
- `cordis.patch.yml` disables `session-title-llm` and inserts the
|
|
102
|
+
`session-title-stamp` row from this package.
|
|
103
|
+
- `lib/index.js` registers a provider through `ctx.sessionTitle.register()` and
|
|
104
|
+
reuses `@deepseek-ai/dsh-session-title-llm` for route selection, timeouts,
|
|
105
|
+
cancellation, token limits, and exact auxiliary-request logging.
|
|
106
|
+
- `lib/format.js` is dependency-free: local-time date parts, template rendering,
|
|
107
|
+
category validation, name sanitizing, and UTF-8 byte truncation.
|
|
108
|
+
|
|
109
|
+
The provider computes the date from `session.header.createdAt` in the host's
|
|
110
|
+
local time zone, asks the model for `{"category":"...","name":"..."}`, validates
|
|
111
|
+
the category, and renders the template. Empty `{type}` or `{topic}` segments
|
|
112
|
+
remove their neighbouring separator instead of leaving `||`.
|
|
113
|
+
|
|
114
|
+
### Retitle cadence
|
|
115
|
+
|
|
116
|
+
The provider is registered as `first-prompt`, so the title service names the
|
|
117
|
+
session once. After that, the plugin refreshes on a completed turn (`turn/end`)
|
|
118
|
+
with two guards:
|
|
119
|
+
|
|
120
|
+
- **cooldown** — at most one automatic refresh every `refreshMinIntervalMs`
|
|
121
|
+
(default 2 minutes), so a fast first answer does not generate twice;
|
|
122
|
+
- **freeze** — after `refreshMaxTurns` user messages (default 100), automatic
|
|
123
|
+
refresh stops; long sessions rarely change their main topic. `/retitle`
|
|
124
|
+
still works.
|
|
125
|
+
|
|
126
|
+
Forks and subagent sessions are skipped, as are titles pinned by a manual
|
|
127
|
+
rename. Setting `refreshOnTurnEnd: false` restores the legacy message-count
|
|
128
|
+
mode using `retitleEvery`.
|
|
129
|
+
|
|
130
|
+
### Header refresh and hover stats
|
|
131
|
+
|
|
132
|
+
0.4.0 adds a small browser half:
|
|
133
|
+
|
|
134
|
+
- a **refresh icon** in the conversation header; it runs the same `/retitle`
|
|
135
|
+
command as typing it, and is disabled while the session is running.
|
|
136
|
+
- a **hover-card line** under the native relative time, for example
|
|
137
|
+
`Duration 1h 51m · est. ≈¥4.11 · 131.7M tokens`;
|
|
138
|
+
- a **Regenerate title** item at the top of the session row's `···` menu. It
|
|
139
|
+
runs the same `/retitle` command and adds no dialog.
|
|
140
|
+
|
|
141
|
+
The hover line is derived from the `sessionStats` and `tokenUsage` projections,
|
|
142
|
+
priced with the current DeepSeek CNY rate table, and labelled as an estimate.
|
|
143
|
+
It is fetched from the host only when the hover card opens; a session that was
|
|
144
|
+
never loaded is not resumed just to show the numbers. Native DSH already shows
|
|
145
|
+
running / pending / completed-unread status (spinner, warning dot, idle line),
|
|
146
|
+
so this plugin does **not** add a second status-color layer.
|
|
147
|
+
|
|
148
|
+
If the hover line is missing, the browser half could not reach the host stats
|
|
149
|
+
route; the refresh button and automatic titles are unaffected.
|
|
150
|
+
|
|
151
|
+
### Failures
|
|
152
|
+
|
|
153
|
+
- A failed retitle **keeps the existing title**: the provider throws, which is
|
|
154
|
+
the title service's signal to retain the previous revision.
|
|
155
|
+
- Only a session with no title yet gets a local fallback: date, an inferred
|
|
156
|
+
category, and the first message's leading words.
|
|
157
|
+
- A `max-tokens` finish is still parsed when it contains usable text; only an
|
|
158
|
+
unusable response counts as a failure.
|
|
159
|
+
|
|
160
|
+
## Configuration
|
|
161
|
+
|
|
162
|
+
Override the row in the profile's `cordis.patch.yml`:
|
|
163
|
+
|
|
164
|
+
```yaml
|
|
165
|
+
- id: session-title-stamp
|
|
166
|
+
config:
|
|
167
|
+
refreshOnTurnEnd: true
|
|
168
|
+
refreshMinIntervalMs: 120000
|
|
169
|
+
refreshMaxTurns: 100
|
|
170
|
+
categoryPreset: zh
|
|
171
|
+
template: '{date} | {type} | {topic}'
|
|
172
|
+
timeZone: Asia/Shanghai
|
|
173
|
+
maxNameChars: 20
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
| Field | Default | Meaning |
|
|
177
|
+
|---|---|---|
|
|
178
|
+
| `refreshOnTurnEnd` | `true` | Refresh after a completed turn; `false` falls back to `retitleEvery` |
|
|
179
|
+
| `refreshMinIntervalMs` | `120000` | Minimum gap between automatic refreshes; `0` disables the cooldown |
|
|
180
|
+
| `refreshMaxTurns` | `100` | Stop automatic refresh after this many user messages; `0` = no freeze |
|
|
181
|
+
| `retitleEvery` | `10` | Legacy message-count cadence, used only when `refreshOnTurnEnd: false` |
|
|
182
|
+
| `backfillOnStart` | `false` | On startup, refresh live sessions whose title is still the fallback |
|
|
183
|
+
| `backfillMaxSessions` | `30` | Maximum sessions in one backfill pass |
|
|
184
|
+
| `backfillIntervalMs` | `2000` | Delay between backfill refreshes |
|
|
185
|
+
| `template` | `{date} \| {type} \| {topic}` | Title template; see the placeholders below |
|
|
186
|
+
| `dateFormat` | `MMDD` | Used by `{date}`: `MMDD` (`1028`), `YYMM` (`2610`), or `YYYYMMDD` |
|
|
187
|
+
| `timeZone` | host local | IANA zone used for date/time placeholders |
|
|
188
|
+
| `categoryPreset` | `zh` | Built-in category language: `zh` or `en` |
|
|
189
|
+
| `categories` | preset | Explicit category list; overrides `categoryPreset` |
|
|
190
|
+
| `maxNameChars` | `20` | Character cap before the byte cap applies |
|
|
191
|
+
| `maxTitleBytes` | service cap (`80`) | Effective cap is the smaller of this and `session-title`'s cap |
|
|
192
|
+
| `recentMessages` | `8` | First message plus this many recent messages are sent |
|
|
193
|
+
| `maxInputBytes` | `4096` | Input budget for one title request |
|
|
194
|
+
| `maxOutputTokens` | `4096` | Output cap, including reasoning tokens |
|
|
195
|
+
| `timeoutMs` | `90000` | End-to-end title-request deadline |
|
|
196
|
+
| `provider` / `model` | session route | Optional pair to route title generation independently |
|
|
197
|
+
|
|
198
|
+
### Template placeholders
|
|
199
|
+
|
|
200
|
+
| Placeholder | Meaning |
|
|
201
|
+
|---|---|
|
|
202
|
+
| `{date}` | Date in `dateFormat` (`1028`, `2610`, `20261028`) |
|
|
203
|
+
| `{YYYY}` `{YY}` `{MM}` `{DD}` `{HH}` `{mm}` `{ss}` | Date/time parts; concatenate freely (`{YYYYMMDD}`, `{HHmm}`) |
|
|
204
|
+
| `{type}` | Category; an empty value removes the segment and its separator |
|
|
205
|
+
| `{topic}` | Name; an empty value removes the segment and its separator |
|
|
206
|
+
|
|
207
|
+
An unknown placeholder is left verbatim (`{MMDD} {bogus}` keeps `{bogus}`), so a
|
|
208
|
+
typo is visible instead of silently disappearing. The template owns the final
|
|
209
|
+
format; `maxTitleBytes` still clips the result.
|
|
210
|
+
|
|
211
|
+
The default taxonomy is Chinese. `categoryPreset: en` switches it to
|
|
212
|
+
`Analysis / Build / Fix / Debug / QA / Deploy / Docs / Other`; `categories`
|
|
213
|
+
overrides either preset with any labels you want. The prompt lists the configured
|
|
214
|
+
labels and the local fallback maps common keywords onto them.
|
|
215
|
+
|
|
216
|
+
## Privacy and cost
|
|
217
|
+
|
|
218
|
+
- The first eligible user message plus up to `recentMessages - 1` recent
|
|
219
|
+
messages are sent to the model. Message text is framed as JSON to keep user
|
|
220
|
+
content from breaking delimiters.
|
|
221
|
+
- The request uses the session's configured provider/model, or the optional
|
|
222
|
+
`provider`/`model` override. The plugin makes no other network calls and
|
|
223
|
+
collects no telemetry.
|
|
224
|
+
- One auxiliary request is made for the first title, then at most one per
|
|
225
|
+
completed turn inside the cooldown, and none after the freeze threshold.
|
|
226
|
+
Title work is asynchronous and never delays the main agent response;
|
|
227
|
+
`maxOutputTokens`, `maxInputBytes`, and `timeoutMs` bound it.
|
|
228
|
+
- The resulting title is stored in the session log as a normal `session/title`
|
|
229
|
+
event. A manual rename pins the title and later automatic revisions do not
|
|
230
|
+
overwrite it.
|
|
231
|
+
|
|
232
|
+
## Compatibility notes
|
|
233
|
+
|
|
234
|
+
- This package targets the DSH 0.2 line. `engines.dsh` refuses to install it on
|
|
235
|
+
0.3+ until it is updated against the new title API.
|
|
236
|
+
- The bundle assumes the shipped row id `session-title-llm`. If another title
|
|
237
|
+
provider is registered first, this plugin logs and stays inactive; disable the
|
|
238
|
+
other provider and reload.
|
|
239
|
+
- `llm`, `commands`, `sessions`, `sessionProjections`, and `connection` are
|
|
240
|
+
awaited lazily; a composition without one still loads the plugin and degrades
|
|
241
|
+
only the affected feature.
|
|
242
|
+
- The browser half needs the Web composition's `sidebar.session.row.hover` and
|
|
243
|
+
`conversation.session.header.actions` seats. A shell that does not expose them
|
|
244
|
+
simply shows no button/hover line.
|
|
245
|
+
- The provider imports `@deepseek-ai/dsh-session-title-llm` dynamically. A
|
|
246
|
+
missing or incompatible executor degrades to local naming, but a changed
|
|
247
|
+
`ctx.sessionTitle` API can prevent registration. Pin the DSH range when
|
|
248
|
+
publishing a release.
|
|
249
|
+
|
|
250
|
+
## Development
|
|
251
|
+
|
|
252
|
+
The repository has no runtime or build dependencies.
|
|
253
|
+
|
|
254
|
+
```sh
|
|
255
|
+
npm test # format tests, provider tests with a stubbed executor, metadata/index checks
|
|
256
|
+
npm run validate:index # optional: validate market/index.json with the official validator if installed
|
|
257
|
+
npm run pack:check # inspect the npm tarball contents
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
The provider test redirects `@deepseek-ai/dsh-session-title-llm` to
|
|
261
|
+
`test/support/stub-executor.mjs` with Node's `module.registerHooks`, so it
|
|
262
|
+
exercises the real provider without a DSH install or model credentials.
|
|
263
|
+
|
|
264
|
+
Release steps are in [PUBLISHING.md](./PUBLISHING.md).
|
|
265
|
+
|
|
266
|
+
## License
|
|
267
|
+
|
|
268
|
+
MIT © 2026 xuedipiaofei
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# dsh-session-title-stamp
|
|
2
|
+
|
|
3
|
+
[English](./README.md) | 中文
|
|
4
|
+
|
|
5
|
+
给 DSH 会话一个稳定的「日期 | 分类 | 名称」标题。除了自动命名,还包括:
|
|
6
|
+
|
|
7
|
+
- **自动重算**:回答完成后更新,2 分钟冷却,超过 100 条消息后冻结;fork、子代理和手动固定的标题跳过。
|
|
8
|
+
- **手动刷新**:会话头 ↻ 按钮、会话行 `···` 菜单「重新生成标题」、或 `/retitle`。
|
|
9
|
+
- **悬停统计**:会话行悬停显示耗时、估算 CNY 成本、token 数。
|
|
10
|
+
- **分类语言**:`categoryPreset: zh | en`,也可以用 `categories` 自定义。
|
|
11
|
+
- **失败保护**:重算失败保留当前标题,不被兜底标题覆盖。
|
|
12
|
+
- **零构建**:纯 Host 逻辑 + 小型手写浏览器半边。
|
|
13
|
+
|
|
14
|
+
默认格式:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
日期 | 分类 | 名称
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
例如:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
1028 | 分析 | 绿洲短剧平台验证
|
|
24
|
+
1027 | 整改 | 四台设备第十题验收返修
|
|
25
|
+
1026 | 部署 | Ubuntu 环境初始化
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
日期由插件从**会话创建时间**计算,不交给模型;分类只能从可配置词表中选择,并在本地校验;模型只负责提出分类和名称,最终格式由模板决定。
|
|
29
|
+
|
|
30
|
+
## 为什么
|
|
31
|
+
|
|
32
|
+
工作区里的会话一多,侧栏标题要么是首条消息的前几个字,要么是容易漂移的自动摘要。固定成 `日期 | 分类 | 名称` 后,跨项目扫侧栏会快很多,也和其它 coding agent 的命名习惯一致。
|
|
33
|
+
|
|
34
|
+
## 要求
|
|
35
|
+
|
|
36
|
+
- DeepSeek Harness `>=0.2.1-rc.3 <0.3.0`。
|
|
37
|
+
- 会话有可用的模型路由;标题请求默认跟随会话当前的 provider/model,也可用 `provider`/`model` 单独指定。
|
|
38
|
+
|
|
39
|
+
## 安装
|
|
40
|
+
|
|
41
|
+
### DSH Desktop / Web(Discover)
|
|
42
|
+
|
|
43
|
+
在 **插件 → 发现(Discover)→ 索引源** 添加:
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
https://cdn.jsdelivr.net/gh/xuedipiaofei/dsh-session-title-stamp@v0.4.1/market/index.json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
然后在列表里安装 **Session title stamp / 会话标题印章**。插件以 profile bundle 的形式安装,之后可在插件页启停。
|
|
50
|
+
|
|
51
|
+
### CLI
|
|
52
|
+
|
|
53
|
+
从 GitHub 安装:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
dsh plugin --profile <profile> add github:xuedipiaofei/dsh-session-title-stamp
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
从 npm 安装:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
dsh plugin --profile <profile> add dsh-session-title-stamp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
本地 checkout:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
dsh plugin --profile <profile> add ./dsh-session-title-stamp
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Electron Desktop 的 profile 由应用管理,请在应用的插件页安装,不要用 `dsh plugin --profile desktop`。
|
|
72
|
+
|
|
73
|
+
bundle 的 patch 会禁用内置的 `session-title-llm` 行并插入本插件。**不要同时启用官方的 `@deepseek-ai/dsh-experimental-session-titles-bundle`**:两者替换的是同一个 provider 槽位,标题服务只接受一个 provider。如果别的插件先注册成功,本插件只打一条 warn 并保持 inactive,不会让启动失败。
|
|
74
|
+
|
|
75
|
+
## 实现方式
|
|
76
|
+
|
|
77
|
+
- `cordis.patch.yml` 关闭 `session-title-llm`,插入本包的 `session-title-stamp` 行。
|
|
78
|
+
- `lib/index.js` 通过 `ctx.sessionTitle.register()` 注册提供者,并复用官方的
|
|
79
|
+
`@deepseek-ai/dsh-session-title-llm` 处理路由、超时、取消、token 上限和请求日志。
|
|
80
|
+
- `client.js` 是手写的浏览器模块:会话头刷新按钮 + 悬停卡片成本/耗时行。
|
|
81
|
+
|
|
82
|
+
插件从 `session.header.createdAt` 按**本机时区**计算日期,让模型返回
|
|
83
|
+
`{"category":"...","name":"..."}`,校验分类,再按模板拼装。`{type}` 或 `{topic}` 为空时,相邻分隔符会一起消失,不会留下 `||`。
|
|
84
|
+
|
|
85
|
+
### 重算节奏
|
|
86
|
+
|
|
87
|
+
provider 注册为 `first-prompt`,首条消息由标题服务自动命名。之后插件在**每次 turn/end(回答完成)**时判断,并有两道闸:
|
|
88
|
+
|
|
89
|
+
- **冷却**:距上次自动刷新不足 `refreshMinIntervalMs`(默认 2 分钟)就跳过;
|
|
90
|
+
- **冻结**:用户消息数超过 `refreshMaxTurns`(默认 100)后停止自动刷新;`/retitle` 仍可用。
|
|
91
|
+
|
|
92
|
+
fork 子会话、子代理会话,以及用户手动改名固定的标题都会跳过。`refreshOnTurnEnd: false` 时回退到旧的「每 N 条消息」模式(`retitleEvery`)。
|
|
93
|
+
|
|
94
|
+
### 会话头按钮与悬停统计
|
|
95
|
+
|
|
96
|
+
0.4.0 增加了一个很小的浏览器半边:
|
|
97
|
+
|
|
98
|
+
- 会话头有一个**刷新图标**,效果等同输入 `/retitle`,会话运行中禁用;
|
|
99
|
+
- 鼠标悬停会话行时,在原生相对时间下面多一行:
|
|
100
|
+
`耗时 1h 51m · 估算 ≈¥4.11 · 131.7M tokens`;
|
|
101
|
+
- 会话行 `···` 菜单最上面多一项「重新生成标题」;同样调用 `/retitle`,
|
|
102
|
+
不新开弹窗。
|
|
103
|
+
|
|
104
|
+
数字来自 `sessionStats` / `tokenUsage` 投影,按 DeepSeek 当前 CNY 价格估算,并标注「估算」。只在悬停时向宿主请求,不会为了显示数字去恢复未加载的会话。DSH 原生已经有运行中/待交互/完成未读的状态点与 loader,所以本插件**不再加一层状态颜色**。
|
|
105
|
+
|
|
106
|
+
如果悬停行不显示,说明浏览器半边没连上宿主统计路由;刷新按钮和自动标题不受影响。
|
|
107
|
+
|
|
108
|
+
### 失败时
|
|
109
|
+
|
|
110
|
+
- 已经有标题时,重算失败会**保留旧标题**:provider 抛错,标题服务据此不提交新修订。
|
|
111
|
+
- 只有还没有标题的会话才用本地兜底:日期 + 推断分类 + 首条消息开头。
|
|
112
|
+
- 模型以 `max-tokens` 结束时,只要还能解析出可用文本就继续用;只有完全不可用才算失败。
|
|
113
|
+
|
|
114
|
+
## 配置
|
|
115
|
+
|
|
116
|
+
在 profile 的 `cordis.patch.yml` 里覆盖:
|
|
117
|
+
|
|
118
|
+
```yaml
|
|
119
|
+
- id: session-title-stamp
|
|
120
|
+
config:
|
|
121
|
+
refreshOnTurnEnd: true
|
|
122
|
+
refreshMinIntervalMs: 120000
|
|
123
|
+
refreshMaxTurns: 100
|
|
124
|
+
categoryPreset: zh
|
|
125
|
+
template: '{date} | {type} | {topic}'
|
|
126
|
+
timeZone: Asia/Shanghai
|
|
127
|
+
maxNameChars: 20
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
| 字段 | 默认 | 说明 |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| `refreshOnTurnEnd` | `true` | 回答完成后刷新;`false` 时回退到 `retitleEvery` |
|
|
133
|
+
| `refreshMinIntervalMs` | `120000` | 自动刷新最小间隔;`0` 关闭冷却 |
|
|
134
|
+
| `refreshMaxTurns` | `100` | 超过多少条用户消息后冻结自动刷新;`0` 不冻结 |
|
|
135
|
+
| `retitleEvery` | `10` | 旧的按消息条数模式,仅在 `refreshOnTurnEnd: false` 时使用 |
|
|
136
|
+
| `backfillOnStart` | `false` | 启动时对仍是 fallback 标题的 live 会话逐个补刷 |
|
|
137
|
+
| `backfillMaxSessions` | `30` | 单次回填最多处理多少个会话 |
|
|
138
|
+
| `backfillIntervalMs` | `2000` | 回填两次刷新之间的间隔 |
|
|
139
|
+
| `template` | `{date} \| {type} \| {topic}` | 标题模板,占位符见下 |
|
|
140
|
+
| `dateFormat` | `MMDD` | `{date}` 使用:`MMDD`(`1028`)/ `YYMM`(`2610`)/ `YYYYMMDD` |
|
|
141
|
+
| `timeZone` | 本机时区 | 日期/时间占位符使用的 IANA 时区 |
|
|
142
|
+
| `categoryPreset` | `zh` | 内置分类语言:`zh` 或 `en` |
|
|
143
|
+
| `categories` | 预设 | 显式分类词表,优先级高于 `categoryPreset` |
|
|
144
|
+
| `maxNameChars` | `20` | 名称字符上限(最终还受字节上限约束) |
|
|
145
|
+
| `maxTitleBytes` | 标题服务上限(`80`) | 实际取它与 `session-title` 的较小值 |
|
|
146
|
+
| `recentMessages` | `8` | 发送首条消息 + 最近 N 条消息 |
|
|
147
|
+
| `maxInputBytes` | `4096` | 单次标题请求输入上限 |
|
|
148
|
+
| `maxOutputTokens` | `4096` | 输出上限(含推理 token) |
|
|
149
|
+
| `timeoutMs` | `90000` | 标题请求超时 |
|
|
150
|
+
| `provider` / `model` | 跟随会话 | 可选,成对设置时用独立模型生成标题 |
|
|
151
|
+
|
|
152
|
+
### 模板占位符
|
|
153
|
+
|
|
154
|
+
| 占位符 | 含义 |
|
|
155
|
+
|---|---|
|
|
156
|
+
| `{date}` | 按 `dateFormat` 渲染的日期(`1028`、`2610`、`20261028`) |
|
|
157
|
+
| `{YYYY}` `{YY}` `{MM}` `{DD}` `{HH}` `{mm}` `{ss}` | 日期/时间部件,可任意拼接(`{YYYYMMDD}`、`{HHmm}`) |
|
|
158
|
+
| `{type}` | 分类;为空时整段及相邻分隔符一起消失 |
|
|
159
|
+
| `{topic}` | 名称;为空时整段及相邻分隔符一起消失 |
|
|
160
|
+
|
|
161
|
+
不认识的占位符会**原样保留**(`{MMDD} {bogus}` 会保留 `{bogus}`),方便一眼看出模板写错,而不是悄悄消失。最终仍受 `maxTitleBytes` 截断。
|
|
162
|
+
|
|
163
|
+
默认分类是中文。`categoryPreset: en` 可切到
|
|
164
|
+
`Analysis / Build / Fix / Debug / QA / Deploy / Docs / Other`;`categories`
|
|
165
|
+
可以覆盖任意预设。提示词使用配置的标签,本地兜底也会把常见中英文关键词映射过去。
|
|
166
|
+
|
|
167
|
+
## 隐私与成本
|
|
168
|
+
|
|
169
|
+
- 发送首条有效用户消息,加上最多 `recentMessages - 1` 条最近消息。消息以 JSON 包裹,避免用户文本破坏分隔符。
|
|
170
|
+
- 请求使用会话当前的 provider/model,或可选的 `provider`/`model` 覆盖。插件没有其它网络请求,也不收集遥测。
|
|
171
|
+
- 首条标题一次辅助请求,之后每个完成的 turn 最多一次(受冷却/冻结约束)。标题生成是异步的,不会阻塞主回复;`maxOutputTokens`、`maxInputBytes`、`timeoutMs` 都会约束它。
|
|
172
|
+
- 标题作为普通 `session/title` 事件写入会话日志。用户手动重命名后标题会被固定,后续自动命名不会覆盖。
|
|
173
|
+
|
|
174
|
+
## 兼容性
|
|
175
|
+
|
|
176
|
+
- 本包面向 DSH 0.2 线;`engines.dsh` 会在 0.3+ 上拒绝安装,直到按新 API 更新。
|
|
177
|
+
- bundle 依赖内置行 id `session-title-llm`。如果已有其它标题 provider 先注册,本插件会打 warn 并保持 inactive;先停用另一个再重载。
|
|
178
|
+
- `llm`、`commands`、`sessions`、`sessionProjections`、`connection` 都采用延迟注入;缺少哪个只损失对应能力,不影响插件加载。
|
|
179
|
+
- 浏览器半边依赖 Web 组合的 `sidebar.session.row.hover` 和 `conversation.session.header.actions` 座位;没有这两个座位的 shell 不会显示按钮/悬停行。
|
|
180
|
+
- 插件动态 import `@deepseek-ai/dsh-session-title-llm`。执行器缺失或不兼容时会降级为本地命名;如果 `ctx.sessionTitle` API 本身变化,则可能无法注册。
|
|
181
|
+
|
|
182
|
+
## 开发
|
|
183
|
+
|
|
184
|
+
仓库没有运行时依赖,也不需要构建。
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
npm test # 格式测试、带 stub 执行器的 provider 测试、元数据/索引检查
|
|
188
|
+
npm run validate:index # 可选:用官方校验器校验 market/index.json(需先安装该包)
|
|
189
|
+
npm run pack:check # 查看 npm tarball 内容
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
provider 测试用 Node 的 `module.registerHooks` 把
|
|
193
|
+
`@deepseek-ai/dsh-session-title-llm` 重定向到
|
|
194
|
+
`test/support/stub-executor.mjs`,因此不需要安装 DSH 或模型凭据。
|
|
195
|
+
|
|
196
|
+
发布步骤见 [PUBLISHING.md](./PUBLISHING.md)。
|
|
197
|
+
|
|
198
|
+
## 许可证
|
|
199
|
+
|
|
200
|
+
MIT © 2026 xuedipiaofei
|