DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

vlln /

vlln/dsh-autofork

Verified

Agent 忙时自动分叉会话:你新发的指令立刻在新会话里得到响应,旧会话留在后台跑完并把结果回注(DeepSeek Harness 插件)

★ 2 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@c8beeb20

dsh-autofork

DSH 插件:agent 忙的时候你提交的新指令会立刻在新会话里得到响应,原来那条会话留在后台把活干完、再把结果回注过来。
Automatically forks the session when you type while the agent is busy — you keep talking, the old turn keeps working.

license format

这是什么

DSH 的自动分叉插件:你在 agent 忙的时候提交新指令,它会立刻把你放进一条新会话里继续说话;原来那条会话留在后台把这一轮跑完(绝不中止),跑完后把结果回注到你手上这条。

关键是"自动"。 官方 subagent / subagent_fork 要求 agent 自己决定要不要并行、fork 哪一段;这里的分叉是 harness 侧被动发生的 —— agent 不需要参与决策,也不会因此被打断。

为什么需要它

问题不在性能,在交互模型:一个 turn 常常十几分钟起步(调工具、长生成),而你现在只有 queue(等当前 turn 结束)或 steer(等下一个 step 边界)两条路,两者都是串行的——这期间你插不进话,一天就被切成很多段"什么都做不了"的碎片时间。

常见对策是同时开好几个 session:哪个在跑就切到另一个上读反馈、写下一条指令。但这要你自己记住每条会话在干什么——人的工作记忆做这种上下文切换很累,还会打断心流;而且精力有限,同时照看 3–5 条基本就到顶了。

dsh-autofork 要改的就是这个"必须等"的交互模型:把并行交给 harness,而不是交给你。你不必判断"要不要开子任务""该开几条""哪条在等",只要在想说话的时候说话——忙的那条自己在后台跑完、把结论交回来。要照看的会话数从"手动维护 3–5 条"降到"1 条"。

它不解决什么

它把"你被挡住"变成"你随时能说话",但不能消除"任务本身要等":真实工作常常有时间因果关系——下一条指令依赖当前这步的结论。那种等待是问题本身的性质,不是换一种交互模型能绕开的。

与相邻方案的区别

方案 谁发起 你要做什么
subagent / subagent_fork(官方) agent 自己决定并行、决定 fork 哪一段 不用管,但得等它想到
agent-team(官方 0.1.5-alpha.2 起) 管理者模式:共享任务区 + lead 身份 + 委派与通信 理解层级与任务区
dsh-autofork harness 被动发生:你插话就分叉 什么都不用做,正常说话即可

没有管理者、没有层级:你始终在跟"当前这个 agent"说话,身后被接管的会话都在后台跑完、把结果交上来;需要时它才用 fork_list 看整个家族。

安装

dsh plugin --profile web add github:vlln/dsh-autofork

装完重启 web(bundle 走层栈)。从本机目录安装(开发用,要一条 symlink 修法)见工程笔记。

用起来是什么样

只在 agent 忙的时候分叉:它空闲时(能立刻响应你)就是它自己回答,不会平白多出一条会话;只有它正跑着自己那一轮、来不及被打断时,你发的那条指令才会带出一条新会话。

一次分叉你会看到:

你会看到 说明
左侧列表冒出新会话 名字是 ⑂1 <原会话名>;同族在列表里成组(⑂1 / ⑂2 … 共用一个基名,序号不累加)
焦点被切到新会话 你立刻在跟一条空闲的 agent 说话;转写带着旧会话已完成部分,读起来是连续的
旧会话继续跑 它这一轮不中止;跑完后最后一条回复回注到你现在这条会话
多出一个「分叉」页签 本会话所在家族的血缘树:每行一条会话(名字 / 状态 / 触发它的那条指令 / 多久之前分的),点任意一行跳过去
注入行折叠成一行 分叉通知、在飞摘要、后台回注各占一行摘要,展开才看正文

再忙一次会接着分叉(A → B → C):最新的那条负责回答你,身后的会话都在后台跑完、把结果交上来。已经被接管的会话里你再发言,消息直接交给最新那条(不再分叉)。

工具

agent 自己会用到的三个工具(exposeDispatchTools: false 可关掉):

工具 说明
fork_list 列出你家族里的其它会话:↑ 你接管的、↓ 接管了你的、~ 同族的另一条分叉。每行带名字与一个实时状态(运行中 / 空闲 / 已结束)
fork_steer 给家族里的一条会话发中途指令(等价 steer):它在当前 step 结束后读到并改向,不必中止
fork_cancel 中止家族里的一条会话(keepInbox=true 只中止当前 turn、保留待处理项)

界面

位置 说明
「分叉」页签 会话视图区新增的一个 tab(与「对话」「轨迹」并列):本会话所在家族的血缘树。每行给四条信息——名字与序号徽章(⑂1)、这一行是几号 / 是不是家族根、此刻在干什么(运行中 / 空闲 / 已结束)、以及这次分叉是为什么、什么时候发生的(触发它的那条指令 + 3 分钟前)。点任意一行切过去;缩进表示它是从哪条分出去的
会话名 分叉出来的会话命名为 ⑂n <家族根名>(如 ⑂1 修 GUI 卡顿),同族在左侧列表里成组
注入行 分叉通知 / 在飞摘要 / 后台回注各占一行可折叠摘要(noticeSummaries: false 可关掉折叠)

参数

全部行为参数集中声明在 src/params.mjs,无硬编码;插件配置覆盖任意字段,未提供者取默认值,未知键被忽略。

