FireOpalus/dsh-laa ↗★ 1

dsh-laa

DSH 插件:为每个会话提供 LAA 模式——DeepSeek 峰时自动停止运行,谷时自动继续运行。 适合可延后执行的任务,按DeepSeek峰谷计价时段暂停并恢复会话及其子代理。

パッケージ
dsh-laa
互換性
未検証
バージョン
0.6.0
ライセンス
MIT
最終更新
2026/09/17

インストール

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:FireOpalus/dsh-laa

ドキュメント

README 全文を読む ↗

使用

LAA 模式是每个会话各自的开关,默认关闭。

命令作用
/laa查看状态:开关、当前是峰时还是谷时、下一次切换、峰时窗口、待恢复的输入数量
/laa on打开该会话的 LAA 模式
/laa off关闭;当刻就把压着的输入发出去(被中断过的轮次会先收到一条「LAA 已关闭」的续跑提示词),命令会报告发了多少、还有几个会话在等实时 agent
/laa status等同于 /laa

命令直接从 UI 作用到会话,不产生任何模型消息,因此开关本身不花 token。

Web UI 里还有第二种入口:滑动开关(轨道 + LAA 标签)。单击即切换当前会话的 模式;悬停显示当前峰谷时段、下一次切换、峰时窗口与待恢复的输入条数;开启且正处峰时 的时候轨道会变成琥珀色,一眼就能看出"它现在正被按住"。命令、开关、状态文件读的是 同一份快照,因此三者永远一致。

开关的位置跟着页面走,页面上任何时刻都恰好有一个:

页面位置
已有对话的会话会话页顶栏右侧(DSH 的 conversation.session.header.utilities)
新对话页(还没有第一条消息)输入框工具行的左侧(conversation.input.left),就在权限选择与 Plan 旁边

DSH 在会话还没有第一条消息时会把整条顶栏藏起来(连标题带工具区),顶栏里的开关因此 根本不会挂载——这就是为什么新对话页需要第二份。它只在顶栏确实不显示时渲染,所以新 对话页、以及正在打开的会话里都点得到,可以在发出第一条消息之前就把 LAA 打开。

典型用法:晚上下班前对一个长任务说「继续做」,然后点一下那个开关(或 /laa on)—— 峰时它会自己停住,谷时(工作日 12:00-14:00、18:00 之后、以及整个周末)它会自己接着做。

子会话(subagent 会话)

LAA 模式属于会话树,而不是单个会话:DSH 的 subagent 子会话(会话 header 里带 origin: 'subagent' 与 parentSession,在侧栏里嵌套显示的那些)没有自己的模式, 它跟随自己所属的顶层会话。于是父会话开着的时候子会话也开着、父会话关掉的时候子会话 立刻恢复放行,整棵树永远只有一个开关:

动作结果
父会话开 LAA子会话也受管:峰时子会话同样被拦下,一个模型请求都不会发出
父会话关 LAA子会话立刻恢复放行
在子会话页点开关 / /laa on|off改的是父会话(命令会明确告诉你改的是哪个会话)
子会话的悬停提示多一行「跟随父会话」,写着父会话的标题与 id

但遗留工作记在会话自己名下:峰时被拦下的输入、被中断的轮次都属于产生它们的那个 会话,谷时回到那个会话自己的 agent 上继续跑——子会话的输入不会被投递到父会话去。

峰谷切换时的动画与通知

峰谷真的翻过去的那一刻(宿主在边界后重新评估相位,前端按快照里的 nextChangeAt 对齐去问),页面上的开关:

  1. 跳三下(prefers-reduced-motion 下自动不播动画);
  2. 开着 LAA 的时候,轨道颜色用 1.26 秒从绿平滑推到琥珀(或反过来)——平时 0.2 秒的 换色只够「变」,不够「看得出它变了」;
  3. 弹一条浏览器通知(LAA · 进入峰时 / LAA · 进入谷时),标签页在后台也收得到; 同一个会话的播报互相顶替,不会堆一屏。

通知要授权:第一次点开关时顺手申请一次(浏览器要求用户手势);拒绝授权、或者浏览器 没有这个 API,都只是没有通知,开关照常工作。播报范围由「配置」里的 notify 决定。

前端只按宿主给的边界对表,不在前端自己算第二套时间;标签页或系统休眠期间错过的切换, 由 20 秒的兜底轮询追平。

这层关系落盘在 $DSH_HOME/laa/state.json(lineage 字段,来自会话 header 的 parentSession),所以进程重启、或者子会话当前没有实时 agent 时,开关与 /laa 依旧显示父会话的那一份状态。

插件见过的子会话会随手记下;装插件之前就存在、之后一直冷着的子会话则由控制面在 第一次收到请求时,向宿主的会话清单(ctx.sessionQuery.listSessions(),也就是侧栏 在用的那一份)补一次谱系——每个进程只补一次,读失败只是退回原来的判断。


配置

编辑 profile 的 cordis.patch.yml(或直接改本包自带的 cordis.patch.yml):

- id: laa            # 注意:patch 是【整体替换】config,不是逐字段合并
  config:
    enabled: true
    timeZone: 'Asia/Shanghai'
    peakWindows:
      - days: [mon, tue, wed, thu, fri]
        start: '09:00'
        end: '12:00'
      - days: [mon, tue, wed, thu, fri]
        start: '14:00'
        end: '18:00'
    defaultMode: false
    peakProviders:
      - deepseek-official
    cancelRunningOnPeak: true
    resumeOnValley: true
    tickMs: 30000
    maxDeferred: 20
字段默认含义
enabledtrue插件总开关。false 时插件照常加载,但任何会话都不会被停。
timeZone'Asia/Shanghai'峰时窗口所依据的 IANA 时区(DeepSeek 官方口径为北京时间)。
peakWindows见上峰时窗口数组,其余时段一律是谷时。days 接受 mon…sun 或 1(周一)…7(周日);start/end 为 HH:MM;end <= start 表示跨零点窗口。
defaultModefalse从未被 /laa 切换过的会话是否默认开启。显式关掉过的会话不会被它重新打开。
peakProviders['deepseek-official']哪些提供方路由按峰谷计费、因此受峰时约束;其余提供方在峰时不被阻塞。空数组表示谁都不拦(峰时约束失效,谷时重放语义保留)。
notify'enabled'峰谷切换时的浏览器通知策略:enabled(只有开着 LAA 的会话才播报)、always(任何会话都播报)、off(不播报)。只影响通知,开关上的切换动画不受影响。
cancelRunningOnPeaktrue峰时开始时是否中止正在跑的轮次。
resumeOnValleytrue谷时开始时是否自动续跑。
tickMs30000相位重新评估的兜底间隔;窗口边界本身总是会被精确唤醒。
maxDeferred20每个会话最多暂存多少条输入,超出时丢弃最旧的并记一条警告。
statePath$DSH_HOME/laa/state.json状态文件位置。

官方峰谷口径

据 DeepSeek 官方定价页 注 (3): 空闲时段价格为高峰时段价格的一半,高峰时段为北京时间周一至周五 09:00-12:00、 14:00-18:00,其余为空闲时段。折算成 UTC 即周一至周五 01:00-04:00 与 06:00-10:00。

于是:工作日 12:00-14:00、每天 18:00 之后、以及整个周末都是谷时,其中从 周五 18:00 到周一 09:00 是一段连续 63 小时的谷时。

窗口写在配置里而不是写死在代码里——DeepSeek 调整过峰谷口径(2025-02-26 的 16:30-00:30 UTC 方案已被 2026-08-17 生效的新口径取代),下一次调整只需要改配置。