dsh-persona-forge 0.3.1 → 0.8.3
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/CHANGELOG.md +671 -1
- package/README.en.md +260 -108
- package/README.md +157 -50
- package/assets/menu.png +0 -0
- package/assets/review-panel.png +0 -0
- package/assets/settings.png +0 -0
- package/cards/muscle-crew.yml +140 -100
- package/cards/omnissiah.yml +152 -98
- package/lib/client.js +589 -320
- package/lib/index.js +0 -16
- package/lib/preserve.js +11 -16
- package/lib/prompts.js +183 -110
- package/lib/rewrite.js +97 -58
- package/lib/routes.js +80 -51
- package/lib/store.js +19 -3
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,654 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.8.3] - 2026-10-11
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **The review panel is no longer cut off at the top of a conversation.** It
|
|
14
|
+
renders in the composer stack, whose seat is `flex: none` — so a tall panel does
|
|
15
|
+
not compress the composer, it pushes the whole stack **up**. With no transcript
|
|
16
|
+
above to scroll, the overflow simply left the viewport: starting a session and
|
|
17
|
+
rewriting immediately showed only the panel's title bar, and during 改写中 that
|
|
18
|
+
was all you got.
|
|
19
|
+
|
|
20
|
+
The panel is now bounded and scrolls **inside** itself, with the header pinned
|
|
21
|
+
so 关闭 stays reachable. It can be any height without ever pushing itself
|
|
22
|
+
off-screen.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **The composer menu names what the card actually declares.** "跟随卡片" was a
|
|
27
|
+
black box: the rewrite-mode and fidelity rows showed the same words whether the
|
|
28
|
+
card declared `llm` or `template`, `style` or `strategy`, so what would actually
|
|
29
|
+
run was invisible. They now read **跟随卡片(模型改写)** and
|
|
30
|
+
**跟随卡片(可加约束)**, and the folded summary line lists fidelity too — it is
|
|
31
|
+
the setting with real consequences.
|
|
32
|
+
|
|
33
|
+
- **The generated example set no longer repeats the generic rule.** The card
|
|
34
|
+
generator had been told, literally, to write `方块系统、合成表、生存模式` for
|
|
35
|
+
"写一个我的世界" — which is the *same* example the rewrite rule already carries,
|
|
36
|
+
so the set it produced was guaranteed to be redundant. The prompt now separates
|
|
37
|
+
the two explicitly: generic detail is already covered (do not write it),
|
|
38
|
+
character-specific demands are what this field is for. The bundled cards were
|
|
39
|
+
rewritten the same way — 欧姆弥赛亚 asks for a record left for those who come
|
|
40
|
+
after and refuses shortcuts; 硬邦邦 refuses a proof-of-concept handed over as a
|
|
41
|
+
deliverable.
|
|
42
|
+
|
|
43
|
+
## [0.8.2] - 2026-10-10
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
|
|
47
|
+
- **The card form now shows the second example set and the template.** 0.8.0
|
|
48
|
+
added both to the card format but left the settings form on the old shape, and
|
|
49
|
+
`save` writes the payload as the **whole card** rather than merging — so the
|
|
50
|
+
moment you edited a card's name, its implementation-detail example set was
|
|
51
|
+
deleted. The form now renders a textarea per intensity level for both sets, and
|
|
52
|
+
a template field for every mode.
|
|
53
|
+
|
|
54
|
+
- **A template is no longer lost when saving an llm card.** It was sent only for
|
|
55
|
+
a template-mode card, so an llm card that had one lost it on the next save —
|
|
56
|
+
and template mode is offered on every card, which is how that switch ended up
|
|
57
|
+
silently falling back to a model call.
|
|
58
|
+
|
|
59
|
+
- **The form's copy matches what the fields now mean:** the second set is labelled
|
|
60
|
+
"may add implementation detail", and the fidelity hint says the same instead of
|
|
61
|
+
the retired "behavioural constraints" wording.
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
- **A check against this whole class of bug.** For every card field added since,
|
|
66
|
+
the form must both read it and write it back into the payload — checking only
|
|
67
|
+
that the name appears somewhere would pass on the read alone, which is exactly
|
|
68
|
+
how the write-back went missing. Confirmed red when the write-back is removed.
|
|
69
|
+
|
|
70
|
+
## [0.8.1] - 2026-10-10
|
|
71
|
+
|
|
72
|
+
### Changed
|
|
73
|
+
|
|
74
|
+
- **"Constraints" now means what it should: concrete IMPLEMENTATION requirements.**
|
|
75
|
+
The strategy fidelity was read as permission to add behavioural framing ("do
|
|
76
|
+
not over-plan"), which is not what a constraint is. It now permits adding the
|
|
77
|
+
concrete features and limits a competent take on the request implies but the
|
|
78
|
+
user did not spell out — for "写一个我的世界": 方块系统、合成表、生存模式、存档.
|
|
79
|
+
|
|
80
|
+
The hard rules said the opposite: "never add, remove, widen or reinterpret a
|
|
81
|
+
requirement", while declaring themselves an override of everything. That
|
|
82
|
+
cancelled the permission and made the setting a no-op. They are now about
|
|
83
|
+
PROTECTING what the draft states — nothing stated may be removed, weakened or
|
|
84
|
+
contradicted — and explicitly leave ADDING to the fidelity block.
|
|
85
|
+
|
|
86
|
+
Both bundled cards' strategy sets now demonstrate real additions (边界用例、
|
|
87
|
+
三数取中、复现之证、入参校验) rather than tone advice.
|
|
88
|
+
|
|
89
|
+
### Known limitation
|
|
90
|
+
|
|
91
|
+
- **The local fact check cannot see an added requirement written in Chinese.**
|
|
92
|
+
compareFacts reports an addition only for a token it can prove is structural
|
|
93
|
+
— a path, number, flag or code span. 方块系统 and 生存模式 are bare words, so they
|
|
94
|
+
pass unreported, and the panel's "新增" line stays empty even when the rewrite
|
|
95
|
+
added them. The added detail is still visible in the editable result column,
|
|
96
|
+
which is where a reviewer would catch it.
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
## [0.8.0] - 2026-10-10
|
|
100
|
+
|
|
101
|
+
### Added
|
|
102
|
+
|
|
103
|
+
- **Every card now carries TWO example sets**, and which one the model is shown
|
|
104
|
+
depends on the fidelity you chose. `examplesByIntensity` demonstrates the voice
|
|
105
|
+
alone; `examplesByIntensityStrategy` demonstrates the voice **plus the
|
|
106
|
+
behavioural framing** the rewrite may add ("do not over-plan", "do not stop at
|
|
107
|
+
the first plausible cause").
|
|
108
|
+
|
|
109
|
+
This fixes a real failure. Raising fidelity used to show the model the
|
|
110
|
+
*voice-only* examples — every one of which merely added a greeting — so it had
|
|
111
|
+
nothing to imitate and invented padding: "写一个我的世界" came back as
|
|
112
|
+
"就想要一个我的世界", a restatement rather than a framing.
|
|
113
|
+
|
|
114
|
+
- **Every card now carries a `template`.** Template mode fell back to a model call
|
|
115
|
+
on every card, because none of them had one: the switch appeared on and the
|
|
116
|
+
model ran anyway. The template substitutes `{{input}}` verbatim, so the wrapper
|
|
117
|
+
supplies the mouth and the user supplies the request.
|
|
118
|
+
|
|
119
|
+
- **The card generator produces both sets and a template**, and refuses a card
|
|
120
|
+
missing any of them, naming which. `DRAFT_MAX_TOKENS` goes to 8192, because a
|
|
121
|
+
complete card is now roughly twice the size of one example set.
|
|
122
|
+
|
|
123
|
+
### Changed
|
|
124
|
+
|
|
125
|
+
- **`fidelity` is no longer a coin flip on a card with nothing to add.** The
|
|
126
|
+
bundled cards' strategy sets demonstrate concrete framing, so the setting
|
|
127
|
+
produces visibly different output instead of a louder restatement.
|
|
128
|
+
|
|
129
|
+
A card without `examplesByIntensityStrategy` still falls back to the voice-only
|
|
130
|
+
set, so cards written before this release keep working; only the generator
|
|
131
|
+
requires the new fields.
|
|
132
|
+
|
|
133
|
+
## [0.7.3] - 2026-10-10
|
|
134
|
+
|
|
135
|
+
### Fixed
|
|
136
|
+
|
|
137
|
+
- **"Constraints too" (fidelity: strategy) actually does something now.** The
|
|
138
|
+
hard rules said *"never add, remove, widen or reinterpret a requirement"* while
|
|
139
|
+
declaring themselves an override of **everything, including the character
|
|
140
|
+
description** — and the `strategy` permission to add behavioural framing was
|
|
141
|
+
written *before* them. The model obeyed the absolute prohibition, so flipping
|
|
142
|
+
the control changed nothing in the output: a setting that appeared to work and
|
|
143
|
+
did nothing.
|
|
144
|
+
|
|
145
|
+
The root cause is that **"constraint" was doing two jobs**. A message carries
|
|
146
|
+
(a) technical content — what is asked, under which files, numbers and limits —
|
|
147
|
+
and (b) behavioural framing — how the assistant should approach the work. The
|
|
148
|
+
hard rules now lock (a) absolutely and say explicitly that they do **not**
|
|
149
|
+
forbid (b); the fidelity block owns (b) and names what it may add.
|
|
150
|
+
|
|
151
|
+
- **Direct send closes its panel.** The message was sent correctly, but the panel
|
|
152
|
+
stayed up — and a settled panel sets `panelOpen`, which **swallows the
|
|
153
|
+
composer's send arrow**. The next thing you typed could not be sent until the
|
|
154
|
+
panel was dismissed by hand. Reported as "direct send works, but the review
|
|
155
|
+
card still shows up". Review mode still keeps its panel: that one *is* the
|
|
156
|
+
decision point.
|
|
157
|
+
|
|
158
|
+
A refused submit leaves the panel up, so the text is still reachable by hand.
|
|
159
|
+
|
|
160
|
+
## [0.7.2] - 2026-10-10
|
|
161
|
+
|
|
162
|
+
### Fixed
|
|
163
|
+
|
|
164
|
+
- **AI card drafting no longer writes the character's own line as the rewrite.**
|
|
165
|
+
Given "the AI is my mother, I am her child", the generator produced examples
|
|
166
|
+
where the **mother** spoke:
|
|
167
|
+
|
|
168
|
+
> `from`: 帮我把标题改成首页
|
|
169
|
+
> `to`: 乖,妈帮你把标题改成首页。 ← the MOTHER talking
|
|
170
|
+
|
|
171
|
+
That is the character answering, not the user asking — the rewrite is always
|
|
172
|
+
the **user's** message, so the card was unusable and the role was inverted.
|
|
173
|
+
|
|
174
|
+
The prompt was the cause: it said the later step rewrites the user's message
|
|
175
|
+
"into that voice", and that `to` is the request "rewritten in the voice". Both
|
|
176
|
+
leave *whose* voice open, and the generator resolved it to the character's.
|
|
177
|
+
|
|
178
|
+
The generator is now told plainly that **the output is always the user's
|
|
179
|
+
message and the character's own speech never appears**, with the direction
|
|
180
|
+
restated for both sides and a **worked wrong/right pair** using exactly this
|
|
181
|
+
case. The bundled cards already obeyed the rule — in both, `to` is the user
|
|
182
|
+
speaking (the client, the believer), never the character — so they are cited as
|
|
183
|
+
the standard.
|
|
184
|
+
|
|
185
|
+
## [0.7.1] - 2026-10-10
|
|
186
|
+
|
|
187
|
+
### Fixed
|
|
188
|
+
|
|
189
|
+
- **AI card drafting no longer runs out of output budget part-way through the
|
|
190
|
+
examples.** The draft call asked for **2048** output tokens, but a complete card
|
|
191
|
+
is bigger than that: each bundled card is ~1700 characters of CJK body, which
|
|
192
|
+
is well over 2000 tokens once the JSON wrapper and four intensity sets are
|
|
193
|
+
counted. `examplesByIntensity` is the longest field and the one written last, so
|
|
194
|
+
it was exactly what got cut. The budget is now 4096, matching the rewrite call.
|
|
195
|
+
|
|
196
|
+
- **A generated card must carry all four intensity sets, or the generation is
|
|
197
|
+
refused.** The parser accepted whatever rungs arrived, so a truncated answer
|
|
198
|
+
produced a card that looked complete in the form while two of its four settings
|
|
199
|
+
had **nothing to demonstrate** — the voice quietly vanished at those levels.
|
|
200
|
+
It now reports which levels are missing and asks the user to retry.
|
|
201
|
+
|
|
202
|
+
This was the second half of the same bug: raising the budget makes truncation
|
|
203
|
+
rare, and refusing the partial card means it can no longer pass unnoticed when
|
|
204
|
+
it does happen.
|
|
205
|
+
|
|
206
|
+
## [0.7.0] - 2026-10-10
|
|
207
|
+
|
|
208
|
+
### Fixed
|
|
209
|
+
|
|
210
|
+
- **AI card drafting no longer fails when the model appends a sentence.** The
|
|
211
|
+
recovery step that slices the outermost `{…}` out of the answer ran **only when
|
|
212
|
+
the text did not start with `{`**. A model that emitted clean JSON and then one
|
|
213
|
+
line of commentary — a very common habit — therefore skipped recovery and was
|
|
214
|
+
parsed whole, failing with:
|
|
215
|
+
|
|
216
|
+
> the model returned invalid JSON: Unexpected non-whitespace character after
|
|
217
|
+
> JSON at position 1051
|
|
218
|
+
|
|
219
|
+
Recovery now runs whenever the raw text does not parse on its own, so a leading
|
|
220
|
+
sentence, a trailing sentence, a code fence, or any combination of them all
|
|
221
|
+
survive. Text with no object in it is still rejected, and a legitimate object is
|
|
222
|
+
never rewritten on the way in.
|
|
223
|
+
|
|
224
|
+
### Removed
|
|
225
|
+
|
|
226
|
+
- **The flat `examples:` list, from the form and from the bundled cards.** It was
|
|
227
|
+
the fallback for a card whose per-intensity sets were missing, but
|
|
228
|
+
`examplesFor` prefers `examplesByIntensity` — so on the bundled cards, which
|
|
229
|
+
populate all four rungs, the flat list of four was **dead data** that still had
|
|
230
|
+
to be read and kept in sync. The two bundled cards carried 4 flat examples plus
|
|
231
|
+
8 per-intensity ones, and only the 8 ever ran.
|
|
232
|
+
|
|
233
|
+
A card file that still carries `examples:` is **still read** and still falls
|
|
234
|
+
back to it; the form simply no longer offers a second place to write the same
|
|
235
|
+
thing. Saving a card preserves any flat list it already had, because `save`
|
|
236
|
+
writes the payload as the whole card rather than merging — without that, editing
|
|
237
|
+
a legacy card's name would silently delete its examples.
|
|
238
|
+
|
|
239
|
+
## [0.6.4] - 2026-10-10
|
|
240
|
+
|
|
241
|
+
### Changed
|
|
242
|
+
|
|
243
|
+
- **The AI-draft box stopped saying the same thing three times.** The direction
|
|
244
|
+
rule was carried by the placeholder, restated in the description ("who the AI
|
|
245
|
+
plays, who you are, how they talk"), and then explained again in a paragraph
|
|
246
|
+
above the button. Three tellings of one instruction is not emphasis, it is
|
|
247
|
+
noise.
|
|
248
|
+
|
|
249
|
+
The paragraph is gone. The **placeholder** keeps the rule — it is the strongest
|
|
250
|
+
teaching signal in the box, and it demonstrates the direction in the shape of a
|
|
251
|
+
real example rather than describing it. The description is back to what the
|
|
252
|
+
form does: which card fields the AI fills.
|
|
253
|
+
|
|
254
|
+
The prompt layer still enforces the direction, and the card-authoring section
|
|
255
|
+
of the README still explains both sides in full; that is where an author
|
|
256
|
+
writing a card by hand needs it.
|
|
257
|
+
|
|
258
|
+
### Added
|
|
259
|
+
|
|
260
|
+
- **A check against restating the rule.** The direction must not reappear as a
|
|
261
|
+
separate paragraph, and the description must not repeat the placeholder. The
|
|
262
|
+
regression was confirmed to fail the suite.
|
|
263
|
+
|
|
264
|
+
## [0.6.3] - 2026-10-10
|
|
265
|
+
|
|
266
|
+
### Fixed
|
|
267
|
+
|
|
268
|
+
- **The character may be on EITHER side of the conversation, and the prompts no
|
|
269
|
+
longer pretend otherwise.** Both the rewrite prompt and the card generator
|
|
270
|
+
asserted flatly that *the character is the ASSISTANT's identity*. That is false
|
|
271
|
+
for half the cards, and the bundled set proves it:
|
|
272
|
+
|
|
273
|
+
| Card | Who is the character |
|
|
274
|
+
|---|---|
|
|
275
|
+
| 万机之神 · 欧姆弥赛亚 | the **AI** — the user addresses it as a petitioner |
|
|
276
|
+
| 肌肉集团 · 硬邦邦 | the **user** — "**the user is** the client", addressing their own team |
|
|
277
|
+
|
|
278
|
+
The rewrite prompt now states both cases, tells the model to take the direction
|
|
279
|
+
from the card's style text, and defines the assistant-side default for a card
|
|
280
|
+
that is genuinely silent. The generator is told the same, and takes the
|
|
281
|
+
direction from the idea it is handed.
|
|
282
|
+
|
|
283
|
+
- **The AI-draft direction note no longer states the wrong rule.** It said "你是
|
|
284
|
+
对角色说话的那个人,**不是角色本身**" — an absolute that contradicts 硬邦邦,
|
|
285
|
+
where you *are* the character. It now says the character can be on either side
|
|
286
|
+
and that picking one and saying so is all that is required.
|
|
287
|
+
|
|
288
|
+
The brief example and description keep asking for the same three things (who
|
|
289
|
+
the AI plays, who you are, how they talk) — the ambiguity was the bug, not the
|
|
290
|
+
two-sidedness.
|
|
291
|
+
|
|
292
|
+
## [0.6.2] - 2026-10-10
|
|
293
|
+
|
|
294
|
+
### Fixed
|
|
295
|
+
|
|
296
|
+
- **The AI-draft example now states the speaker direction.** The placeholder was
|
|
297
|
+
"一个脾气暴躁但极其可靠的老灯塔守夜人,说话简短、爱用航海比喻" — a character
|
|
298
|
+
and nothing else. It never said whether **you** play that character or the
|
|
299
|
+
**AI** does, which is precisely the ambiguity this plugin exists to avoid: the
|
|
300
|
+
character is the *assistant's* identity, and the message being rewritten is
|
|
301
|
+
always the *user's*.
|
|
302
|
+
|
|
303
|
+
The mismatch was sharpest because the generator's own system prompt **requires**
|
|
304
|
+
the style field to state the direction explicitly, while the example it was
|
|
305
|
+
handed omitted it. The example is the strongest teaching signal in that box, so
|
|
306
|
+
it now demonstrates what it demands:
|
|
307
|
+
|
|
308
|
+
> 例如:AI 扮演一位脾气暴躁但极其可靠的老灯塔守夜人,我是他的学徒;他说话简短、爱用航海比喻
|
|
309
|
+
|
|
310
|
+
The description asks for the same three things (who the AI plays, who you are,
|
|
311
|
+
how they talk), and the box carries a standing line stating the rule. Both
|
|
312
|
+
bundled cards already named it — 欧姆弥赛亚 is "a supplicant's prayer to the
|
|
313
|
+
Machine God", 硬邦邦 is "**the user** is the client".
|
|
314
|
+
|
|
315
|
+
- **The AI-draft copy said who does what, and what "the spec" actually is.**
|
|
316
|
+
It read "用一句话描述角色的思路,AI 会按同一套规范把表单填好" — with no
|
|
317
|
+
subject, so it was unclear whether *you* describe the character or the AI
|
|
318
|
+
does, and "同一套规范" pointed at an internal quality standard the user cannot
|
|
319
|
+
see or act on.
|
|
320
|
+
|
|
321
|
+
Both languages now name the actor and the fields: **you** write one sentence
|
|
322
|
+
about the character, and the AI fills the form by the character-card fields —
|
|
323
|
+
name, icon, description, style text, and a conversion example set per
|
|
324
|
+
intensity level. The hint also states the two things a generated card
|
|
325
|
+
deliberately pins: **fidelity is always voice-only** and **rewrite mode is
|
|
326
|
+
always model rewrite**, so a generated card never quietly acquires the power
|
|
327
|
+
to add constraints to your request.
|
|
328
|
+
|
|
329
|
+
### Added
|
|
330
|
+
|
|
331
|
+
- **Checks pin the direction into the brief.** The placeholder must demonstrate
|
|
332
|
+
who plays whom and must name the user's own role; the description must ask for
|
|
333
|
+
it; the rule must be stated in the box; and the copy must carry no markdown,
|
|
334
|
+
since the shell renders it as plain text. Confirmed red against the old
|
|
335
|
+
placeholder.
|
|
336
|
+
|
|
337
|
+
- **Checks pin that copy to the behaviour.** The description must name the four
|
|
338
|
+
fields the generator fills and must not fall back to "规范", and the generated
|
|
339
|
+
card must stay pinned to `fidelity: 'style'` / `mode: 'llm'` with the copy
|
|
340
|
+
saying so. Confirmed red against the old wording.
|
|
341
|
+
|
|
342
|
+
## [0.6.0] - 2026-10-10
|
|
343
|
+
|
|
344
|
+
### Changed
|
|
345
|
+
|
|
346
|
+
- **Fidelity is now free in both directions, on every card.** It used to be
|
|
347
|
+
one-way — a `strategy` card could be narrowed to `style`, but a `style` card
|
|
348
|
+
could never be widened — and the control was rendered only on `strategy` cards.
|
|
349
|
+
The reasoning was that a click on the way to Send should not grant the power to
|
|
350
|
+
add constraints to your request.
|
|
351
|
+
|
|
352
|
+
That reasoning does not hold up: **the card is the user's own file**, and the
|
|
353
|
+
settings page has always let them set `fidelity:` directly. Refusing in the
|
|
354
|
+
menu did not remove the capability, it only made the choice disappear from the
|
|
355
|
+
place where the work happens. The server rule is now symmetric, and all three
|
|
356
|
+
options — card default, voice only, constraints too — appear on every card.
|
|
357
|
+
|
|
358
|
+
The honest framing is kept: "constraints too" genuinely changes the request, so
|
|
359
|
+
the tooltip says so and points at review mode.
|
|
360
|
+
|
|
361
|
+
- **The settings fold no longer explains itself in permanent prose.** Every row
|
|
362
|
+
and every option now carries its explanation as a **tooltip** on the control.
|
|
363
|
+
The fold had grown to four rows plus six lines of standing explanation, which
|
|
364
|
+
is a wall of text for a menu whose job is picking a character.
|
|
365
|
+
|
|
366
|
+
- **Rewrite mode and fidelity now follow the character too**, not the session.
|
|
367
|
+
They were already displayed under the character while being stored per session,
|
|
368
|
+
which meant the setting you saw and the setting you got could disagree after
|
|
369
|
+
switching character. All four settings are now read from, and written to, the
|
|
370
|
+
card's own keys.
|
|
371
|
+
|
|
372
|
+
### Added
|
|
373
|
+
|
|
374
|
+
- **The check suite guards the READMEs.** The status line and the inspiration
|
|
375
|
+
link had both already rotted unnoticed, so three checks per README now pin the
|
|
376
|
+
current minor version, the video link and its author, and the named target
|
|
377
|
+
model.
|
|
378
|
+
|
|
379
|
+
## [0.5.2] - 2026-10-10
|
|
380
|
+
|
|
381
|
+
### Changed
|
|
382
|
+
|
|
383
|
+
- **The README's "what a persona changes" section now says where this came
|
|
384
|
+
from.** The old text was a flat disclaimer — personas do not raise the
|
|
385
|
+
capability ceiling, do not expect a performance switch. That is still true and
|
|
386
|
+
still stated, but it read like a warning label on a project whose whole point
|
|
387
|
+
is delight.
|
|
388
|
+
|
|
389
|
+
It now credits the source: [**“ds:好的,我现在是万机之神了(”**](https://www.bilibili.com/video/BV1Ata26pE92)
|
|
390
|
+
by 身居幻想乡の长乐安生 — a 30-second video whose comment section describes the
|
|
391
|
+
effect better than any spec sheet ("another AI is you pretending to be Guan
|
|
392
|
+
Yu; when Big Fat Fish pretends, she really rides out and cuts down Hua
|
|
393
|
+
Xiong"). It also names **DeepSeek Flash** as the model this is tuned for,
|
|
394
|
+
which was never said anywhere.
|
|
395
|
+
|
|
396
|
+
Both READMEs also carried a stale `0.3.x` status line; they now state `0.5.x`.
|
|
397
|
+
|
|
398
|
+
### Added
|
|
399
|
+
|
|
400
|
+
- **The check suite now guards the READMEs.** The status line and the
|
|
401
|
+
inspiration link had both already rotted unnoticed, so three checks per README
|
|
402
|
+
now pin the current minor version, the video link and its author, and the
|
|
403
|
+
named target model.
|
|
404
|
+
|
|
405
|
+
## [0.5.1] - 2026-10-10
|
|
406
|
+
|
|
407
|
+
### Changed
|
|
408
|
+
|
|
409
|
+
- **"More settings" is gone; everything it held moved into the character's own
|
|
410
|
+
fold.** Having two folds split one idea across two places — intensity and send
|
|
411
|
+
mode under the character, rewrite mode and fidelity somewhere else — so
|
|
412
|
+
**rewrite mode** and **fidelity** now sit in the same **"Settings for
|
|
413
|
+
<name>"** fold as the rest. The folded row's summary names the intensity, the
|
|
414
|
+
send mode and the rewrite mode, so the whole configuration is legible without
|
|
415
|
+
opening it.
|
|
416
|
+
|
|
417
|
+
- **Every rewrite mode now says what it does.** The segmented control named
|
|
418
|
+
three options and explained none of them, so the difference between a model
|
|
419
|
+
rewrite and a template was something you had to guess:
|
|
420
|
+
|
|
421
|
+
- *Card default* — use whatever this card declares on the settings page.
|
|
422
|
+
- *Model rewrite* — calls the model to rewrite your draft in the character's
|
|
423
|
+
voice. Most flexible; costs one model call.
|
|
424
|
+
- *Template* — fills your draft into the card's preset template with no model
|
|
425
|
+
call. No latency, no tokens, but the voice is fixed.
|
|
426
|
+
|
|
427
|
+
### Removed
|
|
428
|
+
|
|
429
|
+
- **"Open card directory" left the rewrite menu.** The settings page already has
|
|
430
|
+
an *Open directory* button, so the menu row was a second door to the same room
|
|
431
|
+
— and it put occasional housekeeping in the path of choosing a character.
|
|
432
|
+
|
|
433
|
+
## [0.5.0] - 2026-10-10
|
|
434
|
+
|
|
435
|
+
### Changed
|
|
436
|
+
|
|
437
|
+
- **Intensity and send mode now belong to the CHARACTER, not to the session.**
|
|
438
|
+
"硬邦邦 always goes out Zealot, un-reviewed" is a property of that character
|
|
439
|
+
rather than a choice about one conversation, so both are stored per card:
|
|
440
|
+
configure it once and picking that card in another session gives you the same
|
|
441
|
+
settings.
|
|
442
|
+
|
|
443
|
+
They also moved out of the shared block above the list and under the card they
|
|
444
|
+
apply to, as a folded **"Settings for <name>"** row. Sitting above every card,
|
|
445
|
+
they read as global switches for the whole menu — and switching character
|
|
446
|
+
silently carried the previous card's intensity over. The fold's summary line
|
|
447
|
+
shows the current rung and send mode, so the state is visible without opening
|
|
448
|
+
it.
|
|
449
|
+
|
|
450
|
+
Switching character now **re-reads that card's settings**; without that the
|
|
451
|
+
previous card's values stayed on screen and were then written to the new
|
|
452
|
+
card's key.
|
|
453
|
+
|
|
454
|
+
- **Picking a character no longer closes the menu.** Choosing a character and
|
|
455
|
+
configuring it are one act, and closing on pick forced a reopen to touch
|
|
456
|
+
intensity at all.
|
|
457
|
+
|
|
458
|
+
- **The card directory moved into "More settings".** It is housekeeping you do
|
|
459
|
+
occasionally, not something to scroll past on the way to choosing a character.
|
|
460
|
+
|
|
461
|
+
## [0.4.1] - 2026-10-10
|
|
462
|
+
|
|
463
|
+
### Removed
|
|
464
|
+
|
|
465
|
+
- **The model-based preservation check.** It made a second model call on every
|
|
466
|
+
rewrite to catch what the free token comparison cannot see — a requirement
|
|
467
|
+
reworded weaker, a constraint added in prose. It is gone, along with its
|
|
468
|
+
prompt, its verdict parser and every wire field (`factCheck`, `modelCheck`).
|
|
469
|
+
|
|
470
|
+
It doubled the latency and the token cost of every send to re-check what the
|
|
471
|
+
local comparison answers for nothing, and the settings page never offered a
|
|
472
|
+
working switch for it — a documented feature with no reachable control. For
|
|
473
|
+
the residue it was meant to cover, the rewrite column is now editable, which
|
|
474
|
+
is more direct than another round trip.
|
|
475
|
+
|
|
476
|
+
The retired `factCheck` config key is **ignored rather than rejected**, so a
|
|
477
|
+
profile still carrying it loads unchanged and simply makes no second call.
|
|
478
|
+
|
|
479
|
+
### Fixed
|
|
480
|
+
|
|
481
|
+
- **Rewriting in one session no longer makes every other session look busy.**
|
|
482
|
+
The composer button read the panel's phase **globally**, so while any single
|
|
483
|
+
session was rewriting, every open composer rendered "改写中…". The panel is one
|
|
484
|
+
shared surface, so its phase is global — but what the button shows must be
|
|
485
|
+
scoped to its own session. It now reads the session-filtered panel, the same
|
|
486
|
+
value the send guards already used.
|
|
487
|
+
|
|
488
|
+
## [0.4.0] - 2026-10-10
|
|
489
|
+
|
|
490
|
+
### Added
|
|
491
|
+
|
|
492
|
+
- **The rewrite column is editable.** What the model wrote is a starting point,
|
|
493
|
+
not a verdict: fixing a dropped identifier or a tone that is slightly off by
|
|
494
|
+
hand is faster than another round trip. **Send** and **Fill** carry the
|
|
495
|
+
edited text, and the panel says when the text differs from what the model
|
|
496
|
+
produced, so nothing goes out silently different.
|
|
497
|
+
|
|
498
|
+
- **While the review panel is open, the composer's send arrow is inert.** The
|
|
499
|
+
panel is the decision point. Pressing send with it up either pushed the
|
|
500
|
+
un-reviewed draft out or started a **second** rewrite of the same draft —
|
|
501
|
+
the duplicate that appeared once the panel was on screen. Closing the panel,
|
|
502
|
+
or choosing an action in it, re-arms the composer. Direct-send mode is
|
|
503
|
+
unaffected: it submits programmatically rather than through a gesture.
|
|
504
|
+
|
|
505
|
+
### Removed
|
|
506
|
+
|
|
507
|
+
- **The "Fill only" button, from the normal flow.** With an editable result,
|
|
508
|
+
writing the text into the composer without sending is the same act as
|
|
509
|
+
reviewing it, so the button was a second control for one idea. It survives
|
|
510
|
+
only for a shell that exposes no submit action, where it is the sole way to
|
|
511
|
+
get the text out.
|
|
512
|
+
|
|
513
|
+
### Fixed
|
|
514
|
+
|
|
515
|
+
- **An edited result is no longer rewritten again.** `apply` recorded only the
|
|
516
|
+
untouched original as "already rewritten", so sending a hand-edited result
|
|
517
|
+
from the composer came back as a fresh rewrite of the user's own edit. Any
|
|
518
|
+
settled text is now recorded. *Restore original* still deletes the memory
|
|
519
|
+
outright, because that is a rewind rather than a settled result.
|
|
520
|
+
|
|
521
|
+
## [0.3.5] - 2026-10-10
|
|
522
|
+
|
|
523
|
+
### Fixed
|
|
524
|
+
|
|
525
|
+
- **The same sentence typed twice is rewritten both times.** The send guard
|
|
526
|
+
skipped a rewrite when the draft matched the last rewrite's result **or** its
|
|
527
|
+
original text. Restoring the original already clears that memory, so matching
|
|
528
|
+
`original` bought nothing and silently disabled the persona for any repeated
|
|
529
|
+
draft — type "讲个笑话", send it, type it again, and it went out
|
|
530
|
+
un-rewritten.
|
|
531
|
+
|
|
532
|
+
- **Interception can no longer disarm itself.** The send listeners were bound
|
|
533
|
+
to the composer card, found through the plugin's own button. A card that was
|
|
534
|
+
not found meant the whole feature silently did not arm — the plugin looked
|
|
535
|
+
dead, with no error anywhere. The listeners now sit on `window` and each
|
|
536
|
+
guard scopes its event to the composer card it belongs to, so a miss costs
|
|
537
|
+
one event rather than the feature.
|
|
538
|
+
|
|
539
|
+
### Changed
|
|
540
|
+
|
|
541
|
+
- **The send-gesture decision is now a pure function** (`decideSend`) returning
|
|
542
|
+
`{ action, reason }`, and the client exposes `__internals` so the check suite
|
|
543
|
+
can boot the real bundle and drive it. This decision fails **silently** in
|
|
544
|
+
both directions — swallow a gesture that should have been sent and the
|
|
545
|
+
message vanishes; let one through that should have been rewritten and the
|
|
546
|
+
persona quietly does nothing — and neither throws, so the only symptom is "it
|
|
547
|
+
stopped working". Thirteen behavioural cases now cover it, replacing
|
|
548
|
+
source-shape assertions. The repeated-draft case was confirmed to fail
|
|
549
|
+
against the old predicate.
|
|
550
|
+
|
|
551
|
+
The pure-guard-plus-seam approach follows
|
|
552
|
+
[`dsh-prompt-hardener`](https://github.com/ffyfox/dsh-prompt-hardener), which
|
|
553
|
+
reached the same conclusion about this class of silent failure.
|
|
554
|
+
|
|
555
|
+
## [0.3.4] - 2026-10-10
|
|
556
|
+
|
|
557
|
+
### Fixed
|
|
558
|
+
|
|
559
|
+
- **One wedged rewrite no longer freezes the plugin for good.** The review
|
|
560
|
+
panel is a single shared surface, but the guards around it were global while
|
|
561
|
+
the send interceptor guarded per session. A single rewrite left stuck at
|
|
562
|
+
`loading` therefore poisoned everything after it: `start()` hit its global
|
|
563
|
+
`busy` guard and **silently did nothing** — no rewrite, no error — while in
|
|
564
|
+
another session the interceptor let the gesture through to the native send.
|
|
565
|
+
Reported as "after it hangs, it just sends my text and the plugin never
|
|
566
|
+
intercepts again".
|
|
567
|
+
|
|
568
|
+
- `runRewrite` now refuses only a concurrent run for the **same** session, so
|
|
569
|
+
one wedged session cannot block another.
|
|
570
|
+
- `start()` guards on the **per-session** busy, matching `runRewrite` and the
|
|
571
|
+
interceptor. A send gesture is now either rewritten or sent; it is never
|
|
572
|
+
swallowed into nothing.
|
|
573
|
+
- A client-side watchdog settles the panel after 135s even if the host never
|
|
574
|
+
answers. The host's own deadline is not enough: this half must stay usable
|
|
575
|
+
without depending on the other half being alive.
|
|
576
|
+
|
|
577
|
+
## [0.3.3] - 2026-10-09
|
|
578
|
+
|
|
579
|
+
### Fixed
|
|
580
|
+
|
|
581
|
+
- **A rewrite now runs on the model you picked, even before you send with
|
|
582
|
+
it.** Switching models in the composer and then rewriting used the
|
|
583
|
+
**previous** model: the route came only from the session's last request
|
|
584
|
+
header, which does not advance until a message is actually sent. Observed
|
|
585
|
+
live as the composer showing `hy4-preview` while the rewrite ran on a stale
|
|
586
|
+
`workbuddy-xdpool-global / deepseek-v4.1-flash` route.
|
|
587
|
+
|
|
588
|
+
The host's own precedence, in `api-session`'s `selectionFor`, is
|
|
589
|
+
**pending → request header → harness default**; the plugin implemented only
|
|
590
|
+
the last two. It now reads the `modelSelection` projection's `pending` first,
|
|
591
|
+
so a dropdown pick takes effect immediately. Every step is still optional and
|
|
592
|
+
falls through to the next, so a harness without that projection behaves
|
|
593
|
+
exactly as before.
|
|
594
|
+
|
|
595
|
+
- **A rewrite whose model never answers now fails instead of hanging
|
|
596
|
+
forever.** `for await (const chunk of llm.stream(...))` only exits when the
|
|
597
|
+
adapter stops yielding. A provider that accepts the request and then sends
|
|
598
|
+
nothing — a rate-limited model — yields no chunk, so the 120s deadline's
|
|
599
|
+
`abort()` was invisible to the loop and the call never returned: the panel
|
|
600
|
+
spun with no result, no error, and no timeout.
|
|
601
|
+
|
|
602
|
+
Each pull is now raced against the deadline, so the loop leaves on its own
|
|
603
|
+
and reports `TIMEOUT` instead of waiting on a stream that will never speak.
|
|
604
|
+
The stream is abandoned rather than drained, because draining a wedged
|
|
605
|
+
provider is the hang this guards against.
|
|
606
|
+
|
|
607
|
+
Two regression tests cover both, and both were confirmed to fail against the
|
|
608
|
+
old code: the route test goes red, and the deadline test hangs the suite
|
|
609
|
+
outright (`Detected unsettled top-level await`) — the reported symptom.
|
|
610
|
+
|
|
611
|
+
## [0.3.2] - 2026-10-09
|
|
612
|
+
|
|
613
|
+
### Changed
|
|
614
|
+
|
|
615
|
+
- **The rewrite stops naming a reasoning effort, so it works again on routes
|
|
616
|
+
that declare none.** An earlier attempt to speed slow rewrites up pinned
|
|
617
|
+
`reasoningEffort: 'low'`; on `workbuddy-xdpool` / `hy4-preview` every rewrite
|
|
618
|
+
then failed outright with
|
|
619
|
+
`does not support reasoning effort "low"`.
|
|
620
|
+
|
|
621
|
+
The harness validates the field before it reaches any provider: when a
|
|
622
|
+
route's model declares **no** reasoning metadata, **any present value**
|
|
623
|
+
throws — including `''`, which is the host's own "Provider default" sentinel
|
|
624
|
+
in the ACP session-config layer, and including `'off'`. Absence is therefore
|
|
625
|
+
the only portable way to ask for the route's default, and the call now leaves
|
|
626
|
+
the field out.
|
|
627
|
+
|
|
628
|
+
The latency this was meant to fix is **not** fixed by it. On a reasoning
|
|
629
|
+
model the rewrite may still spend thinking time before it emits anything; the
|
|
630
|
+
120s deadline and the elapsed-seconds readout from 0.3.1 remain the honest
|
|
631
|
+
answer there.
|
|
632
|
+
|
|
633
|
+
## [0.3.1] - 2026-10-09
|
|
634
|
+
|
|
635
|
+
### Fixed
|
|
636
|
+
|
|
637
|
+
- **A rewrite waiting on a slow model no longer reads as frozen.** The panel
|
|
638
|
+
showed 改写中 with no sense of time, so a slow rewrite was indistinguishable
|
|
639
|
+
from a hang. The host has had a 120s deadline all along, so the panel was
|
|
640
|
+
never stuck forever — but two minutes of a silent spinner looks exactly like a
|
|
641
|
+
freeze. It now ticks elapsed seconds, and past 45s says the model may just be
|
|
642
|
+
slow and how to give up.
|
|
643
|
+
|
|
644
|
+
### Removed
|
|
645
|
+
|
|
646
|
+
- **The settings page's model-check checkbox.** It duplicated the plugin row's
|
|
647
|
+
own `factCheck` config — a second setting for the same thing, sitting on a
|
|
648
|
+
page that is about cards. The host config is the single control and already
|
|
649
|
+
defaults to off. The checkbox, its dictionary keys and the client-side
|
|
650
|
+
preference behind it are gone.
|
|
651
|
+
- **The permanent drift-check instruction line.** The button's tooltip already
|
|
652
|
+
carried the same sentence. The page now renders only the check RESULT, which
|
|
653
|
+
is real feedback rather than the same copy twice.
|
|
654
|
+
- Three dictionary keys no route or component referenced:
|
|
655
|
+
`settings.ai.briefLabel`, `settings.changed`, `error.busy`.
|
|
656
|
+
|
|
9
657
|
## [0.3.0] - 2026-10-09
|
|
10
658
|
|
|
11
659
|
### Added
|
|
@@ -325,7 +973,29 @@ First public release.
|
|
|
325
973
|
tools, and never enters the session log. What the session records is the text
|
|
326
974
|
the user actually sent.
|
|
327
975
|
|
|
328
|
-
[Unreleased]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.3
|
|
976
|
+
[Unreleased]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.8.3...HEAD
|
|
977
|
+
[0.8.3]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.8.2...v0.8.3
|
|
978
|
+
[0.8.2]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.8.1...v0.8.2
|
|
979
|
+
[0.8.1]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.8.0...v0.8.1
|
|
980
|
+
[0.8.0]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.7.3...v0.8.0
|
|
981
|
+
[0.7.3]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.7.2...v0.7.3
|
|
982
|
+
[0.7.2]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.7.1...v0.7.2
|
|
983
|
+
[0.7.1]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.7.0...v0.7.1
|
|
984
|
+
[0.7.0]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.6.4...v0.7.0
|
|
985
|
+
[0.6.4]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.6.3...v0.6.4
|
|
986
|
+
[0.6.3]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.6.2...v0.6.3
|
|
987
|
+
[0.6.2]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.6.0...v0.6.2
|
|
988
|
+
[0.6.0]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.5.2...v0.6.0
|
|
989
|
+
[0.5.2]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.5.1...v0.5.2
|
|
990
|
+
[0.5.1]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.5.0...v0.5.1
|
|
991
|
+
[0.5.0]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.4.1...v0.5.0
|
|
992
|
+
[0.4.1]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.4.0...v0.4.1
|
|
993
|
+
[0.4.0]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.3.5...v0.4.0
|
|
994
|
+
[0.3.5]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.3.4...v0.3.5
|
|
995
|
+
[0.3.4]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.3.3...v0.3.4
|
|
996
|
+
[0.3.3]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.3.2...v0.3.3
|
|
997
|
+
[0.3.2]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.3.1...v0.3.2
|
|
998
|
+
[0.3.1]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.3.0...v0.3.1
|
|
329
999
|
[0.3.0]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.2.1...v0.3.0
|
|
330
1000
|
[0.2.1]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.2.0...v0.2.1
|
|
331
1001
|
[0.2.0]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.1.2...v0.2.0
|