Repository navigation
Releases: regrevia/dsh-fast-mode
Release list
0.7.0 — fast mode is per conversation
0.7.0
Fast mode was a global switch wearing a per-conversation costume: the bolt sits in
one composer, so clicking it read as "this conversation", and it changed every
conversation in the app. Two sessions side by side, one clicked, both accelerated —
and nothing in the UI said so.
- The switch and the gear are per conversation now. A click writes that
conversation's own{enabled, gear}entry; the panel says仅本会话生效/
This conversation onlyunder the switch, so the scope is stated where the
decision is made instead of being inferred. - A conversation with no entry inherits the shared pair, which doubles as the
record of the last choice made anywhere. A brand-new conversation therefore
starts from the last selection rather than from the schema default — the
behaviour asked for, and the reason the shared fields still exist. - Precedence is explicit: experiment assignment > that conversation's entry >
shared default. An assigned trial still outranks the UI, because a click that
quietly overrode the arm would empty the control group of the very trial the
user asked for. - One click, one patch. The shared
enabled/gearand thesessionsentry
travel in a singlesettings/updatecarrying the revision that was read. Two
writes would mean a window where the panel shows one gear and the request runs
on another, plus a second revision race; a refused write rolls back all three
fields and states the reason in the panel. - The column is bounded and forgiving.
sessionsholds at most 200 entries,
oldest pruned first byts(a pruned conversation just falls back to the
shared default). A hand-edited or truncated value degrades to "no overrides"
and never takes the chip down. - The two copies of the format are tested against each other. The host reads
the column (src/state.js), the browser half writes it (client.js), and a
drift between them is a switch that looks on and changes nothing — so the
shippedclient.jsis loaded in avmand fed the same fixtures as the host
module. Writing the test found one real drift (the browser copy accepted an
empty session id the host drops), which is now fixed on both sides. - The conversation id reaches the client half through the slot's own scoped
inject(sessionId); the chip also accepts a standardsessionIdprop, so a
release that stops injecting one spelling still behaves. - One caveat, stated where a user will hit it:
sessionsrenders as a text input
on the plugin settings page, because this build of schemastery has no
.hidden(). Its description says it is maintained automatically; editing it by
hand is how a conversation ends up running a gear nobody chose.
165 tests.
0.6.2 — 测量修好了:token 和步号是真的
0.6.2
The measurement was wrong in the one way that matters most: it counted time but
not tokens, and it lost which step each row belonged to.
- Read the event payload where it is.
session/eventcarries its payload in
event.data({turn, step, usage, …}); the recorder was readingevent.turn
andevent.usageoff the envelope, so every row from 0.6.0 and 0.6.1 had
turn/stepmissing andinput/outputat zero while timings looked fine.
Rows now carry step numbers and real token counts, and both the harness
spelling (inputTokens,reasoningTokens,cacheReadTokens) and the gateway
spelling (uncachedInputTokens,thoughtTokens,cachedReadTokens) are
folded. - Two regression tests feed the wrapped shape, so this cannot come back quietly.
- Rows already written to
~/.dsh/fast-mode-metrics.jsonlkeep their zeros;
delete the file or filter bytswhen comparing against new data.
147 tests.
0.6.1 — 实验有了真正的对照组
0.6.1
0.6.0 could compare gears, but not against the thing they are supposed to beat.
An assignment always switched the mode on, so a randomized trial had no baseline:
the only "off" sessions were ones somebody happened to run before starting the
experiment, which is not an arm — it is a convenience sample.
"off"is a legal arm. The assignment file and
dsh-fast-mode-experiment --gearsnow acceptoff, meaning the control group:
the session runs with the plugin switched off because the experiment said so,
not because a switch was left alone.offis always accepted even when the
options do not list it, and the CLI now defaults to
off,safe,fast,turbo,max, so a freshstartis a controlled trial rather than
a ranking.startsays which kind of run it wrote: withoffit promises a
baseline, without it it warns that the report can only rank arms against each
other.- The control arm is deliberately invisible to the switch. A session assigned
tooffreturns the request untouched even when the global switch is on — the
one place where the UI can disagree with what a session actually does, which
the experiment output and both READMEs now state plainly. - Also lands in this release:
bin/experiment.mjsis documented and exposed as
thedsh-fast-mode-experimentbin with an npm script, and--lanfollows
--sinceinto the dashboard's own1d/7d/30dwindow (default7d) rather
than always asking for one day and quietly printing nothing.
145 tests.
0.6.0 — 测量自己、按会话分档实验、终端报告
0.6.0
0.5.2 made the switch work. 0.6.0 makes the claim checkable: fast mode measures
itself, assigns gears to sessions for comparison, and reports the result from a
terminal. It also adds one gear above max, because on the official route that
is the only honest way past the 2× ceiling.
- Measurement is built in and on by default. A
session/eventtap folds
every step into~/.dsh/fast-mode-metrics.jsonl, one JSON line per completed
step, for sessions with the switch off as well as on — a comparison needs
both sides, and nobody remembers to enable measurement before an A/B. A row
carriesv, ts, sid, turn, step, mode(on|off|unknown),gear(oroff),
effort,route,ttftMs(step start → first assistant message),llmMs
(step start → last assistant message),stepMs(the whole step),calls,
errors,input,output,reasoning,cacheRead,cacheWriteand
assistantMessages.metrics: falsedisables it; a write failure is
swallowed and never fails a request. - Experiment assignment without a UI. An optional
~/.dsh/fast-mode-bench.json(benchmarkFile) assigns gears per session:
{"enabled": true, "gears": ["safe","fast","turbo","max"], "sessions": {"<session id>": "turbo"}}.
An explicit pin wins; otherwise the gear isFNV-1a(session id) mod gears.
The file is re-read on a 1 s TTL, so an edit applies to the next request, and
deleting it returns control to the configured switch. A session an experiment
assigns runs on that assignment even with the switch off — letting a forgotten
click decide the arm would silently put the session in the control group.
Unknown gear names are dropped with one warning. - The experiment has a command, not a JSON file you edit by hand.
bin/experiment.mjs(package bindsh-fast-mode-experiment) writes the
assignment file, reports how many sessions and steps each arm has collected so
far (status, including whether a disabled-state sample exists at all), and
refuses to overwrite a running experiment without--force. An experiment is
what makes the numbers trustworthy: the arm is chosen by the session-id hash,
not by whoever wants the better result. - A report command instead of a spreadsheet.
bin/report.mjs
(package bindsh-fast-mode-report) reads the JSONL and prints a per-stance
table plus a speedup table against theoffbaseline. The unit is the step;
session wall clock is not used, because it mostly measures the human. Flags:
--file,--since 2d|12h|90m,--json,--lan,--help.--lanjoins
local rows to the self-hosted usage dashboard by session UUID using
USAGE_TOKEN(optionalUSAGE_BASE): read-only, nothing leaves the LAN, and
the token is never printed. The dashboard's own window (1d/7d/30d)
follows--sinceinstead of always asking for one day. An unreachable,
slow or unauthorised dashboard degrades to one note; it never costs you the
local report. - New gear
ludicrous(极速), abovemax. It is the only gear whose floor
isoff, i.e. it may setreasoningEffort: 'off', with a fan-out target of 8
anddeepAfter: 1. That is not "a bit faster than max": the official route
declaresoff/low/high/max, so with thinking on the only legal cut is
high → low(≈2×) and 3–4× requires turning thinking off, which this gear
does. The panel therefore marks it极速*/Ludicrous*and the footer says
* 关闭思考,质量会下降. The plugin still never emits an id the route does
not declare — on a route withoutoff, the gear degrades to what the ladder
offers. - The chip is one bolt, and the panel is behind it. The caret and its inline
dropdown are gone. Clicking the bolt opens a panel holding the on/off switch,
the five gear rows (each with its estimated multipliers, the current one
marked) and the fine print, plus a status line that states the reason when a
write is refused. Clicking does not toggle; picking a gear while fast mode is
off still armsenabledin the same write. - The gear is a name.
gearis a schema union of
safe/fast/turbo/max/ludicrous; a number or a typo is a load-time error, as
src/config.jsalready documented. The policy layer still normalises
defensively, so a bad value can never become "unlimited", but it no longer
silently becomessafeeither. - Measurement paths are composition knobs.
metricsFileand
benchmarkFileare deliberately not.volatile(): they are read at load and
do not belong on a live form. Moving the JSONL is acordis.patch.ymledit.
Tests 142: the measurement tap proves off sessions are measured too (one row per
step, first-token vs last-message latency in a multi-message step, token and
error sums, per-session isolation, a missing directory created on first write,
an empty path disabling the sink); the assignment proves a stable hash that
covers every gear, an explicit pin, a single-session pin, a live 1 s re-read,
inertness without a file or with enabled: false, and one complaint for a
broken file; the report proves both tables, a one-sided refusal in words,
--since including the wider dashboard window, --json, --help reading no
file, --lan without a token and with one (the token is never printed), and an
unreachable dashboard degrading to a note; the client proves the contract after
the caret removal — one icon, clicking opens the panel and writes nothing by
itself, the switch writes once and a second click turns it back off, five rows
with estimates and an asterisk on ludicrous, and picking a gear while already
on leaves the toggle alone; and the schema proves an unknown gear name is a
load-time error, measurement is on by default, and exactly
enabled/gear/parallel are volatile.
v0.5.2 — 闪电真的能点了
0.5.1 shipped the switch but left it inert, and the reason was one wrong field
access:
- The remote protocol wraps every answer in
{ ok, value }. The chip read
remote.settings.describe().namespacesstraight off the envelope, so it always
gotundefined, classified this plugin as "not configured", and rendered a
disabledbutton. Chrome suppressestitleon a disabled button, so hovering
said nothing, and the click handler's guard swallowed every click — a control
that was fully explained inside the code and completely dead on screen.
unwrap()now handles the envelope (and a bare payload, so a service stub
still works),ok: falsesurfaceserror.message, and every remote call has a
deadline so a pending bridge becomes a visible error instead of an endless
"loading". - The chip is never disabled. Read-only, unknown and missing-namespace states
all stay clickable and open the menu with the reason in words. - The gear lives on the chip. The caret opens a dropdown listing the four
gears, each row showing its estimated speed and token multiplier
(速度 ~1.6× · token ~1.5×) and marking the current one. Picking a gear while
fast mode is off turns it on in the same write. There is deliberately no gear
form on the plugin page: this harness renders no generic schema form, so one
working entry point beats a second half-working one. - Host-side traceability. With
debugon,applywrites one line per load
before theenabledcheck (apply v0.5.2 entered enabled=… gear=…), then one
more saying it registered nothing; after two seconds it also reports whether
settings.describe()actually serves this namespace. A future "the chip does
nothing" report is now one log line away from an answer.
Tests 89: the client suite runs the real bundle against the real wire envelope,
including the bare-payload tolerance, the refused write, the read-only chip, the
missing namespace, and the gear multipliers.
v0.5.1 — 让开关真的能开关
0.5.0 的开关其实按不动
上一版把三个设置字段声明漏了 .volatile(),整个设置层因此是坏的:
settings.describe()只把带meta.volatile的字段投影进表单。一个都没有 ⇒ 这个条目从表单里直接消失:没有设置页,对话框里的闪电读成 "absent",一直灰着。- 就算点了,
settings.update()也会直接拒绝:Config field "enabled" is not volatile。
现在 enabled / gear / parallel 都是 volatile。这同时是"改一下立刻落到正在运行的 fiber、不重启插件"的机制本身。
同一个契约的另一半:volatile 字段传到插件里是冻结的引用({get()}),不是值。所以 config.enabled === true 在开关打开时仍然是 false。resolveConfig 现在在这一处统一解引用,同时继续接受普通对象。
测试 80 个:每个表单字段都要证明自己是 volatile,Loader 给来的引用必须被读成"已开启"。
升级
# 插件管理器里把目标改成
github:regrevia/dsh-fast-mode#v0.5.1装完重启一次应用(宿主只在启动时扫描 dsh.client,且新 schema 要重新加载),闪电才会出现。
v0.5.0 — 对话框里的闪电开关
默认关闭,点一下才开
0.4.0 的问题是装好即生效:你没法在界面上确认它在不在,也没法一眼看出它开没开。0.5.0 把开关放到对话框里,和模型选择器并排。
- 空心 = 关,实心黑 = 开。 装好后闪电是空心的,不带任何干预;点一下变实心,策略立刻生效。
- 只有字面
true才算开。 空配置、{}、{gear: 'max'}、{enabled: 'yes'}都不会注册任何 handler。 - 点击是乐观写入。 先按新状态画,再发一次带
revision的settings/update,然后轮询等宿主回显;被拒就回滚,并把原因写进 tooltip。 - 只读层 / 条目不存在:闪电变灰、不可点,不会假装能开。
- 同一个字段:闪电、插件设置页、
cordis.patch.yml写的都是enabled;applies: live让它不用重启就跟着变。只有 bundle 本身要刷一次页面——宿主只在启动时扫描dsh.client。
顺带
Config用 schemastery 声明三个值得出现在表单里的键(enabled、gear、parallel),其余调参键原样透传,不被剥离。- 新增 8 个测试:把发布出去的
client.js原样放进vm沙箱跑(桩__ModuleLoader__+ 假settings远端),验证空心/实心、一次带 revision 的写入、被拒回滚、只读禁用、缺少条目时不抛错。 - 测试总数 78,不需要网络。
# ~/.dsh/profiles/<profile>/cordis.patch.yml
- insert:
- id: fast-mode
enabled: true # 默认 false;等价于点对话框里的闪电
gear: fast # safe | fast | turbo | maxv0.4.0 — 档位、并行指令、插件图标
0.4.0 — 档位把"更快"变成一套姿态
插件现在装好即加速:默认档位 fast,三件事全开。想完全回到 0.3.0 的静默行为:gear: safe。
新增
- 四个档位:
gear: 1..4(safe/fast/turbo/max)。档位统一决定三件事——连续干净几次后允许降档(deepAfter6/4/3/2)、回看几条信号(window8/6/5/4)、每一步让模型批量并行几个独立调用(不注入 / 4 / 6 / 8)。floor在任何档位下都不越过。 - 面向模型的并行指令:档位 ≥ 2 时注册一段系统提示(
fast-mode:execution-speed,order 520),数字随档位或parallel变化,并守住三条规则(只并行独立调用、读写不同批、批内失败就缩回小步)。 - 调度上限告警:当
agentLoop.maxParallelToolCalls低于档位目标时,日志里直接给出要改的那行cordis.patch.yml,只报一次。 - 插件显示元数据:包根
icon.png(PNG,11,607 字节)+locale/zh.json/locale/en.json的meta.title/meta.description,插件管理器与设置页显示真实图标和中文文案。
文档
中文 README 成为默认(README.en.md 为英文),并把三件事的原理、官方 API 支持依据、与上下文压缩类插件的组合方式、全配置表、route= 日志含义和诚实的限制都写清楚了。
验证
npm test:64 个测试全通过,无网络、不需要 harness 运行时。- 一键安装实测:插件管理器目标
github:regrevia/dsh-fast-mode→ 装到 0.4.0、application: applied、无 warning。 - 运行确认:
/Users/apple/.dsh/fast-mode.log出现gear=fast行与high=>medium(五档自建网关)。
npm 上的同名包尚未发布,请用 github: 形式安装。