Skip to content

Releases: YuChenLiao/dsh-plugin-structured-prompt

v0.4.3

Choose a tag to compare

@YuChenLiao YuChenLiao released this 26 Oct 09:48

淇

*銆屽璇濆垰寮€濮嬨€嶆椂鑷姩濉厖鍙栦笉鍒版ā鍨嬨€?

0.4.2 瑙e喅浜?璇讳笉鍒颁細璇濇ā鍨?鐨勬湇鍔″彲瑙佹€ч棶棰橈紝浣嗘紡浜嗕竴绉嶆洿甯歌鐨勬儏鍐碉細鏂颁細璇濊繕娌℃湁妯″瀷閫夋嫨銆傜敤鎴锋病鍒囨崲杩囨ā鍨嬨€佷篃杩樻病鍙戣繃璇锋眰锛屾墍浠?modelSelection 鎶曞奖鐨?lastUsed 涓?pending 閮芥槸 null銆?

DSH 鑷繁鐨勫洖閫€閾炬槸 pending 鈫?浼氳瘽 requestHeader 鈫?agentDefaultModel锛坄session-controller/src/agent.ts:283-291锛夈€傛垜浠彧瀵归綈浜嗗墠涓ょ骇锛屼簬鏄竴寮€鏂板璇濆繀鐒惰惤绌恒€傜幇鍦ㄨˉ涓婃渶鍚庝竴绾э細浼氳瘽閲岃涓嶅埌鏃跺洖閫€鍒板涓荤殑**榛樿妯″瀷**锛坄agentDefaultModel.currentSelection()锛夈€?

瀹屾暣浼樺厛绾э細浼氳瘽姝e湪鐢ㄧ殑妯″瀷 鈫?瀹夸富榛樿妯″瀷 鈫?瑁呰浇琛?assistProvider / assistModel銆?

閿欒娑堟伅

涓夌骇閮借惤绌烘椂锛屾秷鎭細鍚屾椂缁欏嚭涓ょ骇璇诲彇鍚勮嚜鐨勫崱鐐癸紝渚嬪锛?

璇讳笉鍒板彲鐢ㄧ殑妯″瀷锛堜細璇濇ā鍨嬶細杩欎釜浼氳瘽杩樻病鏈夋ā鍨嬮€夋嫨锛堝厛鍦ㄦ湰浼氳瘽閲屽彂涓€鏉℃秷鎭紝璁╁畠璺戣繃涓€杞姹傦級锛?
榛樿妯″瀷锛氭嬁涓嶅埌 agentDefaultModel 鏈嶅姟锛夛紱涔熸病鏈夐厤缃?assistProvider / assistModel

鍗囩骇

瑁呭畬閲嶅惎 DSH銆?

v0.4.2

Choose a tag to compare

@YuChenLiao YuChenLiao released this 23 Oct 12:59

淇

銆岃嚜鍔ㄥ~鍏呫€嶆姤銆岃涓嶅埌褰撳墠瀵硅瘽鐨勬ā鍨嬨€嶃€? 鏍瑰洜鏄?*鏈嶅姟鍙鎬?*锛氳嚜鍔ㄥ~鍏呯殑璇锋眰澶勭悊璺戝湪
ctx.inject(['connection']) 缁欏嚭鐨?*浣滅敤鍩熶笂涓嬫枃
*閲岋紝鑰?sessions / sessionProjections
鏈繀鍦ㄩ偅鍎垮彲瑙併€備笂涓€鐗堝彧鍦ㄨ繖涓笂涓嬫枃閲屾壘妯″瀷锛屾壘涓嶅埌灏辫繑鍥炵┖ 鈥斺€?鐣岄潰涓婂彧鍓╀竴鍙?璇讳笉鍒版ā鍨?锛?
鐪嬩笉鍑烘槸鏈嶅姟涓嶅彲瑙併€佷細璇濆彇涓嶅埌銆佽繕鏄姇褰辫繕娌℃湁鐘舵€併€?