参数 默认 含义
enabled true 总开关;关闭后完全退回 steer/queue
minStepAgeMs 0 分叉门槛:step 已运行低于此时长则不分叉。默认 0 = 只要忙就分叉(保守部署可调大)
coalesceWindowMs 2000 距下一个 step 边界不足此时长时降级为 steer(合并窗口)
forkableSourceKinds ['user'] 只对这些 message.source.kind 触发分叉
maxActiveBranches 3 同一前台下的活跃后台分叉上限;超出强制 steer
harvestAfterMs 600000 已回注分叉多久后视为已收割并移出计数
digestMaxChars 8000 in-flight digest 整体字符上限
digestAssistantChars 600 单条助手文本截断长度
digestToolArgsChars 300 单条工具调用参数截断长度
digestToolResultChars 400 单条工具结果截断长度
digestMaxEvents 200 digest 最多渲染事件数(自尾部保留最近)
reInjectOnTurnEnd true 后台 turn 结束后是否回注前台
reInjectMaxChars 4000 回注内容字符上限
wakeForegroundOnTurnEnd true 前台 idle 时用 followup 唤醒,否则只 inject
containerMode false 容器模式(实验,默认关):容器不跑 turn、消息交给绑定实例、答复写成一等回复。不要打开:容器不可能完全不跑 turn,这条模式会与实例重复劳动
rebindNotice true 容器模式下首次绑定 / 重绑 driver 时,在容器里插一行折叠通知
followHead true 是否把焦点切到新分叉。默认 true = 立刻在跟一条空闲 agent 说话(「打破同步交互」的前提);false 留作对照:只看产出、视图不移动
instanceLabel '分叉' 实例在会话头部实例树里的显示名
titleMark '⑂' 分叉会话的命名标记:建 head 时把标题钉成 <标记><序号> <家族根名>(如 ⑂1 修 GUI 卡顿)。序号不累加——同族共用一个基名、各自编号,所以左侧列表里同族成组。走原生 sessionTitle.rename()(与手动重命名同一条路),代价是这条会话不再被自动命名。空串 = 关闭分叉命名
instanceProvider 'fork' 写进子会话 subagent/descriptor 的 provider(只用于冷恢复反查后端)
mirrorInstanceReply false 是否把实例的回复反向追加进容器日志。默认关:方向错了(笔记定的是"旧会话的结果注入回新会话"),那是 reInjectOnTurnEnd 干的
mirrorDelivery 'safe' 实例回复的投递方式:'safe' 避开悬空 tool_calls(必要时延到下一个 step 起点);'immediate' 直接 append(已知会写坏容器转写,仅作对照)
noticeSummaries true 注入消息是否声明 form:'notice'——客户端把它折叠成一行摘要(可展开),不折叠则整块显示
exposeDispatchTools true 是否向前台暴露 fork_list / fork_steer / fork_cancel
debugLogPath '' 决策日志路径(逐行 JSON);空表示关闭,可用环境变量 DSH_AUTOFORK_DEBUG 兜底

渲染预算不足时,digest 显式标注丢弃量(因字符预算未渲染 / 更早的 N 个事件),不做静默截断。

环境变量

两个都不改出厂默认值,优先级低于插件配置:

# 覆盖任意参数(JSON 对象;非法 JSON 与非对象值一律忽略,绝不会让插件起不来)
DSH_AUTOFORK_PARAMS='{"minStepAgeMs":0}' dsh web
# 打开决策日志(逐行 JSON:每条判定与早退原因)。**"为什么没分叉"先看它**——早退默认是静默的
DSH_AUTOFORK_DEBUG=/tmp/dsh-autofork-debug.log dsh web

自检

GET /api/dsh-autofork/health[?sessionId=<id>] 返回插件参数、已注册工具与家族状态(nodes 就是「分叉」页签的数据源,driver 是当前回答你的那条会话)。

它也是"插件到底装上没有"的唯一可靠判据:DSH 的 ctx.logger.info 不写 stdout,dsh plugin list 也只转发安装器。

已知限制

  • 分叉出来的会话是普通会话,不会出现在原生「子代理」实例树里 —— 这换来的是"用户所在的会话不变、内容换成新的"。家族关系由「分叉」页签与 fork_list 表达,重启不丢。
  • 命名会钉住标题:走的是与手动重命名同一条路(侧栏 / 面包屑 / 搜索都正常),代价是这条会话不再被自动命名。titleMark: '' 可整个关掉。
  • 没有文件级隔离:分叉与旧会话共享同一个 cwd。write / edit 有 stale-version 守卫会拦住静默覆盖,但 bash 里跑的 git commit / rm / 重定向不受保护 —— 两个并发会话碰同一批文件时请自己协调。
  • containerMode(默认关)与 mirrorDelivery: 'immediate' 是实验/对照开关,不要打开:前者会让容器与分叉重复劳动,后者已知会写坏会话转写。
  • 客户端一半只在 web 平台生效(dsh.client.platform = web)。

验证

npm run verify = 语法 + 3 道静态门禁 + 127 项自证测试(digest 的确定性/有界性、各条拒绝路径、结果回注、血缘持久化与命名规则、工具的授权边界、「分叉」页签的渲染——用 React 桩渲染真实 bundle,断言用户看到的文案)。npm run preview:tab 可以不开浏览器先看一眼页签长什么样。

真 harness 的行为层 E2E 台在 tests/e2e/:它需要一个 dsh 源码检出(官方 mock LLM 不在 npm 闭包里),所以不进 npm run verify。改这个插件的内部机制与踩坑记录见工程笔记。

插件管理

已装插件可以用 plugin-registry 的薄控制台(浏览器面板)管理 profile 插件安装态(bundle 层栈 + insert 行 + 启停),无需手改配置:

dsh plugin --profile web add https://github.com/vlln/plugin-registry.git#path:/packages/plugin/console

许可

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit c8beeb20a48e

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout