deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:PPawnsir/task-board-plugin
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
智能看板插件 — Agent 自主任务驱动开发:看板管理 + 一次性 Worker/Verifier 派发 + 依赖调度 + Team 模式。
dsh --profile web| 宿主 DSH 版本 | 应装插件版本 | 原因 |
|---|---|---|
| ≥ 0.2.0(含 rc) | ≥ 1.3.0(必须) | 0.2.0 起宿主在启动/安装时强制校验 peerDependencies,区间不含 0.2.0 的包直接拒绝激活(路由不挂载、面板不出现)。1.3.0 声明 ^0.1.7 \|\| 0.2.0-rc.2 \|\| ^0.2.0——注意 semver 预发布不命中宽区间,0.2.0-rc.2 必须显式枚举。运行时 API(subagents/systemPrompt/agents/uiWorkspace.openSession/sessions/v4 日志格式)在 0.2.0 全部兼容,已过 0.2.0-rc.2 实测(E2E 23+8 断言全绿) |
| 0.1.7 ~ 0.1.7-x(含 rc) | ≥ 1.2.2(必须) | 0.1.7 会话日志升级为 format v4:插件消息 source.kind 必须是生产者自有 kind。1.2.1 及更早会在任务完成/阻塞回执落盘时抛 SessionFormatError: format v4 message requires a producer-owned source kind,并连带使主窗口当前轮次失败(表现为「本轮运行失败」);同时「跳转会话」因宿主移除 sessions.open 而失效(控制台 sessionsSvc.open is not a function),卡片活动心跳读不到 v4 日志(session.v4.jsonl.zstd) |
| 0.1.5-rc.1 ~ 0.1.6 | ≤ 1.2.1 | 1.2.2 起按 0.1.7 协议编写(v4 source kind、uiWorkspace.openSession 跳转),旧宿主未做回归验证,建议停留 1.2.1 |
dsh plugin --profile web add dsh-agent-board@latest # 0.1.7+/0.2.x 宿主(推荐)
dsh plugin --profile web add dsh-agent-board@1.2.1 # 0.1.5/0.1.6 宿主
从 ≤1.2.1 升到 ≥1.2.2 必须重启 DSH(host 端代码在启动时加载;1.2.2 之前的老版本还有一个路由残留 bug:禁用/启用热重载会撞
duplicate exact route,只能重启恢复,1.2.2 已修复)。
已发布至 npm 官方 registry(dsh-agent-board):
dsh plugin --profile web add dsh-agent-board
# 重启 dsh 生效
dsh --profile web
git clone https://github.com/PPawnsir/task-board-plugin.git
dsh plugin --profile web add <本仓库绝对路径>/packages/dsh-agent-board
# 重启 dsh
plugin tree failed to loadcurl -X POST http://127.0.0.1:3080/dsh-agent-board \
-H "Content-Type: application/json" \
-d '{"method":"get-tasks","args":{"sessionId":"<sessionId>"}}'
返回 JSON 即正常;405 空 body 说明路由未挂载。
# 市场版
dsh plugin --profile web add dsh-agent-board@latest
# 源码版
git pull
# 两者都需重启 dsh
dsh plugin --profile web remove dsh-agent-board
# 重启 dsh
看板数据存在
~/.dsh/tasks-<sessionId>.json,卸载不删数据。
- 归属:看板文件按会话分文件,同时在文件里记
ownerCwd(创建该看板的会话工作区路径,取不到则省略该字段)——~/.dsh/tasks-*.json每个文件是一块看板,list-boards全局视图可看到本机所有板。- 重启继承:DSH 重启后同一会话的根 id 可能漂移,此时新 id 没有对应文件——若同工作区(
ownerCwd严格相等)存在唯一「原主已不在agents.roots()」的看板,则自动继承:文件重命名为新 id、文件内ownerSession改写为新 id、console.error留一行[task-board] 继承看板 <旧sid> → <新sid>;多个候选一律不自动接管(记一行日志后按空板处理,防误合并,旧板仍可在list-boards全局视图里看到)。- 写盘保护:落盘走 临时文件+rename 原子写(EPERM/EBUSY 退避重试);瞬时读失败/坏文件隔离后返回的空板禁止回写(防一个"读不到"的瞬间把看板覆成空板),坏文件隔离为
.corrupt-<时间戳>留档不丢数据。
currentColor 跟随主题,浅深色自适应)⛁ 数字);区底固定口径 caption:累计 / Top 8 仅看板派发的 run 消耗,主窗口行并列不混入task-muwq9u04):按模型分布行与任务消耗 Top 8 行的主数字一律为有效消耗(含缓存读的合计进悬浮 title),与「今日」「近 7 天」同口径——不再出现「今日有效 3.7M / 单任务 21M」这类同区并排的矛盾数字(模型有效分摊由 host 的 byModelEff 给出;老 host 缺该字段或老卡无五分量留账时,主数字退化用合计并在 title 标 ~)task-muwc7hjd):仪表盘顶部的「统计范围」(快捷:今天 / 近 7 天 / 近 30 天 / 全部 + 自定义起止)现在同时作用于 Token 区的按模型分布 / 任务消耗 Top 8 / 累计三分量——范围变化即用新范围重拉 get-tasks,由 host 按每个 run 的本地日落点(t.runs[i].at → YYYY-MM-DD,两端闭区间)过滤:范围外 run 不计入 total/byModel/top,byDay 同步裁到范围内;范围激活时 Token 区标题带「范围内: …」标记。今日大数字与「近 7 天」柱子是自身固定口径,不随范围变化(区底第二行 caption 与范围筛选浮层均写明)。无范围(两端皆空)时聚合结果与旧版逐字一致(parity);无 run 级留账(老卡 / runs 条目未落 usage)退化为按 updatedAt 本地日判定,无 at 的老 run 只在无范围时计入(宁可漏不错),run 未记模型名时按入选 run 占比摊任务级模型小计(合计与有效同法摊派,ΣbyModel 与入选总量、ΣbyModelEff 与入选有效各自对齐)。数据源口径修正(task-muwq9u04):run 记录数组在任务级 t.runs[](t.usage.runs 只是结算次数计数)——此前误把计数当数组读,范围过滤静默退化成 updatedAt 单日近似、Top 8 与无范围逐字相同;现已逐 run 精确裁切,且范围切换后客户端按 usageSummary 的 JSON 串比对补一次 notify(选范围即渲染,不等下一次任意刷新)。Token 口径三连修(task-muwsnyqv):① 范围内 byDay 按入选 run 的本地日落点逐日重建(to 端闭区间,from=to=今天 时今天不丢——修「范围含今天时 total 含今天、byDay 却吞今天」);② usageSummary 同时返回未过滤全量 byDayFull 与范围内 byDay,「今日」「近 7 天」改读 byDayFull 兑现固定口径承诺(老 host 缺字段退化读 byDay);③ 旧口径日(e=null:老 number 日账 / 存量兜底,只有含缓存读的总量)不拿总量冒充有效值——今日大数字显示「—」+ title 说明,近 7 天画矮灰柱 + tooltip 标「旧口径数据(仅总量,含缓存读)」,柱高归一只按有效值(此前 154M 含 97% 缓存读的旧日账会被画成巨柱、把其余日子的有效柱压平)~/.dsh/sessions/*/<runId>/session.v4.jsonl.zstd),把 assistant/message 事件的 usage(inputTokens / outputTokens / cacheReadTokens / cacheWriteTokens / totalTokens,字段形状以真实日志为准)按 zstd 帧逐帧累加到任务 usage(含按模型小计、runs 计数与 byDay 日账——本地日 YYYY-MM-DD,一次 run 整笔记在结算日;多轮重跑/驳回重做自动累加);run 级留账:本次用量同时原样写回 t.runs 对应条目的 usage(五分量俱全),因此按天 / 按模型 / 按阶段任何维度都能从 runs 精确重建,口径若要再调整不必回头猜byDay 为双指标形态 { t, e }(t = 总量含缓存读,e = 有效消耗不含缓存读;一次 run 不跨日拆分)。聚合端兼容老数据:值是裸 number 的历史日账按 { t: n, e: null } 处理——总量照常保留(「近 7 天」不会整段消失),有效值不可知就置 null;旧口径日(e=null)UI 不拿总量冒充有效值(task-muwsnyqv):今日大数字显「—」、近 7 天画矮灰柱 + tooltip 标「旧口径数据(仅总量)」,总量仅在「含缓存读共 X」小字与悬浮 title 里单列对照get-tasks 再现算 board 级 usageSummary(总量 / 有效合计 / 按模型(合计 byModel + 有效分摊 byModelEff,两者同源累加)/ Top8(带有效与缓存读拆分)/ 日账双份(范围内 byDay + 未过滤全量 byDayFull,后者供「今日」「近 7 天」固定口径读取),不落盘额外表;老任务无 byDay 时整笔近似归到 updatedAt 的本地日,无 updatedAt 则不计入任何日)——只做展示、不做计费断言,日志读不到或没有 usage 时一律显示「暂无数据」;第二参 range(可选 { from, to } 本地日)给定即只聚合该范围内的 run(口径见上条)task-muwsol23):累计行下方新增一行「主窗口(本会话):今日有效 X · 累计 Y(缓存读 Z)」,按模型分布尾部追加一条「主窗口(对话)」(琥珀色与 run 模型区分,有效为 0 不渲染,固定尾部不参与排序);数据源是该看板所属主会话自己的 v4 日志,host 侧增量尾读聚合(缓存键=会话 id:文件不变零读、变大只读新增字节段、变小/轮换全量重读一次,尾部半帧不结算下轮补读——3s 轮询稳态零全量重读),五分量与有效口径(e=输入+输出+缓存写)同 Worker 完全一致;隔离红线:不进 Top 8、不进「本看板累计」、不进架构健康/学习飞轮基数,只并列展示;随统计范围裁剪(按 assistant/message 事件的本地日落点);日志不存在/读取失败/无 usage 一律静默降级(不渲染该行,不炸轮询)get-tasks 顺带对近 50 张卡现算四信号(纯函数零存储:touches 声明热度 ≥8 次且占比 ≥40% / 带 touches 任务滞留中位数 >2 倍 / 任务执行时长 p90 >45min(claimedAt→resolvedAt 纯干活口径,不含排队)/ 同路径驳回 ≥2 次),命中才在仪表盘渲染提示条(⚠️/ℹ️ 两级,最多 3 条)——让运行数据主动提示"该优化架构了"(如某文件反复成为锁热点=该拆),信号只建议不裁判note_search / note_manage)完全由主窗口 agent 自己决定lesson-candidate 消息(场景 / 错误做法 / 来源);② 主窗口裁决 Worker 歧义 → 一条 lesson-candidate 消息(场景 / 疑问 / 裁决结论)。同一事件按「同时间戳 / 同内容前缀」轻量判重只落一条,且不写 history 流转记录(不刷屏)push-lesson RPC followup 给主窗口 agent(提示语写明「请用你可用的笔记/记忆工具沉淀,或评估后忽略」),推送成功后按钮变「✅ 已推送」置灰contextNotes)feedbackEnabled(⚙️ 设置区「学习反馈」,默认开):关掉后不生成候选、prompt 不提软召回、详情页候选卡片与「沉淀」按钮整个不渲染;老看板文件没有该字段 → 读路径自动补 true(与升级前行为一致)dsh-agent-board 与笔记类插件(如 dsh-notes-plugin)之间没有任何依赖、服务调用或文件直写——看板只发一条 followup 文本,怎么用由主窗口 agent 决定draft → pending → in-progress → verifying → resolved → archived
↓ ↑
blocked ←────── reject
dependsOn 声明依赖(DFS 环检测),依赖全部完成后才会被派发,串行链路自动编排full(执行+验证)/ work(只做不验)/ direct(不进池,主窗口直接处理),创建时按规则自动分类、可手动覆盖acceptance 字段写验收脚本命令,Worker 必须实际运行、Verifier 必须独立复跑touches 声明本任务要改的文件/glob(如 ["src/**", "README.md"]);持有文件锁的任务(in-progress/verifying)与候选 touches 重叠就跳过本轮(卡片显示 🔒 等文件释放,详情页列出在等谁),锁随工作态:状态流转到已完成(resolved)即放锁,cancelled/归档同样不再持锁(归档回归纯收纳动作、不再是释放点)——锁只护「正在写」的阶段,「验收后-提交前」的窗口期由主窗口「回执到即提交」纪律 + 史诗 post-hook 承接,不用长持锁把整批串行化。避免并行 Worker 改同一批文件互踩;手动「派发」遇到冲突会列出冲突任务,确认后才以 force 越权派发board_report(kind: "progress",question 写一行进展摘要 ≤200 字符)上报——进行中的卡片显示「📈 最近进展 · 相对时间」(覆盖式只留最新一条),详情页消息流保留全部 progress 条目history 流转记录,避免刷屏archive-task 对「无活跃 run 的 in-progress」——典型如被 parentKick 推进后子任务已全部归档的史诗——直接放行,有活跃 run 的仍拒);归档子任务仍计入史诗进度并在详情留档可见——进度分子口径 settled = resolved | cancelled | archived、分母也含归档,归档一张子卡不会再让史诗进度从 0/10 退化成 0/9(进度只增不减),全归档的父卡也照常显示徽章;详情子任务清单不排归档行(灰化 + 行尾「已归档」徽章 + 沉底排序,点击仍可进子卡看留档)create-task 拒绝不完整 sessionId(防裸短 id 建出幻影板)get-tasks 响应附 tasksHash,任务列表无变化时前端跳过重渲染(usage/池状态等轻量字段照常刷新)isRoot 出现单次瞬态 false——现在需连续 3 次(≈9s)false 才收起抽屉(会话生命周期内「曾确认 true」粘滞),看板不再在生成状态切换时突然消失;从未确认 root 的子代理会话仍即时隐藏入口,保护语义不变settled = resolved | cancelled | archived,total 同口径含归档;有在跑子任务时附「▸ 在跑:标题」行);子卡标题下显示「↳ 父任务标题」;依赖未满足的待办卡底部灰字「⛓ 等待「依赖标题」」(childStats 由 host 现算,缺字段一律不渲染)epic.hooks = { pre: { enabled, prompt, state, runId }, post: 同构 }——前置让子任务具备开跑条件(串行闸门:未完成前该史诗的子任务一张都不派),后置把这批活收口(全部子任务了结后跑一遍,完成后史诗才转验证中);prompt 只给薄框架契约,具体动作由 hook agent 自行决策,吃不准就走歧义上报(失败即转阻塞等人裁决:重试/跳过/放弃,不自动重跑)。hook run 记在 epic.runs[](role hook-pre/hook-post),卡片带相位徽章(⏳ 前置准备中 / 🧪 收尾中 / ⚠️ hook 失败待裁决,无 hooks 的老卡零渲染),详情页 hooks 区可编辑 enabled+prompt、查看 state/耗时并跳转 run 会话、失败一键跳裁决区;hooks 仅主窗口可设(工具与 RPC 双通道门禁),commit/push 不入任何默认形态delete-task RPC(卡片 hover 垃圾桶按钮 / 详情页「删除」按钮,均先 confirm('删除不可恢复,确认删除「标题」?'))+ batch-op op='delete'(多选模式底部「批量删除」,同样 confirm)。状态门禁:草稿/待办/阻塞可删;进行中/验证中拒绝并提示先用 terminate-agent 终止(避免在跑的 run 变孤儿);已完成/取消引导改用归档(archive-task,留档可检索);有未归档子任务时拒删(防 parentId 悬空破坏父任务自动流转);已归档任务幂等返回 ok。是真删(从 tasks 数组移除),因此不产生 batch-undo 撤销快照(批量条对 delete 不显示「↩️ 撤销」),删除操作在 host 端 console.error 留一行日志便于溯源pipeline: direct,不进池派发),再挂若干 10~30 分钟的子任务(task_create 传 parentId=父卡 id,有先后顺序用 dependsOn 串联),子任务全部完成后父卡自动流转(checkParentAuto)——task_create 工具描述与 Team 模式提示词都写了这条契约task_create / create-task 发现描述超 500 字符、或标题/描述命中「全量 / 整体 / 系统级 / 全面 / 重构 / 所有模块 / 整个」等史诗特征词时,返回体附带一行 suggestSplit 建议文案(只提示,不阻断创建与派发;未命中则不出现该字段,老调用方无感)epicSplit(史诗拆分总开关)(⚙️ 入池配置弹层「功能」小节,默认开):关掉只停引导——Team 提示词第 6 条拆分条款整条不注入、create-task/task_create 响应不再附 suggestSplit 软提示;机制不禁(显式传 parentId 建子卡、史诗自动收口与 hooks 状态机照常工作,明确要拆时不受阻);老看板文件没有该字段 → 读路径自动补 true(与升级前行为一致)。工具描述里的粒度契约是静态工具定义(随 request header 快照),不在开关范围路径:L起-L止 — 一句用途」);文件内容本体不进 prompt,由子代理用 read 工具按行号范围按需自取(执行时盘面更新鲜;旧形态「host 读盘取正文注入」既受单文件 8KB/总包 40KB 截断,又随 runtime 快照每轮刷新重发——自治 run 实测 6×48.8K 字符≈白烧 75–100K token);UI 侧调研门禁——full/work 且声明了 touches 却未附调研的卡片亮「⚠️ 无调研」徽章,详情页「调研注入」区列 files 清单 + notes 字数(无则明示)contextFiles 条目写法「path:L2350-L2420 / path:L2350」+ 可选「— 一句用途」(em dash 两侧空格分隔;缺省只给路径行号)——锚点只认尾部 :L<行号>(兼容 Windows 盘符),清单里原样带上行号供子代理直接按行段 read;锚点写错(:L0 / :L5-L2)自动剥掉,不误导子代理去读空段read(path, offset, limit) 取需要的那段,清单里给出的行号就是起点;task_preview_context / preview-context 返回的也是这份瘦身清单(不是文件正文)task_create/create-task 响应附 warning 字段——①描述为空「Worker 只能凭标题猜需求」②full/work + touches 非空而未附调研上下文 ③touches 含整树 glob 建议精确到文件级(可多条合并);GUI 表单内黄色展示不关窗。epic 发布(publish)时自动轻量预检全部子任务注入情况,缺材料则 pushSysNote 汇总提醒主窗口(全有不打扰);派发时清单组装失败落任务「最近失败」行,不再静默board_verdict 工具 / Verifier 文本结算 / 手动 task_verify·verify-task)统一往 t.messages 落一条 kind: "rejection" 完整驳回包(summary + checks 逐条核对证据;手动路径补写 t.verification),经 buildMessages 全量注入重派 Worker prompt——新 Worker 据此返工,不再只看到 300 字截断的 history 记录一句话:Worker 从「一次性 run」变成持久子会话——超时不再丢现场,重派时原会话冷复活接着干;Verifier 与 hook run 仍是一次性。
subagents.startContinuable(rec 持 childId),turn 结束改由 host 事件 agent/status 的 running→idle 观测(一次性路径本就用 run.result 结算,未受影响);claimedBy / t.runs[].id / 详情页会话跳转的 id 语义不变(仍是子会话 id)interrupt 失败(会话已死/无权限)绝不阻断结算:任务照常回待办,只给该 run 落 noResume 标记(重派直接起新 Worker,不空唤醒)subagents.sendMessage(活父 Agent, childId, 断点续跑指令)——子会话带着上一轮全部上下文复活,先盘点工作树再从断点继续;t.runs[] 追加一条 resume: true 记录(详情页历史会话按钮带 ↻ 标记),续跑同样计入三连败计数;续跑不可用(NOT_RESUMABLE 等)→ 回落全新 Worker,并把原因写进任务消息随首条 prompt 注入listChildren(root):仍在列 → 重建 rec 观测(监听器本就在)并重挂两级超时臂、基线取当前日志字节数;不在列 → 视为会话已死,走硬超时等价物(回待办重排 + 留一行流转记录),不占 Worker 并发位seq 记水位线(落在 t.runs[].usageSeq):每次结算只累加水位线之后的 assistant/message 增量,本轮没新增量就一行都不记;one-shot 路径(每 run 独立日志)行为逐字不变workerContinuable(默认开):关掉即逐字回退旧的一次性路径(零 startContinuable/零 sendMessage/零 interrupt,结算仍走 run.result+dispose);Verifier 与 hooks(hook-pre/hook-post)保持一次性,不受该开关影响agent/status 的 running→idle,身份取 agent.session.id)与上报通道(board_report 落定即收尾)两条入口,任一到达即关账(结局/usage/超时臂三件套,幂等);派发周期自带幽灵活跃表项 GC(卡面证据核对回收残留 rec,防残留把派发容量顶到 0 拖死全池)+ 整轮 try/catch 与逐卡隔离(单点异常只作废该卡该轮)+ 去抖 latch 时间戳兜底复位task_intervene 对 continuable rec 走宿主投递通道(保留插件 source,下一个 step 边界消费),会话不可用降级 sendMessage 冷复活投递,再不行回退「记录注入随重派送达」并在 history 注明看板上一个选择器切换三档工作模式(RPC 单入口 set-work-mode,mode = list / auto / team):
| 档位 | 内部映射 | 行为 | 适合场景 |
|---|---|---|---|
| 📋 清单模式 | boardMode=manual + teamMode=false |
看板当 TODO 列表:任务建了就是 pending 躺着,主窗口自己 claim 办理,或逐张在详情页点「派发」才起 Worker |
需求还在拆、想自己盯着逐条推进;或只想借看板记账 |
| ⚡ 自动派发 | boardMode=auto + teamMode=false |
即建即派:pending 任务在心跳周期内自动派给一级 Worker,主窗口也可以自己 claim 干活 |
任务描述已经写清楚、依赖也理顺,交给 Worker 跑 |
| 🤖 Team 托管 | boardMode=auto + teamMode=true |
主窗口当调度员:task_create 缺省建草稿(补完 dependsOn/上下文再 publish 统一发布),Worker 歧义上报主窗口裁决 |
多任务编排、长链路、需要人工把关键决策点 |
三档通用(不是某一档专属):
acceptance 硬性验收脚本命令,Worker 必须实际运行、Verifier 必须独立复跑;跨档一致touches 文件级排他锁在活动任务间生效,冲突任务跳过本轮派发,锁随工作态(in-progress/verifying 持有;状态流转到 resolved 即释放,cancelled/归档同样释放——归档不再承担解锁职责);跨档一致Team 托管档独有(调度员体验):
task_create / create-task 缺省建为草稿(草稿不派发),先把所有任务的 dependsOn、contextNotes/contextFiles 补齐,再逐个 task_update publish=true 统一发布;确实要立即派发的单个任务才显式传 draft:falsenotifyDispatch(⚡ 派发回执:任务被 Worker/Verifier 领走时播报)与 notifyDone(✅ 完成回执:完成/阻塞时聚合播报),缺省均开(老看板文件缺字段自动补 true);歧义裁决通知不受开关影响(裁决通道不是回执,任务等人裁决必须提醒)兼容:旧的
set-board-mode/set-team-mode两个 RPC 原样保留(旧客户端与脚本不受影响), 内部仍以boardMode+teamMode两个字段落盘,老看板文件无损;get-tasks额外返回派生字段workMode供 UI 单点读取。
| 类别 | 工具 |
|---|---|
| 任务管理 | task_create / task_list / task_context / task_update / task_claim / task_resolve / task_verify / task_archive |
| 池治理 | task_terminate / task_intervene / task_arbitrate |
| 子代理上报 | board_report / board_verdict |
管理工具仅主窗口可用(子代理调用会被拒绝);
board_report/board_verdict是子代理的专用上报通道。board_report的kind三档:complete(交付完成)/escalate(歧义上报等裁决)/progress(里程碑进展,静默可见、不通知)。
└── packages/dsh-agent-board/ # 插件全部源码
│ ├── index.mjs # host 端薄壳(~70 行):cordis 契约 + 共享 state 构建 + 模块接线
│ ├── lib/core.mjs # 纯逻辑核心:状态机/依赖/分类/prompt/解析(无 IO,可单测)
│ ├── lib/*.mjs # host 端领域模块(按任务边界拆分,见下节)
│ ├── lib/client/ # client 端模块源(按用户感知域拆分,见下节)
│ ├── lib/client.js # client 端产物(⚠️ GENERATED:scripts/build-client.cjs 拼装,勿直接编辑)
│ ├── scripts/build-client.cjs # 零依赖组装器(模块源 → 产物;--check 校验产物新鲜度)
│ ├── test/core.test.mjs # 单元测试(node --test,84 例)
│ ├── package.json # dsh.bundle.patch + dsh.client 元数据
│ └── cordis.patch.yml # bundle 挂载行
└── docs/
├── PRD.md # 产品需求文档
├── PACKAGING.md # 打包/安装踩坑记录(link 依赖、单例隔离等)
├── icon-style-guide.md # 图标风格指南(Lucide 线性 SVG + emoji 分界)
└── REGRESSION-v59.md # 端到端回归测试记录
v1.6.0 起 host 端从单体 index.mjs(1487 行)拆为薄壳 + 7 个领域模块,
共享闭包状态收进显式 state 对象逐模块注入——模块边界即任务边界,并行任务不再全员互锁:
| 模块 | 域 | 内容 |
|---|---|---|
policy.mjs |
策略层 | 粒度治理软闸门 + 学习飞轮候选教训(纯函数零状态) |
usage.mjs |
统计 | v4 会话日志定位 / zstd 分帧 / token usage 聚合(纯函数;有效消耗 effectiveTokens + byModelEff 模型有效分摊 + byDay 双指标 {t,e} 聚合,兼容老 number 日账;统计范围按任务级 t.runs[].at 逐 run 裁切;sinceSeq 水位线增量结算——同一持久子会话多次结算不重复计账;readMainWindowUsage 主窗口会话增量尾读聚合——文件偏移水位缓存,追加只读增量、截断全量重读、尾部半帧不结算) |
session.mjs |
会话 | root 解析缓存 / 会话 id 归一 / workMode 派生 / runsFor |
store.mjs |
持久化 | boardPath / rt / wt 原子落盘 / 跨重启继承 / fileLocks 串行化 / mutateLocked |
notify.mjs |
通知 | makeMsg / 歧义 25s 去抖 / 回执聚合 + 空闲门控 / 投递前过滤 |
dispatch.mjs |
派发引擎 | poolCycle / spawnOneShot / settleRun / 两级超时 / 孤儿回收 / 可续跑 Worker(continuable + 重启 reconcile + usage 水位线) |
rpc.mjs |
接口层 | RPC 路由 + 13 个 Agent 工具注册 |
(store→dispatch 的循环依赖由 deps.poolCycle 晚绑定解开;index.mjs 对外 re-export 契约不变。)
dsh web 的 client 运行时不具备模块解析能力(entry 被整体读成字符串经 new Function 求值,
相对 import 是语法错误),所以前端模块化走构建时拼装:模块源在 lib/client/,
npm run build-client(pretest/prepublishOnly 已挂链)拼装成单文件产物 lib/client.js。
四个模块按用户感知域划分——边界即未来任务边界,新功能先想清楚落在哪个域:
| 模块 | 域 | 内容 |
|---|---|---|
kernel.js |
底座(用户不可感知) | 渲染原语(ic/icText/ActorLink)、RPC 封装与轮询族、共享状态(state/listeners/notify)、图标(ICONS)、共享任务工具,以及面板骨架入口(BoardButton/ViewTab/TopPanel/slots.inject) |
board-list.js |
看板列表 | 看板列与卡片(Card)、筛选条、多选批量操作条、建卡表单、归档列表 |
task-detail.js |
任务详情 | 详情抽屉(编辑/流转/冻结/touches/历史会话/高优介入)、歧义裁决对话与候选教训沉淀、依赖区块 |
dashboard.js |
仪表盘与设置 | 统计图表、Token 消耗、全局总览、报告生成、团队池视图、池/模型/超时设置与工作模式开关 |
四个模块拼进同一个 apply(ctx) 函数作用域(var/function 声明提升使跨模块引用与拼接顺序无关;
所有同步执行代码——DOM 监听、轮询注册、布局同步、slots.inject——都在 kernel 域内保持原相对顺序)。
改模块源后必须重新组装(npm run build-client)产物才更新;直接编辑 lib/client.js 会在下次拼装时被覆盖。
v68 起拆除了"动态源码 → 静态包"的转换层(build-pkg.cjs):插件已稳定, 双形态维护的复杂度大于收益,包内文件即唯一源码,改完重启 dsh 即生效。
v74 起去池化(一次性派发)+ 纯逻辑抽到
lib/core.mjs,跑node --test packages/dsh-agent-board/test/即可验证状态机/依赖/派发决策, 不用重启 dsh 人肉回归。
tag 驱动,GitHub Actions 自动发布到 npm(.github/workflows/publish.yml)。两种打 tag 方式都支持:
方式 1:命令行
cd packages/dsh-agent-board
npm version patch # 或 minor / major——改 package.json
git add -A && git commit -m 'release: vX.Y.Z' && git tag vX.Y.Z
git push --follow-tags # tag 推送触发流水线
方式 2:GitHub 网页(Releases 页)
packages/dsh-agent-board/package.json 的 version 改成目标版本并合入 main(网页直接编辑即可)vX.Y.Z 选 Create new tag(target 选 main)package.json version(如 tag v1.0.1 但包里是 1.0.0),防止版本错位packages/dsh-agent-board 跑一次 npm run sync-readme 同步进包(npm 页面展示的是包内 README)NPM_TOKEN
(npm granular access token:bypass 2FA + direct publish)test.yml 跑语法检查 + 60 例单测npm publish --registry=https://registry.npmjs.org(本机默认源是镜像时必须显式指定)MIT
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。