鐜板湪涓や釜涓婁笅鏂囬兘璇曪紝骞朵笖澶辫触鏃?*璇存槑鍗″湪鍝竴姝?*锛?

  • 鎷夸笉鍒?sessions 鏈嶅姟
  • `sessions.get('') 娌℃湁杩斿洖浼氳瘽锛堣繖涓細璇濆彲鑳藉凡缁撴潫锛塦
  • 鎷夸笉鍒?sessionProjections 鏈嶅姟
  • 杩欎釜浼氳瘽杩樻病鏈夋ā鍨嬮€夋嫨锛堝厛鍦ㄦ湰浼氳瘽閲屽彂涓€鏉℃秷鎭紝璁╁畠璺戣繃涓€杞姹傦級
  • 鎶曞奖閲屾病鏈夊彲鐢ㄧ殑 provider / model

鍙﹀ sessionProjections 鍙栦笉鍒版椂浼氬厹搴曚粠 sessions.scope(id) 涓婂彇銆?

鍗囩骇

瑁呭畬閲嶅惎 DSH銆?

濡傛灉浠嶇劧鎶ラ敊

鎶婇敊璇秷鎭嫭鍙烽噷閭e彞鍘熷洜鍛婃垜 鈥斺€?瀹冩寚鍚戝叿浣撶幆鑺傦紝姣?璇讳笉鍒版ā鍨?濂藉畾浣嶅緱澶氥€?

v0.4.1

Choose a tag to compare

@YuChenLiao YuChenLiao released this 19 Oct 13:20

修复

安装自检把正常的 npm 安装判成失败。 tests/verify-install.mjs 里那条"依赖指向本插件"只认 link: 与 github: 两种规格,所以从 npm 装上来的 0.4.0 会被判红。现在三种都接受,并额外核对安装形态(link: 应当指向源码、registry 安装应当是独立副本)。

补上 npm 发布形态的守护。 新增五项检查:private 必须不存在、files 必须含 lib / cordis.patch.yml / README.md、files 不得含 src / tests / tools、publishConfig 固定到 npmjs、exports 三个入口与 dsh 字段(bundle.patch 与 client.platform)都在。最后一项尤其重要:缺了 dsh 字段,包装上了但界面不会加载。

文档

README 新增「从 npm 安装」与「切换安装方式之前,先清掉旧条目」两节,写清了 Windows 上从 link: 换装的 EPERM ... rename '..._tmp_...' 及其处理顺序(关 DSH → 删旧条目 → 重新 add),以及"dsh plugin add 在已安装时可能不补写 dsh.profile.bundles"这个坑。

关于那个 EPERM

它不是包内容的缺陷。0.4.0 的包在隔离目录里实测可正常安装(含持有产物文件句柄的情形),装成的包包含 docs / lib / cordis.patch.yml / LICENSE / package.json / README.md 六项、版本正确。失败发生在 pnpm 替换旧 junction 的那一步,属于安装现场状态问题。

升级

dsh plugin --profile desktop add dsh-plugin-structured-prompt

装完重启 DSH。

v0.4.0

Choose a tag to compare

@YuChenLiao YuChenLiao released this 18 Oct 07:20

新功能

自动填充表单。 表单顶部多了一行:写一句需求,点「自动填充」,模型把这句话扩写成结构化字段填进表单。用的是你当前对话的模型,不需要任何配置。

提示词库原文 → 表单。 提示词库详情页多了「转化为表单」:把检索到的成品提示词拆解成字段,直接填进表单并打开它。与「发送原始提示词」是两条路 —— 那条把原文发进对话,这条只改表单,对话里不留任何痕迹。

两条都不经过主对话上下文:请求从界面直接到宿主,结果也只回到界面。

行为约定

  • 只补不改:自动填充只合并模型返回的字段,不会清空你已经填好的其它字段。
  • 校验后才落地:模型输出必须逐字段过校验;未知键、类型不符会被丢弃,并在界面上说明丢弃了几处。宁可补不全,也不写入来路不明的内容。
  • 模型取自当前会话:如果这个会话还没发过任何请求(所以还没有模型选择),会提示"读不到当前对话的模型",发一条消息后重试即可。
  • 子代理路径不可用:本版 DSH 的 subagents.start 不向插件暴露,工具的 delegate 参数会报一句可读错误(含该句柄实际暴露的方法名)。自动填充因此走单次调用,不依赖它。

升级

装完重启 DSH(host 侧注册发生在启动时)。

已知边界

  • 真实模型输出质量只能上线看:模型是否老实只输出 JSON、会不会漏字段 —— 漏字段只会"补不全",不会"清空"。
  • 没有取消按钮:等待上限 90 秒,关掉弹窗后请求仍在跑,结果回来时仍会合并进表单。

v0.3.3

Choose a tag to compare

@YuChenLiao YuChenLiao released this 16 Oct 07:06

修复

弹窗按对话列居中(第二版)。 v0.3.2 的修复没有生效:它只读取遮罩的一层父元素作为定位基准,而槽位的包装层数在不同 DSH 版本里不一样 —— 那一层可能仍是整宽容器,于是测出来正好等于视口宽,弹窗照旧按整个窗口居中。

现在改为从遮罩向上找第一个比视口窄的祖先(右侧栏展开后中间列必然窄于视口),它就是被挤压过的那一列;找不到时退回整视口居中。判据里没有任何宿主内部结构,不会随版本碎。

另外给遮罩加了一个可自证的属性,便于区分"没生效"与"生效了但期望不同":

// 浏览器控制台:'column' = 已按那一列生效;'viewport' = 退回整视口居中
document.querySelector('[data-dsh-prompt-mask]')?.dataset.dshPromptFrame

已知边界

  • 若右侧栏处于全屏 / 覆盖模式(中间列并不变窄),"按列居中"与"按视口居中"等价 —— 那种模式下本插件不会自动避让。
  • 右侧栏开关一瞬是否即时跟随,建议在真实界面确认一次(离线测试只能断言"基准是那一列"与"rect → 样式的映射")。

升级

dsh plugin --profile desktop add github:YuChenLiao/dsh-plugin-structured-prompt

装完重启 DSH(host 侧注册发生在启动时)。

v0.3.2

Choose a tag to compare

@YuChenLiao YuChenLiao released this 16 Oct 06:48

修复

弹窗在右侧栏打开时位置偏了。 结构化表单与提示词库的弹窗原来按整个窗口居中;右侧栏打开时对话列变窄,弹窗就偏右、被右栏压住。现在按弹窗自己所在的那一列居中,右侧栏开合与窗口缩放都会跟随。测不到那一列时(还没布局好)退回原来的整视口居中。

变更

样式从 ts 文件搬进真正的 CSS 文件,采用与 DSH 官方客户端包相同的方案(lightningcss 在构建期编译 + 自动注入):

  • 全局那份在 src/shared/ui/panel.css;组件引用的类在 src/shared/ui/panel.module.css,类名在构建期 hash,不污染全局。
  • 颜色一律用宿主的 --dsw-* 变量,不再在插件里固化主题。
  • 删掉了原来的内联样式对象与手写的样式注入逻辑 —— 注入现在是构建产物的责任,幂等由生成的模块保证。
  • 顺带消掉一处重复:文本域高度上限原来在 JS 与样式里各写一份,现在只有一处(JS 从计算样式读)。

对使用者没有可见变化,除了弹窗位置。

升级

dsh plugin --profile desktop add github:YuChenLiao/dsh-plugin-structured-prompt

装完重启 DSH(host 侧注册发生在启动时)。

已知边界

弹窗定位在真实浏览器里的表现(右侧栏开合时是否即时跟随)建议重启后看一眼 —— 离线测试只能断言「基准取的是那一列」与「rect → 样式的映射是纯函数」,测不了真实布局。

v0.3.1

Choose a tag to compare

@YuChenLiao YuChenLiao released this 15 Oct 11:24

从上一个 tag(v0.2.3)到现在,这个插件从"一个结构化提示词构建器"长成了两个可独立装载的组件。
以下是使用者能感知的变化。

新增

提示词库检索(请求由宿主代发)

输入框工具栏的「提示词库」按钮:检索 prompts.chat 上的公开提示词,列表 → 原文预览
→ 直接发送进会话(或复制)。

  • 检索结果不进构建器:那是别人写好的成品文本,因此不做字段校验、不填表单。检索与构建各走各的路径。
  • 请求由宿主代发:上游不返回 CORS 头,浏览器直连会被拒。副作用是好的 —— 上游地址成了宿主侧配置
    (config.libraryBaseUrl,缺省 https://prompts.chat),换自托管实例只改配置、插件代码不动。
  • 界面上标注数据来源(跟着配置走),并致谢上游:它是双许可项目 —— 源码与站点自产内容 MIT,
    提示词内容与数据 CC0 1.0。本插件只调用其公开只读接口,不打包、不分发它的代码或数据。

表单的模板场景

八套模板(4 套任务形态 + 4 套领域样例)在下拉里各带一句场景说明;套用后表单顶部出现一条场景条,提醒把
本次的具体任务写进「目标」—— 模板带的是形态化目标,不复述具体任务时产物会变泛。场景条是界面陈述,
不参与提示词构建(改它不会改产物一个字节)。

两个可独立装载的组件

cordis.patch.yml 现在是两条装载行,各自带 config、可单独 disabled:

装载行 提供 配置
dsh-plugin-structured-prompt 结构化提示词(表单 + 模型可调用的工具) provider
dsh-plugin-structured-prompt/library 提示词库检索 libraryBaseUrl

配置各归其位:provider 归结构化、libraryBaseUrl 归提示词库。第一条必须保留精确包名(不能改写成
包子路径):DSH 的 client 半边只认精确包名的装载行。

修复

  • 提示词库检索曾返回空列表:客户端拿"上游形状"的映射器去解析宿主已映射的响应,成功路径实际
    不可用(宿主与界面各自都对,错在中间那一层)。已修,并补了能单独证伪它的断言。
  • 注入样式曾打坏结构化弹窗:样式表里的选择器被写成逗号分隔的独立列表,裸属性选择器于是无条件命中
    弹窗根元素 —— 弹窗塌成 10px 高、整棵子树半透明(真实浏览器实测)。改用共享属性后,不再依赖
    "每加一个组件都要把样式改一遍"。
  • 两个入口曾一起消失:两条装载行都写成包子路径时,DSH 的 client 模块系统不会把它们当作 client row,
    client bundle 完全不加载。已改回"恰有一条精确包名",并加了断言守住这条约束。
  • 宿主没有那条检索路由时(典型原因是 host 侧改动没重启 DSH),界面现在直接说明该做什么,而不是显示
    一句"宿主的响应不是 JSON(HTTP 404)"。

升级

dsh plugin --profile desktop add github:YuChenLiao/dsh-plugin-structured-prompt

装完重启 DSH(host 侧的工具注册与路由注册都发生在启动时;客户端 bundle 会随页面刷新更新,这也是
"界面是新的、宿主是旧的"会发生的原因)。

已知边界

  • host 产物是零运行时 import 的打包结果,因此插件自身不需要 npm install,也没有 devDependency。
  • 提示词库弹窗与结构化表单弹窗现在可以同时打开(拆分前它们共用一条注册、互斥)。
  • 上游是第三方服务;指向自托管实例只需改提示词库那条装载行的 config.libraryBaseUrl。
  • 模板库数据在 src/components/structured/shared/templates.json(表单下拉、工具枚举、黄金样本同源)。

详细变更、审查记录、变异验证与遗留项见 docs/changes/ 下的批次归档。

v0.2.3

Choose a tag to compare

@YuChenLiao YuChenLiao released this 14 Oct 12:21

v0.2.3 —— 让模板真的能替模型省字段(2026-10)

本文件归档这一批改动:它起于一次真实用量统计,修的是一个自相矛盾 —— 不是新增能力,而是把
一个已经写好的能力从边界后面放出来。

分支:fix/template-supplies-identity。

起点:数据

扫全部会话里的 build_structured_prompt 调用(共 10 次,来自两个长会话):

观察 计数
带 template 3 / 10
带 delegate(且真的委托了) 1 / 10
手填 9–12 个字段 7 / 10
失败 2 / 10

两次失败的报错是同一句:invalid arguments: role is required and must be a non-empty string ——
模型没传 role,连续失败两次后放弃了这个工具(隔了 26 步才再用一次)。

根因是个自相矛盾:构建时 role/goal 走 prefer(调用参数, 模板底座)(模板本可以补上它们),
但参数边界的 narrowArgs 在进构建之前就强制两者非空 —— 模板永远补不进来。而"用模板少填字段"
正是模板库存在的意义。连带后果:render 里那段「Required field(s) still empty」分支在真实路径上
不可达,成了死代码。

使用者可感知的变更

  1. 传了 template 时,role/goal 可以省(缺省或只填空白都行),由模板提供;调用参数给了
    就用调用参数的。这不放松约束,只是把约束挪到能给出路的地方:放宽的仅是「缺失/空白」,类型错误
    照旧必报(role: 42 有模板也拦)。
  2. 不带模板时缺 role 的报错信息给出出路:末尾加上「或传 template:每套内置模板都带身份与
    目标」。让一次白跑变成一次成功调用。
  3. 工具说明与参数说明同口径:工具级原来写「Those two are required, matching the form」,与参数
    级新写的"模板可以带"打架 —— 模型会照更严的那份行事,于是这次改动就白做了。现在两处一致,并补了
    一个内联示例 { template: 'code-review', goal: '<the concrete task>' }。
  4. goal 的描述提示"能写具体就写具体":模板带的是形态化目标(例如"用最小代价给出该范围内的
    现状结论"),模型若只是选模板不复述任务,产物会变泛。这条是放宽带来的语义风险的直接对冲。

契约与不变量

  • 黄金样本 24 → 25 条:新增 tool-template-supplies-identity(只给 goal + template,身份由
    模板提供)。原有 24 条逐字未变。
  • NarrowedArgs 的 role/goal 改为可选:全仓只有 buildFromArgs 消费它,且经 asString()
    读取(typeof === 'string' ? value : undefined),因此类型放宽不影响任何调用点。
  • schema 的 required: ['role','goal'] 刻意保留:它表达期望(模型该尽量给),而实现在模板在场
    时容忍缺失 —— 这是「宽进严出」,不是矛盾。有硬证据说明 provider 不会依它硬拦:那两次失败的报错是
    我们边界的消息,说明缺 role 的调用成功穿过了 provider 的 schema 校验才抵达工具。
  • missing 分支据实标注为不可达:不留"很难走到"这种含糊说法;它是"某个模板漏了 role/goal"时的
    兜底。
  • 放宽的安全前提被写明:「每套模板都填了 role 与 goal」那条断言现在标注了它是放宽的前提 ——
    一旦某套模板缺这两项,放宽就会真的放出没有身份的产物。
  • 顺带修掉一个既有缺陷:verify-install 的 mtime 断言原来拿「全部源码 vs 单个产物」比,只改
    host 半边时会把 client 产物误判成"没重新构建"(本轮第二次触发;第一次靠加 2 秒容差绕过,这次差
    18 秒)。改成按半边划分源码集合(lib/index.js ← src/host + src/shared + 入口;lib/client.js
    ← src/client + src/shared),实测:把某 host 源文件的 mtime 推后,只有 host 那条红。

审查记录

轮次 范围 结论
第一轮 待提交的完整改动(含产物) 无阻断:15 组边界输入与 4 条突变验证后,放宽只覆盖"缺失/空白",类型错误未被放过,也没出现"既无身份又无目标却成功返回"。2 条需修:工具级说明与参数级说明打架;ToolArgs 的 JSDoc 还说"role/goal 由校验强制"。另 5 条建议
复审 第一轮修复的 diff 无阻断、无需修:tryExecute 哨兵不掩盖错误(行为回退时 6 条断言全红)、3 条描述断言区分度成立、TOOL_DESCRIPTION 五层信息完整(净增 124 字符)。5 条建议,其中"示例里的路径值可能被照抄"已改为 <the concrete task> 占位符、"很难走到措辞偏弱"已改为"不可达"、"示例断言钉死模板名"已改成形式断言

修复后自己跑了三条定点突变(usesTemplate 恒假 → 6 条红且是命名 FAIL;删掉"carries both" →
同口径断言红;删掉"concrete task"提示 → 对应断言红),确认修复本身有效。

待观察(复测口径)

这批复测要盯两个指标,数据只能等下一批真实会话:template 的使用率(改前 3/10)与**"只选模板
但没复述具体目标"的比例**。第二个指标是本次放宽引入的语义风险,goal 的描述只能引导,不能保证。

评估后决定不做

  • 把 role/goal 改回硬必填:那会重新引入这 2/10 的失败,而失败的真实代价不是那一次调用,是
    模型之后不再用这个工具(实测它隔了 26 步才再试)。约束应该放在能给出路的地方,而不是把门关上。

v0.2.2

Choose a tag to compare

@YuChenLiao YuChenLiao released this 14 Oct 08:04

v0.2.2 —— 模板库外置与「模板 / delegate」的说明修正(2026-10)

本文件归档自 v0.2.1 起的改动:改了什么、为什么这么改、哪些选择是权衡后定下的。这一批的起点
是一次上下文消耗诊断(见下节),结论是插件不是当时的成本源,于是改动集中在「让插件更容易
被用对」而不是「省插件自己的字」。

分支:feat/template-library。

起点:一次上下文诊断

在 session-e7ccd8f8(200 步)上统计真实用量:首步基线 22,137 tokens,末步 465,098;增长里
read 工具结果占 59.2%、edit 15.8%、grep 8.8%、pwsh 8.3%、subagent_fork 5.0%;41 条
超过 8k 字符的工具结果占了工具结果字符总量的 51.6%。

同一份扫描给出插件自己的用量:29 个会话里 build_structured_prompt 只被调用 10 次,表单成稿
投递 0 条。所以这一批没有去优化插件产出的字数(那是拿提示词质量换一个不是瓶颈的数字),
而是修了两个「用不对就白花」的地方:

  1. template 只有一个 id 枚举,没有任何场景说明 —— 模型无从判断该选哪套,于是干脆自己填所有
    字段,模板库等于白放。
  2. delegate 只写了「keeps long prompts cheap」,没写什么时候该用、什么时候不该用。

使用者可感知的变更

  1. 模板库 4 套 → 8 套。保留 4 套领域样例(告警研判(原始示例)、代码审查、数据提取、报告
    撰写),新增 4 套形态型:explore(探索调研)、implement(实现改动)、debug(故障
    定位)、handoff(交接归档)。形态型模板把「先读后结论」「最小改动 + 真实验证」「证据优先」
    「区分已完成与未开始」这类约束与任务形态绑定,模型只需给 role/goal 与少量覆盖。
  2. template 的说明带出每套的一句话场景(whenToUse),模型据此挑选。
  3. delegate 的说明重写:明确它是可选路径,说明什么时候值得用(本会话历史已长、或任务
    需要大范围探索),并点出首轮的风险 —— 子代理独立运行、看不到本会话,任务尚未澄清时委托
    等于把手头信息最少的那一版交出去。
  4. 未委托分支的返回多了一句成本陈述:说明这段文本会留在上下文并在之后每一步重放,带
    delegate 的调用可以让下一份不进来。它是陈述事实(模型自己决定),不是把它推成默认路径。
  5. 模板数据外置到 src/shared/templates.json。以前模板硬编码在 templates.ts 里,改一套
    模板要动代码;现在 JSON 是单一真源,表单下拉、工具的 template 枚举、黄金样本都从它取值。

为什么 JSON 走「直接 import」而不是「构建期生成」

实测(本机 tsdown,探针见下):import data from './x.json' 会被内联成字面量 —— host ESM 与
client CJS 产物里都只留 //#region src/shared/templates.json 加数据。host 产物里静态 import、
动态 import(、require( 全部为 false(「零 import」的硬约束不受影响);client 产物里仍只有既有的
三个共享模块请求(react、react/jsx-runtime、@deepseek-ai/dsh-client-store,与本批无关)。

对比另一条路(构建期把 JSON 生成成 .ts 并入库):那样要多出「生成脚本 + 生成物入库 + 防漂移
断言」三个部件,而"改了 JSON 忘了重新生成"正是这个仓库反复踩的那类缺陷。直接 import 只有一个
数据文件 + 一行导入 + tsconfig 一个开关(resolveJsonModule)。

JSON 是文件边界,所以 templates.ts 里对字段形状校验一次:复用导入校验那份 FIELD_SHAPES
与 matchesShape(forms.ts 导出),字段名写错、类型不符、越界枚举都会报错,而不是把坏值塞进
表单。校验发生在模块加载期(导入后立刻跑一遍),不是打包期 —— 打包器只搬运数据、不执行代码;
build() 里还会再校验一次,那只是兜底。

契约与不变量

  • 黄金样本 20 → 24 条:新增的 4 条锁住新模板的产物。原有 20 条逐字未变(JSON 里的 4 套
    领域模板是从原代码逐字搬运的)。
  • 工具 schema 变大是刻意的:8 套 × 一句场景 ≈ 130 字,而 schema 常驻上下文(那条 200 步
    会话的首步基线里就有它)。这就是模板不能无限加的原因,也是模板数量、whenToUse 长度都被断言
    盯住的原因(whenToUse ≤ 40 字)。
  • 移除 ALERT_TRIAGE_FIELDS 导出:模板库改由 JSON 驱动后,测试改用 TEMPLATES[0].build()。
  • 新增断言(并入既有「文档契约」套,不新增第九套):模板 JSON 的版本号、每套模板的必填字段、
    id 唯一且为 kebab-case、字段名与取值符合表单形状、每套都填了 role/goal、whenToUse 长度、
    每套都能构建出以 # 身份 开头的提示词;工具侧断言 template 描述里带出每套场景;README 的
    文件清单断言扩展到 .json,收集范围也扩到仓库根(否则 package.json 会被误判为不存在)。

待决策(刻意没做)

  • 表单侧的「直投子代理」出口:现在工具(模型)有 delegate,而表单(人)只有「复制 / 发送」,
    人构建的长提示词只能走最贵的那条路。技术上客户端已有 ConversationAttachmentFace.sendSession
    与 fileUpload 可用(把成稿变成附件、主上下文只留短指令),但这要新增服务依赖,破坏现在
    「inject 只有 slots」的契约与它的断言 —— 属于插件定位的变化,留给你定。
  • 工具结果裁剪与历史摘要:那是 DSH compaction / tool-result-pruner 的职责(你的 desktop
    合成树里这三个条目是 disabled: true),插件既无职责也无接口。

v0.2.1

Choose a tag to compare

@YuChenLiao YuChenLiao released this 14 Oct 06:41

v0.2.1 —— 审计驱动的修复批次(2026-10)

本文件归档自 v0.2.0 起的这一批改动:改了什么、为什么这么改、哪些结论是评估后决定不做的。
用于以后回溯「当时为什么是这个形状」,而不是流水账式的变更清单。

分支:fix/code-doc-deviations(已合入 main)。下表是修复提交的去向,其后的两处收尾见文末。

批次构成

提交 主题 契约影响
fce5275 注释 / 配置 / 死代码与实现脱节 无(黄金样本逐字未变)
aef6ff9 两条弱断言改为真正定位被测对象 无
0abef1f 审计后的断言收紧与时机误述纠正 无
f9c238b 「空串算未给」判定收敛到一处 黄金样本 +2;全空白数组的语义变更
7aea012 README 硬事实纳入「文档契约」套 无(新增 13 类断言)
901d853 host 黄金样本 18 → 20 + 参数边界断言 黄金样本 +2
0f47828 delegate 的失败路径与取消链路 无
39ac74b provider 默认值改为装载行 config 新增导出 Config;apply(ctx, config)
2a69037 全仓一致性修正 + 文件清单双向断言 无

收尾两处:本归档与版本号提升(333f246),以及给 verify-install.mjs 的 mtime 断言加 2 秒容差
—— git checkout / merge 会把工作区文件的 mtime 刷成同一时刻,毫秒级乱序会让严格比较误报
「lib 未重建」(合并到 main 后实测差 7ms)。

使用者可感知的行为变更

  1. 全空白数组算「未给」(f9c238b):rules: ['']、callable_tools: [''] 现在与空数组等价 ——
    回退模板底座,而不是清空它。此前同一个输入在两个字段上行为相反:rules 走 prefer(只看数组
    长度)会清空底座,callable_tools 按内容判定会回退底座。
  2. provider 默认值可从装载行配置(39ac74b):cordis.patch.yml 的装载行支持
    config: { provider: 'fork' },优先级为「调用参数 delegate.provider > 装载行 config >
    内建默认 spawn」。不写 config 时行为与 v0.2.0 逐字相同。
  3. 配置形状不合法在装载期报错:config 不是对象、含不支持的字段、或 provider 不是字符串
    时,cordis 抛 ValidationError 且 apply 不被调用。两点边界:provider 名是否已注册要到
    第一次 delegate 才校验;provider 必须写在 config: 之下(写成条目层会被 loader 原样保留,
    既不报错也不生效 —— 这条属 loader 行为,需真机确认)。
  4. 工具对模型的说明文案:delegate.provider 的 description 由「the default (a one-shot run)」
    改为「the deployment default」—— 默认值现在可以被部署改,旧文案会让模型误判语义。
  5. 以冒号开头的工具条目(: 用途)不再被当成名叫 : 用途 的工具,而是与空工具名行一样整行
    忽略。

契约与不变量

  • 黄金样本 16 → 20 条,原有 16 条逐字未变(.gitattributes 的 tests/golden/*.txt LF 约定
    继续生效)。
  • host 产物仍零 import:静态 import、动态 import(、require( 三条都在断言里。Config 是
    手写的 standard-schema 对象({ '~standard': { version, vendor, validate } }),不引入校验库
    —— cordis 的 resolveConfig 只消费 ['~standard'].validate。
  • 导出面:inject / Config / apply,全部命名导出、没有 default(函数插件混用 default 会让
    Loader 丢掉整个命名空间)。
  • README 的硬事实由「文档契约」套机器核对,且文件清单改为双向:src/ 下每个文件都必须出现
    在 README 的文件清单里。该断言在落地当轮就抓出三处漏列(host/config.ts、client/send.ts、
    client/plugin-context.ts)。
  • 工具名与用途整组同进同退:由 resolveCallableTools 这个纯函数承载,可直接断言 —— 底座为空
    时"一退一留"在产物上不可观测,只能测函数本身。

评估后决定不做

方案 结论 依据
「越界枚举的拒绝」做成黄金样本 不做 黄金套的取数路径直接调 narrowArgs,抛错会让整套以未捕获异常终止,而黄金机制只比对产出的文本。该断言留在 test-tool.mjs
applyFormatChoice 的「骨架 + 追加」进 host 黄金样本 不做 四套模板都不带 outputSpec,底座经 completeForm 补成空串,只能命中"整段填入"分支;extends 分支恒不成立。要构造它得给模板加带骨架的 outputSpec(产品改动),并漂移四条 template-* 样本
不导出 Config、校验全由插件自己做 改为导出 Config 初版论据是"导出 schema 必须 import 宿主包",审计指出 import type 在 emit 时被完全擦除、本地声明子集本就是本仓现成做法;导出后校验由 cordis 统一执行、错误类型统一
用 defineTool 构造工具定义 不做(沿用 v0.2.0 的结论) link: 安装下 ESM realpath 到源码目录,那里没有 node_modules;import 宿主包会让插件在启动期失败

待决策 / 需真机

  • 优化点 6 三项(导出为 skill、自定义模板持久化、升维为提示词资产管理)未实施:都属产品决策,
    其中持久化需要先选通道(localStorage / remote / 文件即模板)。
  • host 侧改动需重启 DSH 生效(工具注册发生在启动时)。重启后建议确认:build_structured_prompt
    仍在、inject: ['tools'] 正常、以及(可选)临时写 config: { provider: 'fork' } 是否生效。
  • README 里几条静态无法判定的表述(客户端能否对单次对话注入 system prompt、store 更新是否触发槽位
    重渲染、以及历史计数)留待真机或历史记录确认,本轮没有改写。

验证

node tools/lib/build.mjs + node tools/run-tests.mjs:八套全绿 —— 类型检查 / 提示词行为 /
黄金文件(20 条)/ 文档契约 / 注册契约 / 渲染结构 / 工具行为 / 安装产物。

每个大项提交前都跑过一次;关键断言都用 git stash 把实现回退到上一版做过反向验证,确认它在
旧实现下确实会变红(例如「空标签回退默认」「冒号条目得到空工具名」「全空白 rules 与未给等价」
「Config 校验失败给出 issue 列表」)。