Skip to content

Releases: hyperion2144/dsh-hashline-edittool

v0.10.7

Choose a tag to compare

@github-actions github-actions released this 02 Nov 14:35

Added

  • README 增加 dsh-plugin.org 收录徽章(Listed on dsh-plugin.org)。
  • 新增 screenshots.json:在插件仓库里声明市场截图,dsh-market 等店面按此展示。

Changed

  • npm 包的 description 对齐当前工具集:去掉已在 0.4 契约中移除的 batch_edit,补上 write/grep/delete/rename/ast_grep/ast_edit/lsp/format,并把标记形态改成 <anchor>(可选 <anchor>:<line>)。

Fixed

  • grep 的命中数与截断不再静默:每个文件一节末尾报该文件的真命中数与本次返回数,被每文件上限或响应预算截断时写明是哪一刀截的(续取段同样逐文件报);canonical total 改为真命中数(触到上限时补一趟不限额计数),并新增 truncatedBy(#386,ADR-0029)。
  • grep 卡片折叠行标题尾显示命中数与截断原因(双语,与 read 行窗口同一槽位)(#386,ADR-0029)。
  • LSP 自动诊断不再投递过期内容:同一文件的后续写入落地后,先前写入的延迟诊断会被丢弃,诊断探针也不再把旧文本回灌给语言服务器;已进入队列的旧报告带写入代际标注,与新报告自证先后(#388,ADR-0030)。
  • 会话切换工作目录后,read/write/edit 等工具的相对路径跟随 DSH 的当前工作目录,不再落回会话创建时的旧检出(#394,ADR-0031)。

v0.10.6

Choose a tag to compare

@github-actions github-actions released this 30 Oct 08:10

Fixed

  • grep 字面模式(regex: false)搜目录不再恒返回「No matches」:模式作为一个参数原样传给 ripgrep,字面量改用独立的 --fixed-strings,不再把 -F 拼在模式文本前面(#384)。
  • grep 改由 ripgrep 直接产出命中(rg --json),不再逐行用 JS 匹配;锚点、上下文行、预算续取、二进制保真等契约不变(#384,ADR-0028)。
  • grep 的模式方言改为 ripgrep 正则(原先是 JavaScript 正则):ripgrep 拒收的模式即坏模式(E_BAD_SHAPE),与官方 grep 工具同口径(#384,ADR-0028)。

v0.10.5

Choose a tag to compare

@github-actions github-actions released this 29 Oct 14:44

Changed

  • 发布流程改走 npm 的 OIDC trusted publishing:推 tag 即由 CI 自动发包,不再需要本机浏览器 2FA,仓库里不存 npm token(#378)。
  • edit 每次调用的编辑条数不再有上限:每条编辑各自寻址自己的行,一批有多长只取决于改动本身,不必再拆成多轮(#380)。

v0.10.4

Choose a tag to compare

@github-actions github-actions released this 29 Oct 14:13

Added

  • lsp.idle_timeout_ms 设置:语言服务器空闲超过该窗口(默认 5 分钟,0 = 永不)即被回收,下次用到再拉起(#306)。

  • format.fallback_full_file 设置(默认关):range 模式下服务器不支持局部格式化时的回退必须是显式选择——自动路径静默跳过,format 工具显式带 range 则以 E_FORMAT_UNSUPPORTED 拒绝而不是悄悄格式化整文件(#315)。

  • 新增 delete 工具:删除普通文件并回收该路径的 anchor_meta / anchor_lines / 所有会话的 served / 所有深度的 undo,报告逻辑释放量;物理空间在存储关闭时 VACUUM 归还(#322)。

  • 新增 rename 工具:重命名/移动普通文件时把旧路径的四族锚点行迁到新路径,已分配与服务过的锚点无需重读继续可用;目标已存在即拒绝,批量按请求顺序逐对执行(#337)。

  • 新增 write.preview 设置(默认 diff):write 写后回显可选统一 diff / 整文件回读 / 仅一行确认;confirmation 不回显内容也不分配锚点,卡片 gutter 只有行号(#354 / #359)。

  • 五类卡片新增换行开关:read / grep / diff(edit、write、undo、ast_edit、format)/ LSP / error 卡片默认自动换行,一键切回横向滚动;error 卡无回显内容时不出现按钮(#373)。

  • read 折叠行标题行尾显示读取窗口(L240–319 · 共 374 行),预算截断附 resume 提示(#374,ADR-0027)。

Changed

  • web 卡片改用插件自有词表命名空间 hashline.card(zh/en,跟随 DSH 语言设置,其它语言回落英文):卡片界面文字随语言切换,diff / LSP 卡不再显示 key 字面量,delete 卡与 error 卡的硬编码英文一并收编(#372)。
  • host 拼装的文案(read 退化注记、折叠行标题、LSP 状态与安装提示)改为稳定 key 过 wire,客户端按界面语言渲染;插件设置页界面文字全部双语化,delete / rename 卡的标题、摘要与明细随之跟随语言(#375,ADR-0026)。
  • format.editorconfig 默认改为开启:项目 .editorconfig 的缩进声明优先于文件自身的缩进推断,显式设 false 退回推断(#336)。
  • 编辑 diff 改为 unified diff 形态:文件头 --- path / +++ path,段头 @@ -old,count +new,count @@ 函数名() 给出改动前后的行区间与所在函数;删除行留原位,其失效的旧锚点以 ~ 标记(#293,ADR-0020)。
  • 段头的作用域名由纯语法作用域检测给出(src/render/scope/):检测不到就留空,不猜(#294)。
  • 作用域检测按文件扩展名/文件名选语言与声明模式表(git 的做法),覆盖 TS/JS、Python、Go、Rust、Java 系、C 系、Ruby、Lua/Elixir/Julia 等语言;表驱动,加一种语言是一行(#301)。
  • 段头列出该段所有改动落入的作用域名(first() / second()),不再只报第一个(#302)。
  • 作用域检测对同一份文本只扫一次(缓存键含路径):写大文件时不再按改动行重复全文扫描(#303)。
  • web diff 卡片同步新形态:段头作分组标题,折叠只数代码行,复制文本不再带上段头(#295)。
  • format.auto 从布尔改为三态模式 off(默认)/ full / range,存量 true→full、false→off 自动迁移;range 模式只格式化本次调用改到的行,write 无改动区间视同 full(#315)。
  • 自动格式化段的 restored 折叠:被格式化器改回编辑前原文的行不再引用内容,每段连续行折叠为一条 @@ ~start,count @@ scope 定位行加一句 restored 摘要,仅锚点真漂移的行保留内容行;header 增加 r restored 计数(#315)

Fixed

  • format 不再把 4 空格深层嵌套文件推断成 8 空格:缩进单位改按「行间步长 + 重复宽度网格」联合识别,个别错缩进行不再改变单位,全文与范围格式化不再产生 4/8 混排(#336)。
  • rename / delete 卡片标题修复:工具名与路径/数量之间补分隔点;rename 标题统一列出 from -> to,delete 列出实际路径(#337)。
  • format 工具的 range 边界修复:<line>:<anchor> 组合形式(数字在前的行号提示)的行号曾被当锚点查找而误报 E_RANGE_UNVERIFIED,现在两种行号顺序都正确解析(#316)
  • 关闭哈希存储时在写完关闭标记后再做一次 WAL 检查点:干净关机后不留未落盘的 WAL 帧,下一次打开是普通读而不是恢复(#303)。
  • 关闭哈希存储时真正等待 VACUUM 完成;此前的 pending_vacuum 没有生产者、VACUUM 又是异步的,标记在完成前就被清掉。现在删除/淘汰在逻辑字节下降时写标记,关机同步 VACUUM 成功才清(#322)。
  • 语言服务器子进程不再常驻到插件卸载:握手失败的进程立即关闭,空闲超时的会话被回收(#306)。
  • grep 命中区超过单次响应预算时不再返回空壳:改为按行切分,先返回预算内放得下的行(带锚点、可直接编辑,切点不会落在首个命中之前),其余行经 resume 提示续取(#307)。
  • 锚点库冷开与每次写的预算判定不再随 anchor_lines 行数线性变慢:行数改为随写入同事务维护的值(#305)。
  • 锚点库的路径数改为随写入维护的值,预算判定不再随路径数变慢(#311)。
  • 自动格式化段不再逐行重复主 diff 已展示的行:这类行只在 header 计数(k in main diff),write 更新路径同样塌缩为计数,手动 format 全量输出不变(#312)。
  • lsp 错误响应不再被框架拒绝:语言服务器启动失败/超时等转出的错误值补齐了 output schema 必填字段(#317)。
  • LSP 启动失败的槽位不再永久卡死:按指数退避自动重试(5 秒起、上限 1 分钟),成功后计数清零;启动失败报错附服务器 stderr 尾巴(#318)。
  • lsp 工具新增 status(只读查询已启动语言的服务器状态)与 warm(跳过退避立即启动/重拉)操作(#318)。
  • lsp 的 status/warm 出专属状态卡(状态点 + 语言 + 失败原因),词汇表之外的意外错误包装为 [E_LSP_UNEXPECTED] 走错误卡片,不再因宿主无法无损序列化而整次调用失败(#320)。
  • lsp 的错误值不再携带字面 path: undefined:status/warm 无 path,宿主无损 JSON 校验会整次拒绝这类拒绝值,改为省略该字段(#320)。
  • client 半区九个 toolview 接管注册收进一个 TAKEOVER_PRIORITY 常量并改为 -2:与 dsh-claude-style 写死的 -1 不再同值,两个插件同时启用时不再抛 keyed slot "tool.call.toolview" ... already has an entry(#321,ADR-0022)。
  • 同一 edit 调用里释放的锚点不再被该调用的后续行重新分配;多 item 批次的释放池与 served 清理不再被批次收尾的早退吞掉(#351)。
  • PTC preset 会话里的卡片不再回退成原始输入/输出:卡片模型不再一律拒绝 parentCallId,run_code 子调用带同一份 presentationMeta,和原生调用一样渲染(#353)。

v0.10.3

Choose a tag to compare

@github-actions github-actions released this 19 Oct 13:30

Added

  • format 工具:用语言服务器的格式化器格式化文件(全文或 served 锚点 range),可冷启动、进 undo、noop 不写盘(#275)。
  • format.auto / format.editorconfig 设置(默认关):write/edit/ast_edit 后自动格式化被改行,无局部格式化能力时回退全文;格式化改动在卡片上以独立段呈现,未 serve 的行不带锚点(#275,ADR-0019)。

Fixed

  • 区间跨「从未读过的行」的 edit 不再谎报该行「已被删除或改写」:稀疏投影空位 "" 按 never-seen 分类并给出重读参数(#282)。

v0.10.2

Choose a tag to compare

@github-actions github-actions released this 15 Oct 23:53

Changed

  • SDK 开发依赖对齐 0.2.0-rc.2 运行时线(17 个 @deepseek-ai/dsh-* devDependencies);类型与测试期依赖,源码零改动。

Fixed

  • read/edit 不再仅凭 NUL 字节判定「二进制」:含 NUL 的合法 UTF-8 文件可读可改(读服务锚点、编辑写回字节一致),非法 UTF-8 仍可读但不可编辑([E_LOSSY_BYTES]),grep 的 lossless 命中同步升级为可编辑(#273,ADR-0018)。
  • 后端「读取超上限」错误码映射为 [E_TOO_LARGE],不再裸冒 FS_TOO_LARGE(#271)。
  • grep 不再因一个 NUL 字节把整个文件静默跳过(#268)。
  • 冷开性能预算改判「三个新进程取中位数」,共享 runner 上的单样本抖动不再误杀(无票)。
  • 工具目录下嵌套检出的测试不再被主检出收进测试集合(#266)。

v0.10.1

Choose a tag to compare

@github-actions github-actions released this 08 Oct 01:09

Fixed

  • read 游标收纯数字字符串(#256,ADR-0014 Amendment):模型工具调用里行号以字符串形态到达是常态(本单会话内 8 次自然复现:offset "1"/"145"/"148"、limit "8"/"14"/"40" 等),而 parseReadCursor 的 DIGITS_RE 分支把纯数字串一律当「row marker 的行号 hint 被当锚点抄」抛 [E_BAD_REF] —— 同一语义的 number 形态却照收,错误文案指路「Re-read that line to get its anchor」对想要行号的调用方完全无效。修复(#238 决议 6 之修订,维护者拍板):字符串 trim 后命中 /^\d+$/ 即按数字语义解析(offset=起始行号、limit=行数,与 number 同义);沿用 number 同款正整数校验("0" → [E_BAD_SHAPE],顺带修复其原先报 E_BAD_REF 的同语义两种报法);前导零("088")与空白(" 88 ")容忍;line#hash / line:anchor 混合形态维持 [E_BAD_REF],真锚点行为不变;presentCall 卡片同步归一(数字串不再显示为幽灵锚点)。read-window 套件 13 → 20 例(单元级边界矩阵 + 集成级同结果/JSON 同码)。

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 07 Oct 13:09

Changed

  • 续读/分页文案收口成一句话(#246):B 线只统一了 read 自己的尾巴,其它通道仍在各自拼同一句「还有更多」。本轮按每个通道的消费者逐个裁决:括号版通知 (Omitted N lines[ (~C chars)]. Use <consumer> {resume: "TOKEN"} to continue.) 收进共享生成器 formatOmittedNotice(src/infra/response-stream.ts),由 spillModelTextOverflow(edit / lsp / ast_grep / ast_edit / undo 六处调用者输出字节不变)、grep 相邻两处(原先在同一个文件里拼了两遍)与 read 的 [Continued report] 页脚共用 —— 后者是唯一一处可见变化:more lines → lines。write 的自动预览不再自造第四种尾巴:它就是「第 1..K 行 / 共 N 行」的文件窗口,改用 read 的窗口句 [Lines X-Y of N. Omitted K lines. Use read {resume: …} to continue.](formatWindowSummary);JSON 模式下也不再是「JSON 后面粘一句散文」——window 与 continuation 进 payload,顺带关掉一条真实崩溃路径(JSON 模式 + 有 diagnostics + 预览被截断时,diagnostics 合并会对粘了尾巴的文本再 JSON.parse),续读令牌也不再只活在 modelText 里。删除 hashline/anchor-pipeline.ts 里已无人调用的 paginationHint(第三个生成器,死的)与 write 预览里一条裸表达式死代码。此外修掉 read guidance 里描述已退役旧尾巴的引文,并给 grep guidance 的引文补上真实的 (~C chars)。裁决记录:grep 无窗口数字可报(只有 spill)、edit 的拒绝回声今天本就没有分页句,两者语义各自保留。已知未改(诚实记账):read 的 report-segment 续读在 JSON 模式下仍是散文(totalLines:0 / lines:[] 占位),与 #245 的 JSON 纯度承诺不符,另票处理。新增 test/core/issue-246-tail-copy.test.ts(8 例)钉住上述字面量。

  • 模型侧文案改成逐文件口径:prompt 不再宣称「整批原子 / 整批拒绝」(#242):edit 的 description 与两态 guidance 原先写着「The batch is ATOMIC — any hunk failure rejects the WHOLE batch and nothing is written」,而 ADR-0003 的语义是每个文件一个原子批次、文件之间互不牵连、部分成功逐文件上报 —— 模型据此会把一次多文件调用当成单一事务,一处失败就以为整次什么都没发生、也就不会去重试失败的那个文件。现在 editDescription 两态的开头、两态 guidance 的原子性 bullet、edits 参数 description、README / README.zh 的同款 bullet 与 [E_BATCH_ABORT] 错误码行都改成「该文件的批次被拒绝、该文件什么都没写;其它文件不受影响,部分成功逐文件上报,失败文件各带自己的错误码」。顺带查明并收窄同族的另外两处夸大:申报不匹配([E_CONTENT_MISMATCH])与 Classification: noop 也都只在文件范围内成立。引擎抛的 [E_BATCH_ABORT] 尾巴逐字保留 —— 它的作用域就是「一个文件的批次」,单文件调用里字字为真;多文件响应由 ADR-0004 规定剥掉该尾巴(test/core/multi-file-atomicity-contract.test.ts 两处已锁)。另外,两处只在文件范围内成立的守卫消息([E_ANCHOR_AMBIGUOUS] 的两条抛出文案与 README / README.zh 错误码表对应行)原先也收尾于「nothing was written / 未写入任何内容」—— 多文件调用里别的文件可能已经落盘,同一批收窄为「该文件」。新增 test/core/issue-242-atomicity-wording.test.ts 钉住这些文案不再出现整调用口径。

  • 设置文件通道整体删除:插件不再自己读写设置文件(#237):src/infra/legacy-migration.ts(0.1.7 的 settings.yaml / settings.yaml.imported 一次性导入与 .legacy-settings-migrated 标记)、src/index.ts 的调用点、src/config.ts 的 settingsYamlPath() 与手写 YAML 解析器 parseSettingsYaml()(约 145 行)、两个 dev 脚本里的直读段全部删除。设置只剩一条路:profile 的插件配置(Config + apply(ctx, config) 活引用 + settings/document-updated)—— 0.2.1 的设置服务面没有任何 API 能把遗留文档交给插件(importLegacyDocument 私有),而运行时自己会做一次性导入;旧模块既重复,也可能与运行时并发写同一份配置。README / README.zh 的迁移段改写为「导入由 dsh 自己做」;test/core/legacy-migration.test.ts 删除,ast-settings / auto-diag 的解析型用例改为经 schema + applyEffective 断言同一行为。

  • 多文件编辑的部分失败,卡片终于看得见(#247):edit 的 presentationMeta 新增并列的 failures 通道 —— 每个失败文件一条 { path, code?, message, context?, hint? }(与 ErrorMeta 同源、在 src/infra/error-result.ts 只声明一次;path 必填、空表不出现键),顺序与输入一致。它与既有的 error 互斥且穷尽:error = 这次调用零变更,failures = 至少一个文件落盘、至少一个没落;全失败只发 error(其 context 已逐行列出每个文件),全成功两者都不发;不新增派生计数 —— 卡片自己用 diffRowGroups 与 failures 的长度算「N of M files failed」,计数不可能与清单打架。per-file 的 code 改取内层失败的头部 [E_*](原先取消息里最后一个字面量:拒绝会原样带回 ±3 回声,被回显的源码行自带 [E_BAD_SHAPE] 这类字面量时会把卡片显示的码劫持掉),message/context 与单文件错误走同一个切分 splitErrorText。卡片侧:失败文件与成功文件同为 tab(红点 + ErrorCard 面板),上方一条 role="alert" 横幅(「N of M files failed」,每条失败可点进对应 tab),行摘要显示第一个失败路径与计数,而行状态仍是成功(确有变更落盘)。同时修掉两处会让失败整块消失的陷阱:presentationMeta 的空 diff 兜底分支漏发 diffRowGroups(唯一成功文件是整文件 no-op 时会连失败一起丢),以及客户端 metaDiffRowGroups 对零行分组整表返 null(同一种丢失);ErrorCard 的 alert 改为可关(部分失败时由横幅独占播报)。ADR-0015;新增 test/core/partial-failure-meta.test.ts(6 例,含回声劫持夹具)。

  • read 的行窗口改吃锚点,尾巴只剩一句(#245):offset / limit 现在各收正整数或活锚点(闭区间:锚点所在行包含在窗口内;limit 给锚点即“读到这一行为止”,给数字仍是行数),数字与锚点可混用 —— 行号关闭时 grep 只回裸锚点,模型从此能直接说“从这个锚点读到那一行”,续读不再依赖会漂移的行号。窗口总结改为宿主算一次、只有一句:[Lines X-Y of N. End of file.] / [Lines X-Y of N. Use offset="<末行锚点>" to continue.] / 预算截断时 [Lines X-Y of N. Omitted K lines. Use read {resume: …} to continue.];散在渲染器 footer、formatPaginationHint 与字符截断正则三处的旧句式([Showing lines …]、[End of file - total N lines.])全部删除,JSON 与 web 卡片同读宿主投射的 window: {start, end, totalLines}(start 是实际服务的第一行;越界不产出 window;空文件是 {1,1,1} 的真实服务,末行仍是空文件提示)。游标失败复用编辑侧的 served 探针并按 edit 的口径报码:锚点已死 → [E_STALE],served 类异常(没服务过 / 行移了 / 内容变了)→ [E_RANGE_UNVERIFIED]([E_RANGE_STALE] 仍只留给校验和与版本守卫),旧式 line#hash 标记 → [E_BAD_REF];resume 与 offset/limit 同时出现不再被静默偏向 resume,改为 [E_RESUME_CONFLICT]。三个既有 E_RESUME_* 码此前由字符串插值生成、互锁测试看不见,也从未进 README —— 本轮改为字面量表并补进错误码表。ADR-0014;新增 test/core/read-window.test.ts(13 例)钉住两种游标、闭区间、唯一新增硬拒绝与四条尾巴。

  • ⚠️ BREAKING:line_numbers 从工具参数变成用户设置(#244):read / edit / grep / undo_last_edit 不再接受该参数 —— 现在是设置卡片里的一个开关(行号 / Show line numbers,默认关闭),决定模型侧每一行是 <anchor>:<line> 还是裸 <anchor>。行号不该由被观察的一方决定自己看到多少文件结构,这个旋钮属于读者。默认值同时从「开」翻成「关」,所以仍带该字段的调用一律 [E_BAD_SHAPE] 硬拒绝(静默忽略等于用旧语义回答新问题),提示直接指向设置位置。开关只作用于模型侧行渲染:text 行与 JSON 的 {"<marker>": content} 键同源,read 的表头图例与 ANCHOR:LINE|CONTENT 形态一并跟随;web 卡片读结构化 presentationMeta(整数 lineNumber),因而不受开关影响。覆盖 read / grep / edit 文本 diff / undo / ast_grep / write 的自动预览 / lsp —— 全部经同一个标记落点 fmtMarker。新增 test/core/line-numbers-switch.test.ts(8 例)钉住开关两态与四工具的拒绝路径。

  • ADR-0013: refusals by size are retired — all tools stream oversized results in segments (per-response char budget, default 48,000) with resume tokens instead of refusing. GREP_MAX_TOTAL_BYTES and the 100 MiB read gate are gone. grep never returns "No matches" for a file it hasn't searched.

  • grep default context is now context_lines (3) instead of 0 — text and JSON contracts are aligned.

  • 锚点事实回归唯一持久化真相源(契约 §3/§8):hashline/session-anchors.ts 里那个 Map<string, SparseState> + LRU 整体删除(连同 ANCHOR_CACHE_LIMIT、dropAllAnchorState、applyEditToState)。它让纯域层持有 store 作用域的可变状态,并把两个只应由库回答的问题从内存回答:「该文件已分配了哪些锚点」(§2.1 第3条)与「这个锚点还活着吗」(§2.2 条件1)。现在 ensureState 每次都读 anchor_lines,anchorsFor 是纯读(不再 realign、不再写库)。

  • store 耦合的实现搬出 hashline/(契约 §3):hashline/session-anchors.ts(705 行)→ domain/session/anchor-state.ts(持久化端口、事务、anchor_lines 读写、对齐通知);纯逻辑留在 hashline/alloc.ts(SparseState / AnchorEntry / allocateInto / anchorsPure)。hashline/index.ts 的 barrel 不再转发 store 耦合的一半 —— 那正是 tools/ 当初能跨层的口子。解析引擎对状态的反向依赖改为注入式读取器(registerAnchorReader,由 hash-store 装配时接上,无会话时回退纯推导)。验证:grep 确认 hashline/ 内零 domain/ import、全库零 session-anchors 引用;6 个 tools/lsp 文件改从 domain/session/ 引入。

  • 6 处整文件级冗余库读删除:ast_edit / ast_grep(两处)/ lsp(两处)/ auto_diag 原先先做一次「把整文件锚点读成稠密数组」,随后又把其中实际会被读取的那几行全部覆盖成铸造结果 —— 数组里被真正用到的项无一来自那读。改为按行数建空数组、由铸造填。ast_grep 的槽宽也改为按已服务的行计算(原先按整文件算,静默把每条槽拉宽)。

  • write 的 diff 不再二次记 served:allocateForRenderedRows 现在把全部渲染行交给唯一入口(复用的行本就不会重铸),于是一行的锚点与它在 served 里的存在同一事务落定 —— write 工具里那段补记的 recordServed 随之删除(那是同一事实的第二次写,也是 #187 两半可能不一致的入口)。

  • 宿主的 FS_STALE_VERSION 与锚点的 E_RANGE_STALE 共用一码这件事已在契约 / README / README.zh 写明:两者补救手段相同(重新 read 拿新锚点),但只有后者是「校验和 ≠ anchor_meta」的重映射信号。原先文档只说了后者,读码的人会以为标签只有一种含义。

Added

  • read / grep / ast_grep / lsp: resume parameter + continuation tokens. Oversized results return a first segment and a continuation footer; pass the token back to get the next segment.
  • Settings: max_response_chars (per-response char budget, default 48,000, clamp [8,000, 49,984]).
  • New module src/infra/response-stream.ts: spill store + resume tokens + segment assembler.

Fixed

  • diff 渲染在超大单块上抛 RangeError(#229):partsFromRuns 把游程拼回行数组时用 push(...slice) —— 展开会把每个元素当成实参,单块同向连续行达到约 15 万行就撞上 V8 的实参上限(Node 26 实测:10 万行通过、15 万行抛)。爆点在渲染,差分本身已算完(degraded 仍为 false)—— write 一个超大文件、undo 一次超大回滚会直接得到栈溢出而不是 diff。改为逐元素追加(新 pushSlice),输出逐位不变;新增 test/core/line-diff-bounds.test.ts 以 100 万行纯插入/纯删除钉住。

  • Myers maxD 加上硬上限 1,024(#230):trace 是 O(maxD²)(maxD + 1 条 2·maxD + 1 的 int32),而 maxD 可由调用方指定且原本无上限 —— 实测 16,384 时单次调用要 ~1.3 GB。现在在分配点(myersRange)夹取,任何调用方都无法把 trace 撑过 ~8.4 MB;巨型 maxD 与上限本身的行为逐位一致(不再靠碰运气撞 RangeError)。

    • ⚠️ 不采纳 #230 的另一半(默认值 256 → 64):该结论基于单个 800k 行 / 50% 变更的输入测得「快 6 倍且输出逐位相同」,但不普遍成立。反例是 #190 一致性契约里的「整文件重写、尾部幸存」:maxD=64 时预算先耗尽 → 区间被切分 → 幸存行被渲染成 -/+ 而不是 context。test/core/line-diff.test.ts 把这一形状当作与 jsdiff 的逐字段一致性要求,所以 64 是渲染缺陷而非白赚的提速。默认值保持 256;两个数字的不同职责(默认值是质量底线、上限是安全网)写进源码注释,反例由 test/core/line-diff-bounds.test.ts 保存。
  • 取锚收敛为唯一入口(#223):read / grep / lsp / ast_grep / ast_edit / undo / write 各自直接调 allocateForLines / allocateInWorkspace,再另外调一次 recordServed 的时代结束 —— 全部改走 anchorForInWorkspace,取锚与 served 记录在同一事务提交。两次调用之间失败就留下「模型看见了但没有任何会话见过」的锚点(下一次编辑拿它直接被判 never-served),这是 #136/#143 那一类缺陷的孳生地。

    • 会话层的 serve 拆成两半:anchorForInWorkspace(进作用域取锚 + 记 served,并返回锚点供渲染 —— 模型看到的必须就是它能用的那批)与 serveRowsInWorkspace(只剩 observe)。session-view.allocateInWorkspace 删除,已被前者取代。
  • allocateInto 不再在同一次调用里释放旧锚点:那条 used.delete(existing.anchor) 会把刚释放的锚点交给同一批次里的另一行,而模型手里那份仍在自己的 served 里 → 校验放行 → 静默错行编辑(#217 §1 已构造性复现)。

  • 锚点状态写入改为差量:原先每次写回都是「整路径 DELETE + 逐行重插」(#217 实测约 187× 写放大);现在对照 store 现状只 upsert/delete 变化的行,并把写路径收成一条(persistAnchorState)。

  • 关库时同时清掉内存里的锚点状态(已被取代:内存里的锚点状态已整体删除 —— 见 ### Changed 第一条。当时那条修的是「缓存跑在库前面」,而现在根本没有缓存可跑;shutdownHashStore 只需关库,dropAllAnchorState 随之消失)。

  • 编辑路径补全「释放三清」(#223 §4 / #224):原先被替换行的锚点只做了一清 —— 从 anchor_lines 丢掉就结束,既没告诉发起释放的那个会话(served 里那条还在),也没进本次调用的释放池。于是它还能被重新发给另一行,而模型手里那份仍然「活着且已 served」→ 校验放行 → 静默错行编辑。现在 updateAnchorsAfterEdit 报告丢掉的行,reconcileServed 把摘掉的锚点送入释放池,edit 引擎据此同步本会话的 served 镜像。

  • probeLines 的四类 reason 改为按「哪一条判定失败」给出:原先靠「那行现在挂着谁的锚点」反推,那回答的是行的状态而非拒绝原因 —— 一个已被释放、其旧行现在挂着别人锚点的锚点会被报成「文件在你会话期间被改过」(line-changed),而真相是「这个锚点已经没了」(not-live)。

  • undo 改为快照回退(#224):内容、行↔锚点绑定、校验和三者一起退回。原先只退内容,重新拿到的是被撤销那次编辑铸的锚点 —— 模型撤销一次误操作、再重提它本来要做的编辑时,会因为「锚点不是你会话见过的」被拒。现在 UndoRecord 里本就保存的编辑前绑定被整体写回。

  • undo 栈深度 10 → 3:栈是用来从一次误编辑里恢复的,不是历...

Read more

v0.9.5

Choose a tag to compare

@github-actions github-actions released this 29 Sep 01:21

Added

  • grep 默认按 .gitignore 取候选清单(新设置 grep_respect_gitignore,默认开):候选文件清单改由 ripgrep 出(--files --no-require-git --no-ignore-parent),被忽略的树(target/、build/、vendored 副本)根本不进读取集,从根上避开 64 MiB 读取预算那道墙。--no-ignore-parent 是刻意的:只认被搜目录自身的忽略规则,祖先目录(恰好包含该路径的那个仓库)的规则不得把清单悄悄清空。关掉该设置即退回插件自己的走查(隐藏项 + node_modules 跳过、不读忽略文件),rg 不可用时是同一回退;且预算被撑爆而预过滤没参与时,notice 会点名这一点。

Fixed

  • lines 是行数组:一个元素就是一行,[""] 不再变成删除(#198):载荷在进入引擎前被 join("\n") 压成字符串,而字符串形态用同一个 "" 同时表达「没有行(删除)」与空行族 —— 于是 [""] 变成零行(replace 静默删掉该行、ins 变成无操作),["",""] 静默少一行。修法是把数组→文本的桥做成无损编码(新增 encodeText,即 parseText 在数组面的逆):只有全空族改用 "\n".repeat(n) 的写法,其余数组一字不变;op:"del" 仍独占删除语义。resolveIns 的第二处 join 一并修正(锚点行自身为空时,插入空行同样会丢行)。
  • diff 的 - 行改为指向引擎真正移除的那一行(#198):删除行的锚点原本取自文本 diff 的自身对齐(前向贪心),相邻同内容行时它挑中后面那个孪生行 —— 也就是幸存行 —— 于是 - 行报出幸存行的锚点与旧行号,而被指定行的锚点整份 diff 都看不到。现在渲染器接收域层算好的候选范围(replacedOriginalRanges:按 toAnchorHunk 的既有规则排除 ins —— 它的锚点行是被保留的、不构成被移除的行 —— 并升序返回),删除行取其中最早未被认领且内容相同的一行;无 hunk 的全文 diff(write / undo)不带该信息,行为不变。评审时正是这里查出过缺陷:ins 的锚点行一度混进候选池,端到端用例靠 hunk 顺序恰好有利而假绿;修好后用变异测试(临时去掉排除 → 2 例失败)钉住。
  • 新增 test/core/issue-198-lines-semantics.test.ts(16 例):[""] / ["",""] / ["\n"] 的行语义、ins 空行(含锚点行自身为空的形状)、[""] 与 del 可区分、候选池排除 ins 且升序,以及相邻同内容行下 del / replace 的删除行归属;另含穷举用例 —— 长度 ≤ 3、字母表含空串 / 文本 / 裸换行 / CRLF 的全部 156 个数组,期望值由规则推导而非逐例手写。根套件 114 文件 / 1,346 例、client 6 文件 / 148 例、两侧 tsc 与 npm run build(含 client bundle 校验)均绿。
  • grep 的 ripgrep 预过滤:零命中不再当成失败(rg -l 的退出码 1):rg --files-with-matches 用退出码 1 表示「一个都没匹配」,而代码把任何非零退出都当作失败 → 调用方保留全量候选列表 → 用 JS 引擎把整棵树读一遍,正好在最该省的那一种情形(无命中)烧光 64 MiB 读取预算并报「N 个文件未搜索」。现在退出码 1 是一种答案(收窄为空列表,不读任何文件);只有 ENOENT / 超时 / 退出码 2(rg 自身的错误,如模式被拒)才回退 JS 引擎。
  • rg 解析加固 + 降级可观测:@vscode/ripgrep 的平台二进制若未落地(install script 被跳过、可选平台包缺失),rgPath 会指向一个不存在的路径,过去每次调用都 spawn 失败;现在先校验存在性再采用,否则退回 PATH 上的 rg。当预算真的被撑爆、而预过滤没有参与时,notice 会点名这一点,不再把「全量 JS 扫描」伪装成普通结果。
  • 清掉已废弃的 per-file 过滤(#169 只关了开关、没删代码):GREP_MAX_FILE_BYTES、ReadBudget 的 maxFileBytes 维度与 "too-large" 跳过原因一并删除,预算只剩「整次扫描的总量」一个口径;tool-grep 里那句「超限文件会被跳过」的陈旧注释也修正了。
  • 新增 test/core/grep-rg-prefilter.test.ts:退出码分类(1 ≠ 失败)、依赖声明与二进制确实可用、以及真实 rg 下「零命中收窄为空列表而非 undefined」。issue-167 的集成用例同步重写:不命中的巨型文件不再进读取集(这正是报障场景),而命中且总量超预算时仍给出诚实的预算提示。

Docs

  • README 版本支持表与实测数据纠正:三处陈述已被仓库现状证伪 —— ①当前构建/测试 SDK 基线写的是 0.1.6-alpha.1,实际 @deepseek-ai/dsh-* 全部为 0.1.7-alpha.1(0.9.0 起仅支持 0.1.7,0.8.x 留给 0.1.6);②兼容性仍写“由 @deepseek-ai/dsh-settings peer 依赖 + npm 强制”,该依赖已在 0.9.0 移除,现仅 @deepseek-ai/schemastery >=3.18.3;③test/ 注为 1,210 个测试,实测 113 文件 / 1,330 例。中英两版同步修正。
  • 建立 Agent 记忆层:新增根 AGENTS.md(Level 0 入口 + l0_domains 导航表)与 docs/workflows/release.md(tag-first 发布流程从 CLAUDE.md 迁出,单一归属);CLAUDE.md 退化为指向 AGENTS.md 的薄指针,消除双入口;知识变更记入 docs/CHANGELOG-MEMORY.md。同时删去 CLAUDE.md 里早已移除的 batch_edit 工具名。

v0.9.4

Choose a tag to compare

@github-actions github-actions released this 26 Sep 03:28

Fixed

  • 拒绝回显不再改写已分配的锚点(#187 现场故障):回显窗口的分配曾用重建串 fileLines.join("\n")(丢了尾换行)→ ensureState 判 checksum 不符 → 触发一次虚假 realign,把已分配的有效锚点释放、给同一行重铸新锚。现场表现:read 给 ai:405,edit 被拒后回显变成 WQ:405 并反过来报 ai 是 stale。现两处回显分配都改用文件真实内容(verifyServedRange 新增 content 参数、由已有该参数的 applyEdit 传入),拿不到真实内容时跳过分配而不是近似重建。全 src 排查:无重建串调用点残留;四个锚点入口内部规范化,raw/规范化混用无害。