{"_id":"@domitor-syh/dsh-rollback","_rev":"7-825cbc30665dfc0e3b2aa04b9f1cfd1d","name":"@domitor-syh/dsh-rollback","dist-tags":{"latest":"0.4.0"},"versions":{"0.1.0":{"name":"@domitor-syh/dsh-rollback","version":"0.1.0","license":"MIT","_id":"@domitor-syh/dsh-rollback@0.1.0","maintainers":[{"name":"domitor-syh","email":"syh2846579@gmail.com"}],"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-client-ui-conversation","@deepseek-ai/dsh-client-ui-slots"],"platform":"web"}},"dist":{"shasum":"c8b5f49e9ca524407e91e78d7999f4f9d1100c49","tarball":"https://registry.npmjs.org/@domitor-syh/dsh-rollback/-/dsh-rollback-0.1.0.tgz","fileCount":7,"integrity":"sha512-YNAOIpcWHkORT0ow80LlFW92xsVYMfCACGsLGHX4aOKnE79Zf21o4F4an+ujNMj+iz0o/4KRuHjxW+VItfUd2A==","signatures":[{"sig":"MEUCID37vvlSVRgPEx9T+BVo428rtjDGQPwi/uvk5G6h2SjWAiEA+FW6tu04GrLjjTQJzhrxVXELsea1qSL7TlJHI3uwT5A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":66641},"main":"lib/index.js","type":"module","exports":{".":"./lib/index.js","./client":"./lib/client.js","./invariant":"./lib/invariant.js","./package.json":"./package.json"},"scripts":{"test":"vitest run","build":"tsdown","prepare":"tsdown","typecheck":"tsc --noEmit"},"_npmUser":{"name":"domitor-syh","email":"syh2846579@gmail.com"},"_npmVersion":"10.9.8","description":"TRAE-style conversation rollback for DeepSeek Harness: per-turn checkpoints, file restore + in-place conversation truncation, model tool, /rollback command, and a Web rollback button with affected-file diff preview","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","tsdown":"^0.22.2","vitest":"^2.1.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"peerDependencies":{"react":"^18.2.0"},"_npmOperationalInternal":{"tmp":"tmp/dsh-rollback_0.1.0_1788958567530_0.2555449978302544","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@domitor-syh/dsh-rollback","version":"0.2.0","keywords":["deepseek","dsh","rollback","checkpoint","trae","plugin"],"license":"MIT","_id":"@domitor-syh/dsh-rollback@0.2.0","maintainers":[{"name":"domitor-syh","email":"syh2846579@gmail.com"}],"homepage":"https://github.com/domitor-syh/dsh-rollback#readme","bugs":{"url":"https://github.com/domitor-syh/dsh-rollback/issues"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-client-ui-conversation","@deepseek-ai/dsh-client-ui-slots"],"platform":"web"},"engines":{"dsh":">=0.1.1-rc.2 <0.2.0"}},"dist":{"shasum":"0537f30665bf0594344dcdd3eac811286ce252c0","tarball":"https://registry.npmjs.org/@domitor-syh/dsh-rollback/-/dsh-rollback-0.2.0.tgz","fileCount":9,"integrity":"sha512-8XCoQCVfsfphCGrgVBrB4KiZsl00SVPhgqawYdbVfWkAlrNW/HQgLRRyJXGzUzdFiiGrifh6KveZmGjNsLpwDQ==","signatures":[{"sig":"MEUCIDoSa/eKZ4jUPV4s7S/wy12RtwqrX6ynLLGjWahLMvkeAiEAiQQdXU11yIq3Atns2cjjlD2afBa/DWbaFEp4gcjv/IE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQCj2mp1+8WU+Hsjfwp3sS6zZDD4eUGU/j8CLciHxvmiuwIgZhcqSk+kcrUVMkG1mBbMzW7D4AZliXAzgmdnEYroDHw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":159518},"main":"lib/index.js","type":"module","engines":{"node":">=18"},"exports":{".":"./lib/index.js","./client":"./lib/client.js","./invariant":"./lib/invariant.js","./package.json":"./package.json"},"gitHead":"19adc49a41a76503bad0c43d1d901c6dccc759a7","scripts":{"test":"vitest run","build":"tsdown","prepare":"tsdown","typecheck":"tsc --noEmit && node scripts/typecheck-host.mjs","deploy:profile":"node scripts/deploy-profile.mjs","typecheck:host":"node scripts/typecheck-host.mjs"},"_npmUser":{"name":"domitor-syh","email":"syh2846579@gmail.com"},"repository":{"url":"git+https://github.com/domitor-syh/dsh-rollback.git","type":"git"},"_npmVersion":"10.9.8","description":"TRAE-style conversation rollback for DeepSeek Harness: per-turn checkpoints, file restore + in-place conversation truncation, a /rollback command, and a Web rollback button with an affected-file preview","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","tsdown":"^0.22.2","vitest":"^2.1.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"peerDependencies":{"react":"^18.2.0"},"_npmOperationalInternal":{"tmp":"tmp/dsh-rollback_0.2.0_1789834945309_0.28385505566581526","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@domitor-syh/dsh-rollback","version":"0.2.1","keywords":["deepseek","dsh","rollback","checkpoint","trae","plugin"],"license":"MIT","_id":"@domitor-syh/dsh-rollback@0.2.1","maintainers":[{"name":"domitor-syh","email":"syh2846579@gmail.com"}],"homepage":"https://github.com/domitor-syh/dsh-rollback#readme","bugs":{"url":"https://github.com/domitor-syh/dsh-rollback/issues"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-client-ui-conversation","@deepseek-ai/dsh-client-ui-slots"],"platform":"web"},"engines":{"dsh":">=0.1.1-rc.2 <0.2.0"}},"dist":{"shasum":"bc8581c899aeeb52f7da0c5818327554399c529c","tarball":"https://registry.npmjs.org/@domitor-syh/dsh-rollback/-/dsh-rollback-0.2.1.tgz","fileCount":9,"integrity":"sha512-A6OyKP2zXS5mA7pE8PC3krfEty6UueSEeJApWWBMhTrCj6OdJKSloStC4lo9He8lPLv0yxYrPW5p8mapVzVLfQ==","signatures":[{"sig":"MEYCIQDbqzvszOuOkguTbjIetYt5XYY59Cr3zsQgSbLRC3KLsAIhANqBZNMxJCCGxzWlFugKhRNsG3kvVpn+3QYdYUbG8HUQ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIEN8WAFcm6vg1F/bYsyr0S8P4it/9w3ooaVYQ2RnTtPDAiEA6fQtM+YDB6rOcqFiX24So+mEl/Fe66i3qLx+0KDNZEY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":167967},"main":"lib/index.js","type":"module","engines":{"node":">=18"},"exports":{".":"./lib/index.js","./client":"./lib/client.js","./invariant":"./lib/invariant.js","./package.json":"./package.json"},"gitHead":"d5e3ea1ad1740b22db7de57b8dff02c20a3500b5","scripts":{"test":"vitest run","build":"tsdown","prepare":"tsdown","typecheck":"tsc --noEmit && node scripts/typecheck-host.mjs","deploy:profile":"node scripts/deploy-profile.mjs","typecheck:host":"node scripts/typecheck-host.mjs"},"_npmUser":{"name":"domitor-syh","email":"syh2846579@gmail.com"},"repository":{"url":"git+https://github.com/domitor-syh/dsh-rollback.git","type":"git"},"_npmVersion":"10.9.8","description":"TRAE-style conversation rollback for DeepSeek Harness: per-turn checkpoints, file restore + in-place conversation truncation, a /rollback command, and a Web rollback button with an affected-file preview","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","tsdown":"^0.22.2","vitest":"^2.1.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"peerDependencies":{"react":"^18.2.0"},"_npmOperationalInternal":{"tmp":"tmp/dsh-rollback_0.2.1_1789903453900_0.11134407150805958","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"name":"@domitor-syh/dsh-rollback","version":"0.2.2","keywords":["deepseek","dsh","rollback","checkpoint","trae","plugin"],"license":"MIT","_id":"@domitor-syh/dsh-rollback@0.2.2","maintainers":[{"name":"domitor-syh","email":"syh2846579@gmail.com"}],"homepage":"https://github.com/domitor-syh/dsh-rollback#readme","bugs":{"url":"https://github.com/domitor-syh/dsh-rollback/issues"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-client-ui-conversation","@deepseek-ai/dsh-client-ui-slots"],"platform":"web"},"engines":{"dsh":">=0.1.1-rc.2 <0.2.0"}},"dist":{"shasum":"3cd87a03e0611789f14dc3564b42db43d48ca254","tarball":"https://registry.npmjs.org/@domitor-syh/dsh-rollback/-/dsh-rollback-0.2.2.tgz","fileCount":9,"integrity":"sha512-qj/3OtI7u02W7cIP2rnVXsQECNaGpAq8kZBjLOMEWcyAkM3SGb1pAEVTOtj0Ny6L0AbijVur83DhRMG9xJiE+A==","signatures":[{"sig":"MEUCIEe76iZ/c4COHiWZ69T//lpxC+0bePDixvnAjTmCg8VFAiEArWywVrGf/zWHNYiEdGyShBdvbNfP+NapWIoldwNAI5M=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIGJ5ucLDru3iWMQzt24oqO3haHNviU+RDcwyfxJ4/znXAiEAzlKQOEBywBTKPtdOc0khbyqKfph4CE/Mr7TTX2W3zsE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":169143},"main":"lib/index.js","type":"module","engines":{"node":">=18"},"exports":{".":"./lib/index.js","./client":"./lib/client.js","./invariant":"./lib/invariant.js","./package.json":"./package.json"},"gitHead":"f57d53bab3781eae668f35400c658b338456bf8e","scripts":{"test":"vitest run","build":"tsdown","prepare":"tsdown","typecheck":"tsc --noEmit && node scripts/typecheck-host.mjs","deploy:profile":"node scripts/deploy-profile.mjs","typecheck:host":"node scripts/typecheck-host.mjs"},"_npmUser":{"name":"domitor-syh","email":"syh2846579@gmail.com"},"repository":{"url":"git+https://github.com/domitor-syh/dsh-rollback.git","type":"git"},"_npmVersion":"10.9.8","description":"TRAE-style conversation rollback for DeepSeek Harness: per-turn checkpoints, file restore + in-place conversation truncation, a /rollback command, and a Web rollback button with an affected-file preview","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","tsdown":"^0.22.2","vitest":"^2.1.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"peerDependencies":{"react":"^18.2.0"},"_npmOperationalInternal":{"tmp":"tmp/dsh-rollback_0.2.2_1789917189337_0.1876312551574184","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@domitor-syh/dsh-rollback","version":"0.3.0","keywords":["deepseek","dsh","rollback","checkpoint","trae","plugin"],"license":"MIT","_id":"@domitor-syh/dsh-rollback@0.3.0","maintainers":[{"name":"domitor-syh","email":"syh2846579@gmail.com"}],"homepage":"https://github.com/domitor-syh/dsh-rollback#readme","bugs":{"url":"https://github.com/domitor-syh/dsh-rollback/issues"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-client-ui-conversation","@deepseek-ai/dsh-client-ui-slots","@deepseek-ai/dsh-client-ui-chat","@deepseek-ai/dsh-client-ui-renderer","@deepseek-ai/dsh-client-locale","@deepseek-ai/dsh-api-remotes","@deepseek-ai/dsh-api-session-controller","@deepseek-ai/dsh-api-workspace-controller"],"platform":"web"},"engines":{"dsh":">=0.1.5-rc.2 <0.2.0"}},"dist":{"shasum":"c27689c01b922a1920263c1cfb64da154f2ef956","tarball":"https://registry.npmjs.org/@domitor-syh/dsh-rollback/-/dsh-rollback-0.3.0.tgz","fileCount":9,"integrity":"sha512-6FdfqJlECRwmde25V83FCxikWKu09RXjXWSEDYS7mnYzhDfSu2MXU+5Yn6zfbVQ48QBgocT22jYwgG11jZ/DPw==","signatures":[{"sig":"MEUCIQD+1ny5EOjwueic3nEMIpkX3g1sL5kzM02RaxbVX7BEQwIgc0aT1CLBB4126cCVZYTrsB3k6e27sdDooY3h3r9RLic=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIEQOhSIWT86qp8rgOLIBxzlCoX1RvmfBJoz+5X+DaTCGAiEAgcyDpc/sR0z5boonTGLvN4XK1/ie9w6jP1nv2Xx1Bj4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":219907},"main":"lib/index.js","type":"module","engines":{"node":">=18"},"exports":{".":"./lib/index.js","./client":"./lib/client.js","./invariant":"./lib/invariant.js","./package.json":"./package.json"},"gitHead":"031c9ddff2f187d2a3215784c0469a76c5a9bb5c","scripts":{"test":"vitest run","build":"tsdown","prepare":"tsdown","typecheck":"tsc --noEmit && node scripts/typecheck-host.mjs","deploy:profile":"node scripts/deploy-profile.mjs","typecheck:host":"node scripts/typecheck-host.mjs"},"_npmUser":{"name":"domitor-syh","email":"syh2846579@gmail.com"},"repository":{"url":"git+https://github.com/domitor-syh/dsh-rollback.git","type":"git"},"_npmVersion":"10.9.8","description":"TRAE-style conversation rollback for DeepSeek Harness: per-turn checkpoints, file restore + in-place conversation truncation, a /rollback command, and a Web rollback button with an affected-file preview","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","tsdown":"^0.22.2","vitest":"^2.1.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"peerDependencies":{"react":"^18.2.0"},"_npmOperationalInternal":{"tmp":"tmp/dsh-rollback_0.3.0_1790189176196_0.3341609106330816","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@domitor-syh/dsh-rollback","version":"0.3.1","keywords":["deepseek","dsh","rollback","checkpoint","trae","plugin"],"license":"MIT","_id":"@domitor-syh/dsh-rollback@0.3.1","maintainers":[{"name":"domitor-syh","email":"syh2846579@gmail.com"}],"homepage":"https://github.com/domitor-syh/dsh-rollback#readme","bugs":{"url":"https://github.com/domitor-syh/dsh-rollback/issues"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-client-ui-conversation","@deepseek-ai/dsh-client-ui-slots","@deepseek-ai/dsh-client-ui-chat","@deepseek-ai/dsh-client-ui-renderer","@deepseek-ai/dsh-client-locale","@deepseek-ai/dsh-api-remotes","@deepseek-ai/dsh-api-session-controller","@deepseek-ai/dsh-api-workspace-controller"],"platform":"web"},"engines":{"dsh":">=0.1.5-rc.2"}},"dist":{"shasum":"9e062a99da889c6e613653ae0171d44711d49631","tarball":"https://registry.npmjs.org/@domitor-syh/dsh-rollback/-/dsh-rollback-0.3.1.tgz","fileCount":9,"integrity":"sha512-qiLOEIDm54/Fqv8JuwqtvsbDPJs2Jb45Ug9Bf33W377IyaHxn4pqyVFPvKuj+BF/qv0HWCekZGDr4ZP22znlCA==","signatures":[{"sig":"MEYCIQCE0Vlp/VdWlmBlZTQjTV5elIZT9c0O0et3XqEWzw/+/wIhAOby7kjCMh4fKQ2MbCuq0k4UHDQhwl3IhkI7y1pw40fy","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCICkYrD+HC/6NOnoS6MOB2roZUn++rWl58QT3MVQIVPapAiAGAzN6M8MDQCqKCrWuU8ut+Son++HrpD/mnsnpB24zCg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":227842},"main":"lib/index.js","type":"module","engines":{"node":">=18"},"exports":{".":"./lib/index.js","./client":"./lib/client.js","./invariant":"./lib/invariant.js","./package.json":"./package.json"},"gitHead":"3ff9790465ee1cc4b68bd57fe40c0fdfcac7ca1f","scripts":{"test":"vitest run","build":"tsdown","prepare":"tsdown","typecheck":"tsc --noEmit && node scripts/typecheck-host.mjs","deploy:profile":"node scripts/deploy-profile.mjs","typecheck:host":"node scripts/typecheck-host.mjs"},"_npmUser":{"name":"domitor-syh","email":"syh2846579@gmail.com"},"repository":{"url":"git+https://github.com/domitor-syh/dsh-rollback.git","type":"git"},"_npmVersion":"10.9.8","description":"TRAE-style conversation rollback for DeepSeek Harness: per-turn checkpoints, file restore + in-place conversation truncation, a /rollback command, and a Web rollback button with an affected-file preview","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","tsdown":"^0.22.2","vitest":"^2.1.0","typescript":"^5.6.0","@types/node":"^22.0.0"},"peerDependencies":{"react":"^18.2.0"},"_npmOperationalInternal":{"tmp":"tmp/dsh-rollback_0.3.1_1790235107336_0.14664428129925433","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"_id":"@domitor-syh/dsh-rollback@0.4.0","dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-connection","@deepseek-ai/dsh-client-ui-conversation","@deepseek-ai/dsh-client-ui-slots","@deepseek-ai/dsh-client-ui-chat","@deepseek-ai/dsh-client-ui-renderer","@deepseek-ai/dsh-client-locale","@deepseek-ai/dsh-api-remotes","@deepseek-ai/dsh-api-session-controller","@deepseek-ai/dsh-api-workspace-controller"],"platform":"web"},"engines":{"dsh":">=0.1.5-rc.2 <0.2.0-0 || >=0.2.0-rc.2 <0.3.0-0"}},"bugs":{"url":"https://github.com/domitor-syh/dsh-rollback/issues"},"dist":{"shasum":"c90965d762a6115738bc65965fb68f1cde1a49a8","tarball":"https://registry.npmjs.org/@domitor-syh/dsh-rollback/-/dsh-rollback-0.4.0.tgz","fileCount":9,"integrity":"sha512-X8Xb6XwdJ33NIupwI25LmRW26cfa00wvYUmtKdTKRFF1a/QvBtxJfPlAnSy3ceQxl7Ky0vysIUYwmgwU9kFziw==","signatures":[{"sig":"MEQCIEFQvG8FwQWzj4uxiim0KPfkBh6frDtz3GgAPXtCBc4AAiAlyLU4V/RwTpsUW8muHhyNJQvC4RAQOcPNfLUEHcKBYg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCCZzH0DVkyqL2O0aQPsqtvieg9wWKfDIn1PZaFJcR6aQIgRW9xhr59rzRVcC3em0KVfszp4A30R78t/dl2qaSEWzg="}],"unpackedSize":283912},"main":"lib/index.js","name":"@domitor-syh/dsh-rollback","type":"module","engines":{"node":">=18"},"exports":{".":"./lib/index.js","./client":"./lib/client.js","./invariant":"./lib/invariant.js","./package.json":"./package.json"},"gitHead":"69173be107230c7cb1e8953b77036a1a7a929e7a","license":"MIT","scripts":{"test":"vitest run","build":"tsdown","prepare":"tsdown","typecheck":"tsc --noEmit && node scripts/typecheck-host.mjs","check:readmes":"node scripts/check-readmes.mjs","deploy:profile":"node scripts/deploy-profile.mjs","prepublishOnly":"pnpm typecheck && pnpm test && pnpm check:readmes && pnpm build","typecheck:host":"node scripts/typecheck-host.mjs"},"version":"0.4.0","_npmUser":{"name":"domitor-syh","email":"syh2846579@gmail.com"},"homepage":"https://github.com/domitor-syh/dsh-rollback#readme","keywords":["deepseek","dsh","rollback","checkpoint","trae","plugin"],"repository":{"url":"git+https://github.com/domitor-syh/dsh-rollback.git","type":"git"},"_npmVersion":"10.9.8","description":"TRAE-style conversation rollback for DeepSeek Harness: per-turn checkpoints, file restore + in-place conversation truncation, a /rollback command, and a Web rollback button with an affected-file preview","directories":{},"maintainers":[{"name":"domitor-syh","email":"syh2846579@gmail.com"}],"_nodeVersion":"22.23.2","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","tsdown":"^0.22.2","vitest":"^2.1.0","typescript":"^5.6.0","@types/node":"^22.0.0","@types/react":"^18.3.31","@types/react-dom":"^18.3.7","@deepseek-ai/cordis":"4.0.4","@deepseek-ai/dsh-fs":"0.2.0-rc.2","@deepseek-ai/dsh-tools":"0.2.0-rc.2","@deepseek-ai/dsh-session":"0.2.0-rc.2","@deepseek-ai/dsh-commands":"0.2.0-rc.2","@deepseek-ai/dsh-invariants":"0.2.0-rc.2","@deepseek-ai/dsh-system-prompt":"0.2.0-rc.2","@deepseek-ai/dsh-sandbox-policy":"0.2.0-rc.2","@deepseek-ai/dsh-client-ui-primitives":"0.2.0-rc.2"},"peerDependencies":{"react":"^18.2.0","@deepseek-ai/dsh":">=0.1.5-rc.2 <0.2.0-0 || >=0.2.0-rc.2 <0.3.0-0","@deepseek-ai/dsh-session":">=0.1.5-rc.2 <0.2.0-0 || >=0.2.0-rc.2 <0.3.0-0","@deepseek-ai/dsh-invariants":">=0.1.5-rc.2 <0.2.0-0 || >=0.2.0-rc.2 <0.3.0-0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dsh-rollback_0.4.0_1790875511232_0.311259359386554"}}},"time":{"created":"2026-09-09T12:56:07.279Z","modified":"2026-10-01T17:25:11.443Z","0.1.0":"2026-09-09T12:56:07.655Z","0.2.0":"2026-09-19T16:22:25.420Z","0.2.1":"2026-09-20T11:24:13.998Z","0.2.2":"2026-09-20T15:13:09.426Z","0.3.0":"2026-09-23T18:46:16.339Z","0.3.1":"2026-09-24T07:31:47.419Z","0.4.0":"2026-10-01T17:25:11.308Z"},"bugs":{"url":"https://github.com/domitor-syh/dsh-rollback/issues"},"license":"MIT","homepage":"https://github.com/domitor-syh/dsh-rollback#readme","keywords":["deepseek","dsh","rollback","checkpoint","trae","plugin"],"repository":{"url":"git+https://github.com/domitor-syh/dsh-rollback.git","type":"git"},"description":"TRAE-style conversation rollback for DeepSeek Harness: per-turn checkpoints, file restore + in-place conversation truncation, a /rollback command, and a Web rollback button with an affected-file preview","maintainers":[{"name":"domitor-syh","email":"syh2846579@gmail.com"}],"readme":"﻿<div align=\"center\">\r\n\r\n# dsh-rollback · TRAE 式「回退」插件\r\n\r\n<img src=\"./docs/page-display.jpeg\" alt=\"dsh-rollback 界面预览\" width=\"100%\">\r\n\r\n> 🌐 语言 / Language: **中文** · [English](./README.en.md)\r\n\r\n[![Awesome DSH Plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com) [![listed plugins](https://img.shields.io/endpoint?url=https://awesome-dsh-plugin.com/count.json)](https://awesome-dsh-plugin.com) [![npm](https://img.shields.io/npm/v/@domitor-syh/dsh-rollback)](https://www.npmjs.com/package/@domitor-syh/dsh-rollback) [![downloads](https://img.shields.io/npm/dt/@domitor-syh/dsh-rollback)](https://www.npmjs.com/package/@domitor-syh/dsh-rollback) [![MIT License](https://img.shields.io/badge/license-MIT-green)](./LICENSE) [![CI](https://img.shields.io/github/actions/workflow/status/domitor-syh/dsh-rollback/test.yml?branch=main)](https://github.com/domitor-syh/dsh-rollback/actions/workflows/test.yml)\r\n\r\n</div>\r\n\r\n为 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)（DSH）Web 端提供一个 TRAE 式「回退到本轮对话发起前」的插件：按轮次建立检查点，一键把【工作区文件】和【模型上下文】同时回退到某一轮发起之前，保持同一会话 id。\r\n\r\n## 是什么\r\n\r\n`dsh-rollback` 忠实实现了 TRAE 回退功能的核心设计思想——**对话状态与文件状态对齐回滚**：\r\n\r\n- 模型行为由「对话历史」和「工作区文件」共同决定，因此回退必须**同时**回滚两者，否则会出现幻觉延续或状态冲突。\r\n- 回退 = 恢复该轮及之后触碰过的文件（修改→写回原内容，新建→删除）+ 原位截断对话历史（同一 session id，模型不再看到被截断的内容）。\r\n\r\n## 兼容的 DSH 版本\r\n\r\n最新发布（**0.4.0**）在各已发布 DSH 构建上的情况；清单取自 npm registry 上的 `@deepseek-ai/dsh`，覆盖截至本次发布已发布的全部构建：\r\n\r\n| DSH 版本 | 状态 | 说明 |\r\n| --- | --- | --- |\r\n| 0.2.0-rc.2 | **已实测** | 官方桌面版当前捆绑的构建，本次发布适配并验证的目标。两处核心变更都在它上面修掉并复验：`conversation.chat.turnTail` 由 chain 变 list（否则被中断轮次的回退按钮会静默消失）、会话格式 v4 拒收 `source.kind: 'plugin'`（否则回退之后该会话每一轮都失败） |\r\n| 0.1.5-rc.2 | **已实测** | 上一代适配并逐项验证的构建：回退、截断、文件恢复、欢迎页、隐藏、图片回填都在它上面端到端跑通。0.4.0 对它仍是**实测过的那一套行为**——本次两处修复都保留了对旧拼写的识别与回落（`chain` 座位、`{kind:'plugin'}` 标记），所以同一份产物继续服务这一代 |\r\n| 0.1.1-rc.2 | 按设计支持，**未实测** | 插件为这一代契约保留了特性探测回退分支：已退役的 `conversationEvents` 服务、快照上的 `session.chat`、旧拼写的表层 `start`/`end`、`addImages`/`pruneImages`。本机没有 0.1.1 的构建，这条路径**没有在真实运行 0.1.1 的机器上跑过**——它是设计上的回退，不是实测结论 |\r\n| 0.1.1-rc.1、0.1.2-alpha.2–alpha.5、0.1.2-rc.1、0.1.3-alpha.2、0.1.5-alpha.1/alpha.2/rc.1/rc.3、0.1.6-alpha.1/alpha.2、0.1.7-alpha.1/alpha.2/rc.1 | **未验证** | 没有在这些构建上跑过，包括同属 0.1.5 线、晚于 0.1.5-rc.2 发布的 `0.1.5-rc.3`。某个构建若改了契约，插件不会静默失败：它会在控制台点名报错（见下），隐藏逻辑 fail-closed，最坏只退化成「不隐藏」这一外观影响。要在这些版本上试，请挑一个可以丢弃的会话 |\r\n| 0.0.1-rc.1/rc.2/rc.5、0.1.0-rc.2/rc.3/rc.6/rc.7/rc.8（低于 0.1.1） | 不支持 | 低于 0.1.1 没有上述回退所覆盖的契约，且落在兼容区间（`dsh.engines.dsh` 与 `peerDependencies` 同为 `>=0.1.5-rc.2 …`）的下界之外 |\r\n| 0.2.0-rc.1 及更早的 0.2.0 预发布，以及 0.2.0 之后尚未发布的构建 | **未验证** | 只实测过 `0.2.0-rc.2` 这一个构建。0.2.0 线仍在预发布期，契约可能再变；同样的自我保护仍然生效（控制台点名报错、隐藏 fail-closed，最坏只退化成「不隐藏」）。要在这些构建上试，请挑一个可以丢弃的会话 |\r\n\r\n- 在 DSH **0.1.5-rc.2** 上装 **0.3.x**。**不要**把 0.1.0–0.2.2 装到 0.1.5 上：那一代在 0.1.5 上会**弄坏会话加载**——客户端半边把已不再存在的 `conversationEvents` 服务写进必需注入，于是永远停在 pending（表现是「装了什么也没发生」）；宿主半边则在框架恢复会话时从 `session/created` 观察者里抛错（0.1.5 不再交出 `session.events`），结果是会话打不开、对话区渲染为空。\r\n- 在 DSH **0.1.1-rc.2** 上，0.1.0–0.2.2 正是为它开发和验证的一代，因此 **0.2.x 是实测最多的选择**；0.3.0 也按设计兼容（用的就是上表列出的旧契约回退分支），只是没有在 0.1.1 构建上跑过。\r\n\r\n框架契约和插件预期不一致时，插件**大声报错而不是静默失败**：浏览器控制台会打印 `[rollback] framework contract mismatch: …` 或 `[rollback] hiding disabled for this pass: …`。界面隐藏逻辑是 **fail-closed** 的——聊天结构读不出来时它**什么都不隐藏**，并把此前隐藏的内容全部交还，所以未来的框架改动最多退化成「不隐藏」（纯外观影响），永远不会把对话区清空。宿主半侧把它注册的每个观察者都包在自己的 try/catch 里，所以插件出错不会影响宿主自身的会话加载。\r\n\r\n`package.json` 里的 `dsh.engines.dsh` 为 `>=0.1.5-rc.2 <0.2.0-0 || >=0.2.0-rc.2 <0.3.0-0`。**DSH 不读取这个字段**——真正生效的兼容闸门在 `peerDependencies`（见下），它只是给人和其他工具（市场徽章）看的元数据，取值与闸门保持同一个表达式。\r\n\r\n**兼容闸门**（`dsh-app-boot` 内）：只检查 `peerDependencies` 里名字为 `@deepseek-ai/dsh` 或 `@deepseek-ai/dsh-*` 的项，用 `semver.satisfies(runtimeVersion, range, { includePrerelease: true })` 与**当前运行时的版本**比较；**一项都没声明就完全不检查**。本插件因此显式声明了 `@deepseek-ai/dsh`、`@deepseek-ai/dsh-session`、`@deepseek-ai/dsh-invariants` 三项（后两项只是把我们实际耦合的宿主接口写明白，比较对象仍是运行时版本），范围同上。\r\n\r\n这个区间为什么长这样，两处都是必须的：\r\n\r\n- **两段子句，而不是一个开区间下界**。`>=0.1.5-rc.2` 这一个子句在**默认** semver 规则下**不包含** `0.2.0-rc.2`（区间里没有同一 `major.minor.patch` 的预发布比较子）——这正是生态里同类插件普遍写成 OR 链的原因。本插件同时支持 0.1.5 线与 0.2.0 线（旧契约回退分支就是为此存在），所以两条线各写一段。\r\n- **上界写成 `<0.2.0-0` / `<0.3.0-0`，而不是 `<0.2.0` / `<0.3.0`**。闸门带 `includePrerelease`，而预发布版排在自己的正式版**之下**，于是 `<0.3.0` 会**放行** `0.3.0-rc.1`。`<0.3.0-0`（数字标识符低于任何字母标识符）才是「整条下一条线都不要」的正确写法。这道闸门的存在意义就是防止「跑在没见过的格式上导致崩溃或数据丢失」，所以下一条线必须**被拦下并要求重新验证**，而不是静默通过。\r\n\r\n因此**上面这张矩阵才是权威说明**：区间决定闸门放不放行，矩阵说明哪些构建真的跑过。\r\n\r\n## 功能特性\r\n\r\n| 能力 | 说明 |\r\n| --- | --- |\r\n| 按轮次检查点 | 每轮发起前建立检查点，只记录该轮实际触碰的文件（Copy-before-Write 前置内容），非全量快照 |\r\n| 10 轮滑动窗口 | 借鉴 TRAE「仅最近 10 轮」，超出窗口的检查点被丢弃 |\r\n| 文件回退 | 修改过的文件写回本轮前内容；已被删除的文件放回来；本轮新建的文件被删除；无法恢复的文件单独报告跳过 |\r\n| 原位截断 | 用 **`user/message` 承载的表层 `replace`**（内置 `/compact` 同款官方原语）就地替换模型上下文，**回退当场即生效**，保持同一 session id |\r\n| 两种触发入口 | 人工命令 `/rollback`、Web 端每轮结束后的「回退」按钮（正常轮次在回复的动作条上；被中断的轮次在轮次页脚） |\r\n| 受影响文件列表 | Web 按钮弹出对话框，列出本轮及之后受影响文件及动作（恢复/找回/删除/跳过）。这是**只读回执**：行不可点击（早期版本可以点开编辑器，实测没人用），要打开文件走右侧文件树 |\r\n| 运行中禁止回退 | 只要有一轮还在跑就整体拒绝，必须等它结束或暂停（运行中的轮次本来也不会出现按钮） |\r\n\r\n## 快速上手\r\n\r\n### 安装\r\n\r\n```sh\r\ndsh plugin --profile web add @domitor-syh/dsh-rollback\r\n```\r\n\r\n然后重启 `dsh web`。从源码运行 DSH 时：\r\n\r\n```sh\r\npnpm dsh plugin --profile web add @domitor-syh/dsh-rollback\r\n```\r\n\r\n### 使用\r\n\r\n1. **Web 按钮**：每条已完成 AI 回复下方、与「赞/踩」并排的动作条里出现 ↩「回退」按钮 → 弹出受影响文件列表 → 确认回退。\r\n2. **人工命令**：输入框键入 `/`，在指令菜单里选「回退」（英文界面是 `rollback`）→ 弹出官方样式的**轮次选择弹窗**：`回退到上一轮` 加每个可回退轮次一个数字选项。**选择即执行，输入框不会被插入任何文字。**\r\n   - **两种入口**：`/rollback`（不带参数）弹出轮次选择弹窗 → **确认弹窗** → 回退；`/rollback <轮次号>` **直接回退，没有确认弹窗**。\r\n  - 差别来自框架：确认弹窗在客户端，而**带参数的命令行不会经过客户端** —— 客户端条目与装饰只对「裸」命令词生效，宿主命令一旦声明了参数就会把参数直接交给宿主。所以想要确认，就用不带参数的那种。\r\n  - 读信息的辅助命令：`/rollback list` 列轮次、`/rollback preview <n>` 预览回退到第 n 轮前会影响的文件（不执行）。\r\n3. **不提供模型工具**：模型自己调用回退必然发生在\"某一轮进行中\"，而运行中一律禁止回退，所以这个工具无法成立，已移除（回退始终由人发起）。\r\n\r\n## 界面预览\r\n\r\n1. **回退按钮**：每轮结束后出现——正常轮次在回复下方动作条里（与「赞/踩」并排）；**被中断的轮次**在轮次页脚（那一轮没有收尾回复，动作条上不可能有按钮）。运行中按钮保留但置灰。\r\n\r\n   ![回退按钮](./docs/images/rollback-button.png)\r\n\r\n2. **回退弹窗与文件修改提示**：点击 ↩ 后弹出确认框，逐条列出受影响文件及其动作——修改过的写回原内容（`恢复`）、被删掉的放回来（`找回`）、新建的删除（`删除`）、无法恢复的标「跳过」。\r\n\r\n   ![回退弹窗与文件修改提示](./docs/images/rollback-dialog.png)\r\n\r\n3. **被回退的消息从对话流中隐藏**：确认后被回退的消息立刻隐藏（界面上不渲染分隔线）；被回退那一轮的用户文本 / 图片自动回到输入框，方便接着改。\r\n\r\n4. **回退首条消息的界面**：回退到第一条消息之前时，对话区显示「已回退到对话发起前」欢迎页。\r\n\r\n   ![回退首条消息的界面](./docs/images/rollback-hero.png)\r\n\r\n## 架构\r\n\r\n| 文件 | 职责 |\r\n| --- | --- |\r\n| `src/core/` | 纯逻辑（无 DSH 依赖）：检查点模型、捕获合并、回退规划、截断规划、滑动窗口、会话折叠，全部单测覆盖 |\r\n| `src/service.ts` | Host 侧执行：`tools/result` 捕获写/改的前置内容 + `session/event` 折叠轮次；执行恢复/删除/截断 |\r\n| `src/index.ts` | 插件体（Host 半侧）：注册 `rollback` 工具与 `/rollback` 命令 |\r\n| `src/client/index.ts` | 浏览器半侧：官方 `assistant-actions` 槽的「回退」按钮 + 受影响文件对话框 + 本地化，经已出厂 `commands` Remote 触发宿主 |\r\n\r\n关键实现点：\r\n\r\n- **前置内容捕获**：`write`/`edit` 的执行结果里已带 `before`/`after`，用 `ctx.on('tools/result')` 取完整前置内容；`str_replace_editor` 的结果只有渲染文本，改由 `tools/pre-execute` 在调用前预读目标。\r\n- **盘根写入兜底**：Windows 上文件工具无法操作盘符根目录**正下方**的文件——文件系统层写前会先 `mkdir` 父目录，而 `dirname('E:\\\\file.txt')` 是**带尾分隔符**的 `E:\\`，Windows 对卷根 mkdir 返回 EPERM。插件包装 `ctx.fs.writeText` 与 `ctx.fs.editText`：**仅当原路抛出这一精确形状的错误时**，改用「同目录临时文件 + `rename`」落盘（不做 mkdir 预检）。`edit` 分支还逐字复刻了字面匹配语义（`FS_EDIT_NOT_FOUND` / `FS_AMBIGUOUS_EDIT` 的判定与文案）并保留原文件的**行尾风格**与权限位；只包装后端实际实现了的方法。其余错误、以及策略不允许的路径（fail closed）一律按原样抛出。文件系统层若不再预建目录，该分支自动失效。\r\n- **空目录清理**：回退删掉它创建的文件后，把「**已被清空、且创建时间落在被回退时间段内**」的祖先目录一并删除（最深优先；把本次即将删除的子目录视为已不存在，所以整条新目录链会一起清掉）。创建时间用于区分两种情况：目录在第 3 轮创建、文件在第 5 轮创建时——回退到第 5 轮之前**只删文件、保留目录**，回退到第 3 轮之前**两者都删**。创建时间不可得、目录不可读、或仍有内容时一律保留（fail closed）。\r\n- **边界重扫 + 最后已知内容**：捕获只看得到文件工具，所以插件**在每一轮结束时（以及每条用户消息到来时）**复查它监视过的那些路径（状态指纹不变就只 stat、不读），把「被 shell 改写」或「被 shell 删除」的变化记到**该轮**上——模型删完、这一轮一结束就已经记录，**不需要你再多发一条消息**；回退到该轮之前即可恢复（回退在规划前会等待正在进行的扫描，避免\"刚结束就回退\"的竞态）。判定是 fail-closed 的：内容从未被读过就消失的文件**不记录**（记了也只会在恢复阶段被标为无法恢复，而**任何一个无法恢复的文件都会中止整次回退**），只报一次警告；超过 8MiB 的文件不再监视而不是假装能恢复；回退成功后注册表只清\"内容记忆\"、**保留路径继续监视**（否则回退会悄悄丢掉覆盖）。\r\n- **写入偏好提示**：向模型注入一条常驻运行说明——只有被 `write`/`edit` 触碰过的文件才进入回退跟踪，改内容请用这两个工具而不是 shell。\r\n- **原位截断**：对当前 `session.surface.nodes` 中「第 n 轮及之后」的连续节点，append 一条 **`user/message`** 表层 `replace`（`surfaceOp: { op:'replace', start, end }` + `sourceEventSeqs` 覆盖全部被遮蔽节点），就地替换这段历史；会话 id 不变。\r\n  - 标记在 `/rollback` 执行时**当场**写入日志，被回退区间随即从模型历史中消失。\r\n  - 标记内容是一段自动生成的检查点说明，并指示模型不要提及它；再次回退到同一点时，新标记的替换范围覆盖旧标记，只保留一条。\r\n- **界面隐藏**：客户端按标记的替换起点，把被回退区间内的聊天座位隐藏（`display:none`）；隐藏由日志里的持久标记驱动，刷新/重启后保持。\r\n- **回退标签分四类**：`恢复`（文件还在，写回旧内容 · 绿）、`找回`（文件已被删除，重新放回 · 蓝）、`删除`（撤销该区间新建的文件 · 红）、`跳过`（无法恢复）。前三者的区别由记录里的 kind 决定（`updated` / `removed` / `created`），所以\"写回内容\"和\"把文件找回来\"在数据上就是两件事。空列表**不再**显示\"无文件变更\"——插件看不见 shell 直接写出的文件，这种话它保证不了。\r\n- **回退入口分两处，但**每轮只有一个按钮**：正常轮次的按钮仍在**助手回复的动作条**（你熟悉的位置）；只有**被中断的轮次**（没有收尾回复 → 动作条上不可能有按钮）才改由**轮次页脚**提供。页脚条目走官方 `conversation.chat.turnTail` 槽——它是 **chain** 槽，条目**必须提供 `select`**（缺了会抛错且界面毫无痕迹），`select` 返回 null 即不渲染，这正是\"只在动作条无法提供时才出现\"的实现方式；注册带 try/catch 并在控制台留痕，**不允许再出现\"按钮静默消失\"**。\r\n- **运行中一律禁止回退**：只要有一轮处于打开状态就整体拒绝（`src/core/rollback-guard.ts`），无论目标是哪一轮。agent loop 持有表层位置并持续追加，在它下面截断会把\"这一轮正在写的历史\"遮蔽掉、而它更晚的输出还在；命令也不会打断运行，所以**拒绝本身**才是让两者不交错的原因。运行中的轮次**本来就没有按钮**（页脚节点要等该轮 `turn/end` 才存在，动作条要等助手消息收尾），所以这里只有一条规则、没有第二套禁用机制。\r\n- **欢迎页**：把整段对话回退掉之后，由 driver 往对话区注入宿主元素，再用 React portal 把欢迎页渲染进去。\r\n- **客户端传输**：复用已出厂 `ctx.remote.commands.execute` 调 `/rollback …`。\r\n- **回归测试**：14 个文件 / 206 个用例 —— `tests/core.test.ts`（35）、`tests/truncation-plan.test.ts`（29）、`tests/log-replay.test.ts`（18）、`tests/dir-cleanup.test.ts`（15）、`tests/root-write-fallback.test.ts`（15）、`tests/rollback-guard.test.ts`（14）、`tests/root-write.test.ts`（14）、`tests/boundary-scan.test.ts`（13）、`tests/boundary-rescan.test.ts`（13）、`tests/literal-edit.test.ts`（12）、`tests/turn-entry.test.ts`（11）、`tests/sidecar-replay.test.ts`（7）、`tests/boundary-pipeline.test.ts`（5）、`tests/empty-dirs.test.ts`（5）。\r\n\r\n## 已知限制（Known Limitations）\r\n\r\n- **无法恢复的文件会被跳过，而不是中止整次回退**：某个文件恢复不了（前置内容从未记录，或文件系统拒绝：被占用、无权限、沙箱不允许恢复工作区外的路径）时，插件**照做能做的**、**对话照样截断**，并在结果里列出跳过项。这是刻意的取舍——之前\"任何文件失败就整次中止\"会导致：文件已部分恢复、对话没截断、而卡住的原因往往不会自己消失 → 反复失败 → 用户被彻底卡住。跳过项在**确认前**的预览里就标着「跳过」，所以是知情选择；而且**被跳过路径的记录会保留**，下次回退会再试一次（文件锁释放后就能恢复）✓\r\n- **超出保留范围的回退目标会被拒绝**：检查点是 10 轮滑动窗口，更早的状态无法重建。GUI 里超出范围的按钮**置灰**（不额外加提示——置灰本身就是说明），命令返回报错并给出可回退范围——此前它会**静默按最旧的保留记录恢复**（错的状态、无提示）✗\r\n- **聊天轨迹仍保留已回退的消息**：DSH 的聊天轨迹按 append-origin 事件渲染，而日志本身 append-only、无法改写；表层 `replace` 只作用于**模型上下文**。插件在界面层把被回退区间隐藏（由日志里的持久标记驱动，刷新/重启后保持）。\r\n- **模型会读到一行检查点文字**：轮次之间写不出\"模型不可见\"的标记（那需要一个已开启的 step），所以模型会读到那段检查点说明，约 **34 token**（136 字符；压缩前是 331 字符 / 约 83 token）。这段文字**会一直留在模型上下文里直到本次会话结束**（只有下一次回退会替换它），所以每个后续请求都要付一次——因此措辞写到最紧，同时保住四件事：① 它是机器生成的、不是用户说的；② 此后的消息已移除；③ 工作区文件同时被还原；④ 从剩下的内容继续、**不要在意也不要提及这个检查点**。`tests/truncation-plan.test.ts` 用一条字符预算断言钉住它，防止以后又长回去。\r\n- **检查点为进程内存态 + 20 轮 sidecar**：折叠状态随会话对象存于内存（`WeakMap`）；重启后由 sidecar（`storages/dsh-rollback/checkpoints-v2/`）重建，保留最近 20 轮（`KEEP_TURNS`），更早的记录在加载时被剪枝。\r\n- **新建文件删除与空目录清理走本地文件系统**：文件系统抽象层没有删除原语，删除通过 `processPath` + Node `unlink`、空目录清理通过 Node `rmdir` 完成，仅对本地后端可靠。\r\n- **回退后带路径的附件（参考芯片）不会回到输入框**：文字与图片都会回填，唯独参考芯片回不来——框架把输入框建模为 `{ draft, occurrences, attachmentIds }`，但只对插件开放**后两项的登记动作**，而**芯片**唯一入口是 `@` 提及协议返回的引用，插件没有任何接口能主动插入一个芯片。同类里最成熟的 `dsh-recall-plugin` 同样做不到（其引用相关命中均为 0）。因此这里只回填文字与图片，**不把路径当纯文本塞进输入框**（那会改变你要发出去的内容）。\r\n- **用 `@路径` 提的文件，回退后回来的只有那段文字**：这条是上一条的实测版。`@路径` 提及在消息里就是**一个 `text` 块**（实测：`blocks: {text: 1}`，内容形如 `@C:\\dir\\file.dll 这是什么文件`），**框架不会把它变成上传附件**，所以插件既看不到附件、也无从重建芯片——回退后你看到的是**原样的 `@路径` 文字**。这与\"附件没回来\"看起来一样，但原因不同：**那个附件从来就不存在**。诊断方式：回退后控制台会打印 `[rollback] rolled-back turn message: { turn, blocks, restorable, beforeRead }` —— `blocks` 里没有 `file`/`image` 就说明这一轮消息里没有附件。\r\n- **明确声明**：**没有任何官方接口能把「文件」送回输入框**。插件能重建的只有**图片**（用 `uiConversation.imageUrl` 取回字节 + `conversation.createDrafts` 登记草稿 + `addAttachments` 挂回，这条路已实测可用 ✓）。对**文件**，官方面只提供了同一套 `createDrafts`（它会触发一次上传），插件**已经照做并且会重试 append**，但凡框架没有把该文件留成 durable attachment（例如输入框里那个\"芯片\"其实是 `@路径` 引用），就**没有任何办法**把它变回输入框里的芯片——这不是本插件缺了什么，而是插件可用的接口里不存在这条路。同类插件 `dsh-recall-plugin` 对文件同样是尝试 + 兜底提示（其文案写明\"官方 API 只读得回图片\"），也没有别的招。\r\n- **回退不可撤销**：执行即替换历史，不提供 redo 链。\r\n- **shell 新建的文件、以及从未被文件工具碰过的文件不在回退范围**：插件只从 `write`/`edit` 的结果里捕获改动，并在消息边界复查这些路径——所以「shell 改写了/删除了**已登记**文件」（含工作区外，如 `E:\\`、桌面）**可以**回退；而「shell 新建一个全新文件」或「改动一个从未被工具碰过的文件」无法回退（后者连它存在过都不知道）。插件会注入提示引导模型改走文件工具，但无法强制。\r\n\r\n## 开发\r\n\r\n```sh\r\npnpm install        # 安装依赖（prepare 会先构建一次）\r\npnpm build          # 从 src/ 产出 lib/index.js、lib/invariant.js、lib/client.js\r\npnpm test           # 运行核心逻辑（src/core/）单测\r\npnpm typecheck      # tsc 检查 core + tests；scripts/typecheck-host.mjs 再检查\r\n                    #     宿主与客户端面向 DSH 的 5 个文件（src/dsh-types.ts、\r\n                    #     src/service.ts、src/index.ts、src/invariant.ts、\r\n                    #     src/client/index.ts），**一条诊断都不静音**\r\npnpm check:readmes  # 中英两个 README 的结构与语言无关事实必须一致\r\npnpm deploy:profile # 打包并部署到 DSH profile（默认 web），随后需重启 DSH\r\n```\r\n\r\n> ⚠️ **改了代码必须 `pnpm deploy:profile`**：DSH 不是从本仓库加载插件，而是从 profile 的\r\n> `node_modules` 加载 `file:` tarball 解出来的副本（见 `scripts/deploy-profile.mjs` 的说明）。\r\n> 只跑 `pnpm build` 改的是本仓库的 `lib/`，**运行时毫无变化**。脚本会打包、更新 profile 引用的\r\n> tarball、覆盖已解出的副本并校验字节一致，最后提醒重启 DSH 与刷新页面——而**重启必须由你手动做**：\r\n> 宿主半侧在启动时载入，脚本无法替正在运行的进程换代码。\r\n\r\n## 许可证\r\n\r\n[MIT](./LICENSE)\r\n","readmeFilename":"README.md"}