dsh-config-manager 0.1.71 → 0.1.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (548) hide show
  1. package/README.md +111 -96
  2. package/README.zh-CN.md +207 -86
  3. package/lib/adapters/agent-instructions.js +1 -1
  4. package/lib/adapters/file-collection.js +1 -1
  5. package/lib/adapters/index.d.ts +1 -1
  6. package/lib/adapters/index.js +1 -1
  7. package/lib/adapters/link-report.d.ts +1 -0
  8. package/lib/adapters/link-report.js +27 -3
  9. package/lib/adapters/messages.d.ts +18 -0
  10. package/lib/adapters/messages.js +50 -0
  11. package/lib/adapters/plugin-files.js +3 -2
  12. package/lib/adapters/plugins.js +4 -3
  13. package/lib/adapters/prompts.d.ts +1 -0
  14. package/lib/adapters/prompts.js +7 -1
  15. package/lib/adapters/self.d.ts +7 -1
  16. package/lib/adapters/self.js +1 -1
  17. package/lib/adapters/sessions.d.ts +1 -0
  18. package/lib/adapters/sessions.js +4 -2
  19. package/lib/adapters/ui.d.ts +2 -2
  20. package/lib/adapters/ui.js +25 -25
  21. package/lib/adapters/workspaces.d.ts +1 -0
  22. package/lib/adapters/workspaces.js +7 -3
  23. package/lib/cli/actions.d.ts +9 -1
  24. package/lib/cli/actions.js +5 -2
  25. package/lib/cli/help.js +23 -10
  26. package/lib/cli/index.d.ts +23 -0
  27. package/lib/cli/index.js +90 -32
  28. package/lib/cli/sessions-inspect.js +3 -1
  29. package/lib/cli/sessions-repair.js +25 -6
  30. package/lib/cli/web/page.d.ts +12 -0
  31. package/lib/cli/web/page.js +21 -12
  32. package/lib/cli/web/routes.js +2 -2
  33. package/lib/cli/web/server.js +4 -4
  34. package/lib/client.d.ts +1349 -490
  35. package/lib/client.js +43047 -36738
  36. package/lib/core/backup-plan.js +30 -17
  37. package/lib/core/backup-verify.js +24 -23
  38. package/lib/core/backup.d.ts +5 -0
  39. package/lib/core/backup.js +51 -25
  40. package/lib/core/boot-rescue.js +4 -3
  41. package/lib/core/boot-safety.js +2 -1
  42. package/lib/core/command-rollback.d.ts +178 -0
  43. package/lib/core/command-rollback.js +289 -0
  44. package/lib/core/consult-source.js +7 -6
  45. package/lib/core/disk-usage.d.ts +74 -10
  46. package/lib/core/disk-usage.js +67 -4
  47. package/lib/core/engine-locale.d.ts +87 -0
  48. package/lib/core/engine-locale.js +125 -0
  49. package/lib/core/exporter.js +3 -3
  50. package/lib/core/fork-reconcile.d.ts +128 -0
  51. package/lib/core/fork-reconcile.js +150 -0
  52. package/lib/core/journal.js +6 -5
  53. package/lib/core/local-plugin-host.js +3 -2
  54. package/lib/core/local-plugin-pack.js +11 -6
  55. package/lib/core/messages-core.d.ts +292 -0
  56. package/lib/core/messages-core.js +615 -0
  57. package/lib/core/messages.d.ts +60 -2
  58. package/lib/core/messages.js +116 -13
  59. package/lib/core/migration-consult.js +21 -20
  60. package/lib/core/migration-history.js +19 -5
  61. package/lib/core/model-tools.d.ts +0 -22
  62. package/lib/core/model-tools.js +50 -27
  63. package/lib/core/phase3-host.js +30 -5
  64. package/lib/core/plugin-cli.js +18 -19
  65. package/lib/core/reconcile.d.ts +2 -14
  66. package/lib/core/reconcile.js +87 -17
  67. package/lib/core/recovery-orchestrator.js +19 -18
  68. package/lib/core/reinstall.d.ts +4 -0
  69. package/lib/core/reinstall.js +26 -21
  70. package/lib/core/restore.js +20 -17
  71. package/lib/core/rollback-ignore-rules.d.ts +31 -0
  72. package/lib/core/rollback-ignore-rules.js +140 -0
  73. package/lib/core/rollback-point.d.ts +1208 -0
  74. package/lib/core/rollback-point.js +2993 -0
  75. package/lib/core/run-registry.js +6 -5
  76. package/lib/core/session-health.d.ts +15 -0
  77. package/lib/core/session-meta.d.ts +42 -2
  78. package/lib/core/session-meta.js +140 -1
  79. package/lib/core/session-title.d.ts +28 -0
  80. package/lib/core/session-title.js +185 -0
  81. package/lib/core/startup-barrier.d.ts +24 -0
  82. package/lib/core/startup-barrier.js +22 -0
  83. package/lib/core/transaction-coordinator.js +4 -3
  84. package/lib/core/types.d.ts +18 -1
  85. package/lib/core/validator.js +5 -4
  86. package/lib/core/verify-recovery.js +31 -30
  87. package/lib/foreign/bundle.js +3 -2
  88. package/lib/foreign/continue.js +2 -1
  89. package/lib/foreign/crush.js +2 -1
  90. package/lib/foreign/gemini.js +2 -1
  91. package/lib/foreign/goose.js +2 -1
  92. package/lib/foreign/kilocode.js +2 -1
  93. package/lib/foreign/kimi.js +2 -1
  94. package/lib/foreign/messages.d.ts +15 -0
  95. package/lib/foreign/messages.js +40 -0
  96. package/lib/foreign/mimocode.js +2 -1
  97. package/lib/foreign/openclaw.js +2 -1
  98. package/lib/foreign/opencode.js +2 -1
  99. package/lib/foreign/pi.js +2 -1
  100. package/lib/foreign/qoder.js +2 -1
  101. package/lib/foreign/qwen.js +2 -1
  102. package/lib/foreign/registry.js +7 -6
  103. package/lib/foreign/teleagent.js +2 -1
  104. package/lib/foreign/workbuddy.js +2 -1
  105. package/lib/foreign/zcode.js +2 -1
  106. package/lib/foreign/zed.js +2 -1
  107. package/lib/index.d.ts +223 -7
  108. package/lib/index.js +729 -54
  109. package/lib/market/git-file-writer.js +20 -19
  110. package/lib/market/github-repos.js +35 -19
  111. package/lib/market/index-parser.js +31 -30
  112. package/lib/market/messages-market.d.ts +138 -0
  113. package/lib/market/messages-market.js +305 -0
  114. package/lib/market/my-repo.d.ts +2 -2
  115. package/lib/market/my-repo.js +32 -29
  116. package/lib/market/prepare.js +18 -15
  117. package/lib/market/security.js +20 -19
  118. package/lib/market/types.js +2 -1
  119. package/lib/market/url.js +2 -1
  120. package/lib/migrations/index.js +6 -5
  121. package/lib/migrations/messages-migrations.d.ts +32 -0
  122. package/lib/migrations/messages-migrations.js +58 -0
  123. package/lib/profiles/dsh-profile-launcher.js +2 -1
  124. package/lib/profiles/dsh-profile-runtime.js +2 -1
  125. package/lib/profiles/messages.d.ts +7 -0
  126. package/lib/profiles/messages.js +27 -0
  127. package/lib/routes/backup.js +18 -2
  128. package/lib/routes/import.js +3 -3
  129. package/lib/routes/index.d.ts +1 -1
  130. package/lib/routes/kit.d.ts +3 -2
  131. package/lib/routes/kit.js +6 -1
  132. package/lib/routes/market.js +1 -1
  133. package/lib/routes/messages.d.ts +42 -0
  134. package/lib/routes/messages.js +100 -0
  135. package/lib/routes/profiles.js +12 -6
  136. package/lib/routes/recovery.js +378 -114
  137. package/lib/routes/snapshots.js +51 -4
  138. package/lib/routes/sync.js +68 -16
  139. package/lib/schema/config.d.ts +11 -1
  140. package/lib/schema/config.js +52 -20
  141. package/lib/schema/index.d.ts +8 -6
  142. package/lib/schema/index.js +8 -6
  143. package/lib/schema/manifest.js +19 -18
  144. package/lib/schema/messages.d.ts +47 -0
  145. package/lib/schema/messages.js +110 -0
  146. package/lib/schema/section-registry.d.ts +41 -14
  147. package/lib/schema/section-registry.js +35 -29
  148. package/lib/schema/versions.js +6 -5
  149. package/lib/security/encryption.js +16 -15
  150. package/lib/security/integrity.js +8 -7
  151. package/lib/security/messages.d.ts +30 -0
  152. package/lib/security/messages.js +77 -0
  153. package/lib/security/redaction.js +12 -0
  154. package/lib/security/secret-scanner.js +16 -13
  155. package/lib/security/vault.js +2 -1
  156. package/lib/security/zip-security.js +4 -3
  157. package/lib/sync/ancestor.js +2 -1
  158. package/lib/sync/autosync-config.d.ts +11 -0
  159. package/lib/sync/autosync-config.js +12 -0
  160. package/lib/sync/autosync-scheduler.d.ts +11 -1
  161. package/lib/sync/autosync-scheduler.js +115 -76
  162. package/lib/sync/backup-files.js +3 -2
  163. package/lib/sync/backup-scheduler.js +6 -5
  164. package/lib/sync/blob-store.js +2 -1
  165. package/lib/sync/git/git-transport.d.ts +55 -0
  166. package/lib/sync/git/git-transport.js +152 -2
  167. package/lib/sync/github-auth.js +10 -9
  168. package/lib/sync/layout.js +13 -12
  169. package/lib/sync/messages.d.ts +63 -0
  170. package/lib/sync/messages.js +149 -0
  171. package/lib/sync/review-queue.js +6 -5
  172. package/lib/sync/s3/messages.d.ts +7 -0
  173. package/lib/sync/s3/messages.js +16 -0
  174. package/lib/sync/s3/s3-providers.d.ts +4 -1
  175. package/lib/sync/s3/s3-providers.js +5 -5
  176. package/lib/sync/s3/s3-transport.js +1 -1
  177. package/lib/sync/s3/sigv4.js +2 -1
  178. package/lib/sync/snapshot-crypto.js +6 -5
  179. package/lib/sync/snapshot-json.js +3 -2
  180. package/lib/sync/sync-engine.d.ts +37 -15
  181. package/lib/sync/sync-engine.js +67 -37
  182. package/lib/sync/sync-history.js +7 -6
  183. package/lib/sync/sync-selection.d.ts +15 -4
  184. package/lib/sync/sync-selection.js +17 -5
  185. package/lib/sync/webdav/webdav-transport.d.ts +1 -0
  186. package/lib/sync/webdav/webdav-transport.js +4 -1
  187. package/lib/ui/artifact-view.js +4 -3
  188. package/lib/ui/commands.d.ts +1 -1
  189. package/lib/ui/commands.js +10 -0
  190. package/lib/ui/disk-usage-view.d.ts +33 -4
  191. package/lib/ui/disk-usage-view.js +29 -1
  192. package/lib/ui/errors.d.ts +1 -1
  193. package/lib/ui/errors.js +4 -3
  194. package/lib/ui/export-flow.js +24 -20
  195. package/lib/ui/foreign-view.d.ts +5 -1
  196. package/lib/ui/foreign-view.js +26 -2
  197. package/lib/ui/i18n.d.ts +65 -0
  198. package/lib/ui/i18n.js +114 -0
  199. package/lib/ui/import-wizard.d.ts +7 -0
  200. package/lib/ui/import-wizard.js +13 -10
  201. package/lib/ui/progress.d.ts +2 -2
  202. package/lib/ui/progress.js +1 -2
  203. package/lib/ui/report.d.ts +5 -5
  204. package/lib/ui/report.js +6 -6
  205. package/lib/ui/rollback-view.d.ts +756 -0
  206. package/lib/ui/rollback-view.js +724 -0
  207. package/lib/ui/selection-model.d.ts +41 -0
  208. package/lib/ui/selection-model.js +30 -0
  209. package/lib/ui/session-inventory-view.d.ts +97 -3
  210. package/lib/ui/session-inventory-view.js +76 -2
  211. package/lib/ui/session-issue-labels.d.ts +43 -0
  212. package/lib/ui/session-issue-labels.js +48 -0
  213. package/lib/ui/sync-settings-view.d.ts +33 -0
  214. package/lib/ui/sync-settings-view.js +40 -0
  215. package/lib/ui/types.d.ts +8 -3
  216. package/lib/ui/types.js +1 -1
  217. package/lib/utils/atomic-write.js +6 -5
  218. package/lib/utils/bundle-scan.d.ts +11 -1
  219. package/lib/utils/bundle-scan.js +12 -1
  220. package/lib/utils/env-lock.d.ts +10 -0
  221. package/lib/utils/env-lock.js +73 -46
  222. package/lib/utils/json.js +6 -5
  223. package/lib/utils/messages-utils.d.ts +96 -0
  224. package/lib/utils/messages-utils.js +216 -0
  225. package/lib/utils/proxy.js +18 -5
  226. package/lib/utils/recursive-walk.d.ts +22 -0
  227. package/lib/utils/recursive-walk.js +75 -1
  228. package/lib/utils/session-log-repair.js +10 -9
  229. package/lib/utils/zip.js +27 -26
  230. package/package.json +12 -2
  231. package/src/adapters/agent-instructions.ts +1 -1
  232. package/src/adapters/file-collection.test.ts +1 -0
  233. package/src/adapters/file-collection.ts +1 -1
  234. package/src/adapters/files.test.ts +27 -0
  235. package/src/adapters/index.ts +1 -1
  236. package/src/adapters/link-report.test.ts +22 -2
  237. package/src/adapters/link-report.ts +29 -4
  238. package/src/adapters/messages.ts +54 -0
  239. package/src/adapters/plugin-files.ts +3 -2
  240. package/src/adapters/plugins.ts +4 -3
  241. package/src/adapters/prompts.ts +7 -1
  242. package/src/adapters/self.test.ts +26 -0
  243. package/src/adapters/self.ts +8 -2
  244. package/src/adapters/sessions.ts +4 -2
  245. package/src/adapters/settings.test.ts +38 -1
  246. package/src/adapters/ui.test.ts +2 -2
  247. package/src/adapters/ui.ts +27 -26
  248. package/src/adapters/workspaces.ts +7 -3
  249. package/src/cli/actions.test.ts +28 -0
  250. package/src/cli/actions.ts +11 -2
  251. package/src/cli/help.ts +23 -10
  252. package/src/cli/index-guards.test.ts +193 -0
  253. package/src/cli/index.ts +106 -31
  254. package/src/cli/sessions-inspect.test.ts +15 -1
  255. package/src/cli/sessions-inspect.ts +3 -1
  256. package/src/cli/sessions-repair.test.ts +84 -0
  257. package/src/cli/sessions-repair.ts +33 -7
  258. package/src/cli/web/console-route-parity.test.ts +115 -0
  259. package/src/cli/web/page.ts +23 -12
  260. package/src/cli/web/routes.ts +2 -1
  261. package/src/cli/web/server.ts +4 -4
  262. package/src/cli/web/web.test.ts +2 -2
  263. package/src/client/ConfigManagerSection.tsx +30 -2
  264. package/src/client/about/AboutPanel.tsx +2 -2
  265. package/src/client/about/PluginUpdateDialog.tsx +1 -1
  266. package/src/client/about/ReleaseNotesDialog.tsx +2 -2
  267. package/src/client/about/about-view.test.ts +13 -3
  268. package/src/client/about/about-view.ts +10 -6
  269. package/src/client/about/release-notes-view.test.ts +14 -4
  270. package/src/client/about/release-notes-view.ts +4 -2
  271. package/src/client/api.ts +18 -6
  272. package/src/client/chat/ChatEntryView.tsx +391 -0
  273. package/src/client/chat/RollbackDiffTab.tsx +61 -0
  274. package/src/client/chat/RollbackFileIcon.tsx +96 -0
  275. package/src/client/chat/RollbackReviewDialog.tsx +591 -0
  276. package/src/client/chat/chat-entry-view.test.ts +1174 -0
  277. package/src/client/chat/chat-entry-view.ts +758 -0
  278. package/src/client/chat/chat-locales.ts +152 -0
  279. package/src/client/chat/chat-node-registration.ts +64 -0
  280. package/src/client/chat/chat-point-window.ts +173 -0
  281. package/src/client/chat/chat-slots.ts +107 -0
  282. package/src/client/chat/rollback-diff-tab.test.ts +105 -0
  283. package/src/client/chat/rollback-diff-tab.ts +164 -0
  284. package/src/client/chat/rollback-entry-view.test.ts +1469 -0
  285. package/src/client/chat/rollback-entry-view.ts +941 -0
  286. package/src/client/chat/sidebar-slots.ts +37 -0
  287. package/src/client/common/ConfirmDialog.tsx +7 -3
  288. package/src/client/common/ContentPicker.tsx +48 -10
  289. package/src/client/common/DiffTable.tsx +97 -0
  290. package/src/client/common/ErrorBanner.tsx +4 -4
  291. package/src/client/common/Icon.tsx +2 -0
  292. package/src/client/common/Modal.tsx +8 -1
  293. package/src/client/common/ProgressBar.tsx +8 -2
  294. package/src/client/common/ReportView.tsx +4 -4
  295. package/src/client/common/RunsCenter.tsx +1 -0
  296. package/src/client/common/client-route-parity.test.ts +78 -0
  297. package/src/client/common/info-hint-guard.test.ts +46 -13
  298. package/src/client/common/picker-lazy-units.test.ts +42 -0
  299. package/src/client/common/plan-text-redaction.test.ts +17 -6
  300. package/src/client/common/progress-view-edge.test.ts +8 -7
  301. package/src/client/common/progress-view.test.ts +9 -9
  302. package/src/client/common/progress-view.ts +2 -2
  303. package/src/client/common/routes.ts +28 -0
  304. package/src/client/common/ui.tsx +73 -10
  305. package/src/client/config-manager.module.css +996 -21
  306. package/src/client/css-scale-guard.test.ts +30 -0
  307. package/src/client/environment/EnvironmentPanel.tsx +11 -4
  308. package/src/client/environment/maintenance/DiskUsageCard.tsx +23 -2
  309. package/src/client/export/ExportView.tsx +13 -8
  310. package/src/client/history/HistoryPanel.tsx +4 -3
  311. package/src/client/history/history-api.ts +2 -2
  312. package/src/client/history/history-locales.ts +5 -1
  313. package/src/client/home/HomePanel.tsx +29 -8
  314. package/src/client/import/ForeignImportView.tsx +4 -5
  315. package/src/client/import/ImportWizardView.tsx +5 -3
  316. package/src/client/import/import-wizard-split.test.ts +3 -1
  317. package/src/client/import/import-wizard-steps.tsx +9 -9
  318. package/src/client/index.ts +186 -3
  319. package/src/client/library/LibraryActions.tsx +32 -7
  320. package/src/client/library/LibraryPanel.tsx +23 -3
  321. package/src/client/library/RestorePlanView.tsx +9 -66
  322. package/src/client/locales.ts +54 -5
  323. package/src/client/lucide-icons.d.ts +1 -0
  324. package/src/client/market/MarketPanel.tsx +13 -2
  325. package/src/client/market/MyConfigsView.tsx +5 -4
  326. package/src/client/market/MyConfigsWizard.tsx +10 -8
  327. package/src/client/market/market-api.test.ts +9 -8
  328. package/src/client/market/market-api.ts +2 -2
  329. package/src/client/market/market-locales.ts +3 -0
  330. package/src/client/market/market-view.test.ts +6 -6
  331. package/src/client/market/market-view.ts +3 -3
  332. package/src/client/market/my-configs-api.ts +2 -2
  333. package/src/client/market/my-configs-view.test.ts +23 -22
  334. package/src/client/market/my-configs-view.ts +8 -8
  335. package/src/client/recovery/IncompleteCopiesSection.tsx +2 -1
  336. package/src/client/recovery/RecoveryPanel.tsx +442 -283
  337. package/src/client/recovery/RollbackCard.tsx +665 -0
  338. package/src/client/recovery/RollbackDiffPane.tsx +58 -0
  339. package/src/client/recovery/RollbackFileChanges.tsx +163 -0
  340. package/src/client/recovery/incident-api.ts +2 -2
  341. package/src/client/recovery/recovery-api.ts +116 -6
  342. package/src/client/recovery/recovery-locales.test.ts +299 -0
  343. package/src/client/recovery/recovery-locales.ts +488 -149
  344. package/src/client/recovery/recovery-panel-layout.test.ts +132 -2
  345. package/src/client/recovery/rollback-file-changes.test.ts +73 -0
  346. package/src/client/recovery/session-dialog-mount.test.ts +54 -0
  347. package/src/client/run-store.ts +84 -21
  348. package/src/client/sync/AutosyncCard.tsx +25 -1
  349. package/src/client/sync/ChannelClearConfirmDialog.tsx +6 -3
  350. package/src/client/sync/ChannelConfigDialog.tsx +10 -10
  351. package/src/client/sync/DecryptPasswordCard.tsx +2 -2
  352. package/src/client/sync/SecurityOptionsCard.tsx +27 -17
  353. package/src/client/sync/SyncChannelEntryCard.tsx +14 -2
  354. package/src/client/sync/SyncConfirmView.tsx +46 -8
  355. package/src/client/sync/SyncLogList.tsx +1 -1
  356. package/src/client/sync/SyncPanel.tsx +31 -23
  357. package/src/client/sync/SyncRepositoryPicker.tsx +3 -5
  358. package/src/client/sync/SyncSectionPickerDialog.tsx +11 -2
  359. package/src/client/sync/SyncSettingsView.test.ts +20 -1
  360. package/src/client/sync/UnreadableSnapshotsBanner.tsx +1 -1
  361. package/src/client/sync/WorkDirCard.tsx +112 -0
  362. package/src/client/sync/history-model.test.ts +42 -9
  363. package/src/client/sync/history-model.ts +30 -15
  364. package/src/client/sync/sync-api.test.ts +57 -33
  365. package/src/client/sync/sync-api.ts +80 -6
  366. package/src/client/sync/sync-locales.ts +104 -3
  367. package/src/client/sync/sync-view-v2.test.ts +65 -13
  368. package/src/client/sync/sync-view.test.ts +107 -52
  369. package/src/client/sync/sync-view.ts +124 -41
  370. package/src/core/backup-plan.test.ts +28 -0
  371. package/src/core/backup-plan.ts +29 -17
  372. package/src/core/backup-verify.ts +24 -23
  373. package/src/core/backup.test.ts +76 -0
  374. package/src/core/backup.ts +55 -25
  375. package/src/core/boot-rescue.ts +4 -3
  376. package/src/core/boot-safety.ts +2 -1
  377. package/src/core/command-rollback.test.ts +536 -0
  378. package/src/core/command-rollback.ts +402 -0
  379. package/src/core/consult-source.ts +7 -6
  380. package/src/core/disk-usage.test.ts +257 -1
  381. package/src/core/disk-usage.ts +134 -14
  382. package/src/core/engine-locale.test.ts +593 -0
  383. package/src/core/engine-locale.ts +175 -0
  384. package/src/core/exporter.ts +3 -3
  385. package/src/core/fork-reconcile.test.ts +279 -0
  386. package/src/core/fork-reconcile.ts +234 -0
  387. package/src/core/journal.ts +6 -5
  388. package/src/core/local-plugin-host.ts +3 -2
  389. package/src/core/local-plugin-pack.ts +11 -6
  390. package/src/core/messages-core.ts +657 -0
  391. package/src/core/messages.ts +123 -17
  392. package/src/core/migration-consult.ts +21 -20
  393. package/src/core/migration-history.ts +19 -5
  394. package/src/core/model-tools.ts +28 -27
  395. package/src/core/phase3-host.ts +21 -5
  396. package/src/core/phase3-p2b-fingerprint.test.ts +22 -0
  397. package/src/core/plugin-cli.ts +18 -19
  398. package/src/core/reconcile.test.ts +117 -0
  399. package/src/core/reconcile.ts +85 -17
  400. package/src/core/recovery-orchestrator.ts +19 -18
  401. package/src/core/reinstall.ts +26 -21
  402. package/src/core/restore.ts +20 -17
  403. package/src/core/rollback-file-diff.test.ts +159 -0
  404. package/src/core/rollback-ignore-rules.ts +126 -0
  405. package/src/core/rollback-point.test.ts +2603 -0
  406. package/src/core/rollback-point.ts +3755 -0
  407. package/src/core/run-registry.ts +6 -5
  408. package/src/core/session-health.ts +15 -0
  409. package/src/core/session-meta.test.ts +117 -1
  410. package/src/core/session-meta.ts +149 -2
  411. package/src/core/session-title.test.ts +127 -0
  412. package/src/core/session-title.ts +189 -0
  413. package/src/core/startup-barrier.test.ts +69 -0
  414. package/src/core/startup-barrier.ts +27 -0
  415. package/src/core/transaction-coordinator.ts +4 -3
  416. package/src/core/types.ts +18 -1
  417. package/src/core/validator.ts +5 -4
  418. package/src/core/verify-recovery.ts +31 -30
  419. package/src/foreign/bundle.ts +3 -2
  420. package/src/foreign/continue.ts +2 -1
  421. package/src/foreign/crush.ts +2 -1
  422. package/src/foreign/file-budget.test.ts +2 -0
  423. package/src/foreign/gemini.ts +2 -1
  424. package/src/foreign/goose.ts +2 -1
  425. package/src/foreign/kilocode.ts +2 -1
  426. package/src/foreign/kimi.ts +2 -1
  427. package/src/foreign/messages.ts +44 -0
  428. package/src/foreign/mimocode.ts +2 -1
  429. package/src/foreign/openclaw.ts +2 -1
  430. package/src/foreign/opencode.ts +2 -1
  431. package/src/foreign/pi.ts +2 -1
  432. package/src/foreign/qoder.ts +2 -1
  433. package/src/foreign/qwen.ts +2 -1
  434. package/src/foreign/registry.ts +7 -6
  435. package/src/foreign/teleagent.ts +2 -1
  436. package/src/foreign/workbuddy.ts +2 -1
  437. package/src/foreign/zcode.ts +2 -1
  438. package/src/foreign/zed.ts +2 -1
  439. package/src/index.facade.test.ts +340 -1
  440. package/src/index.ts +813 -57
  441. package/src/market/git-file-writer.ts +20 -19
  442. package/src/market/github-repos.test.ts +23 -0
  443. package/src/market/github-repos.ts +37 -19
  444. package/src/market/index-parser.ts +31 -30
  445. package/src/market/messages-market.ts +325 -0
  446. package/src/market/my-repo.ts +32 -29
  447. package/src/market/prepare.ts +18 -15
  448. package/src/market/security.ts +20 -19
  449. package/src/market/types.ts +2 -1
  450. package/src/market/url.ts +2 -1
  451. package/src/migrations/index.ts +6 -5
  452. package/src/migrations/messages-migrations.ts +59 -0
  453. package/src/profiles/dsh-profile-launcher.ts +2 -1
  454. package/src/profiles/dsh-profile-runtime.ts +2 -1
  455. package/src/profiles/messages.ts +31 -0
  456. package/src/routes/backup.ts +18 -2
  457. package/src/routes/import.ts +3 -3
  458. package/src/routes/index.ts +1 -1
  459. package/src/routes/kit.ts +9 -3
  460. package/src/routes/market.ts +1 -1
  461. package/src/routes/messages.ts +104 -0
  462. package/src/routes/profiles.ts +12 -6
  463. package/src/routes/recovery.test.ts +405 -1
  464. package/src/routes/recovery.ts +377 -109
  465. package/src/routes/snapshots.ts +51 -4
  466. package/src/routes/sync.ts +67 -16
  467. package/src/schema/config.ts +53 -20
  468. package/src/schema/index.ts +7 -5
  469. package/src/schema/manifest.ts +19 -18
  470. package/src/schema/messages.ts +120 -0
  471. package/src/schema/registry.test.ts +19 -5
  472. package/src/schema/section-registry.ts +64 -39
  473. package/src/schema/versions.ts +6 -5
  474. package/src/security/encryption.ts +16 -15
  475. package/src/security/integrity.ts +8 -7
  476. package/src/security/messages.ts +87 -0
  477. package/src/security/redaction.test.ts +17 -1
  478. package/src/security/redaction.ts +13 -0
  479. package/src/security/secret-scanner.ts +19 -12
  480. package/src/security/security.test.ts +47 -0
  481. package/src/security/vault.ts +2 -1
  482. package/src/security/zip-security.ts +4 -3
  483. package/src/sync/ancestor.ts +2 -1
  484. package/src/sync/autosync-config.ts +21 -0
  485. package/src/sync/autosync-scheduler.test.ts +63 -7
  486. package/src/sync/autosync-scheduler.ts +118 -78
  487. package/src/sync/backup-files.ts +3 -2
  488. package/src/sync/backup-scheduler.ts +6 -5
  489. package/src/sync/blob-store.ts +2 -1
  490. package/src/sync/git/git-transport.test.ts +170 -8
  491. package/src/sync/git/git-transport.ts +176 -2
  492. package/src/sync/github-auth.ts +10 -9
  493. package/src/sync/layout.ts +13 -12
  494. package/src/sync/messages.ts +153 -0
  495. package/src/sync/review-queue.ts +6 -5
  496. package/src/sync/s3/messages.ts +16 -0
  497. package/src/sync/s3/s3-providers.ts +9 -6
  498. package/src/sync/s3/s3-transport.ts +1 -1
  499. package/src/sync/s3/sigv4.ts +3 -1
  500. package/src/sync/snapshot-crypto.ts +6 -5
  501. package/src/sync/snapshot-json.ts +3 -2
  502. package/src/sync/sync-engine-download-tombstone.test.ts +1 -1
  503. package/src/sync/sync-engine.test.ts +226 -8
  504. package/src/sync/sync-engine.ts +103 -41
  505. package/src/sync/sync-history.ts +7 -6
  506. package/src/sync/sync-selection.test.ts +12 -2
  507. package/src/sync/sync-selection.ts +18 -5
  508. package/src/sync/webdav/webdav-transport.ts +4 -1
  509. package/src/ui/artifact-view.ts +4 -3
  510. package/src/ui/commands.test.ts +1 -0
  511. package/src/ui/commands.ts +11 -1
  512. package/src/ui/disk-usage-view.test.ts +113 -6
  513. package/src/ui/disk-usage-view.ts +63 -7
  514. package/src/ui/errors.ts +4 -3
  515. package/src/ui/export-flow.ts +24 -20
  516. package/src/ui/foreign-view.test.ts +43 -0
  517. package/src/ui/foreign-view.ts +30 -3
  518. package/src/ui/i18n.ts +125 -0
  519. package/src/ui/import-wizard.ts +18 -10
  520. package/src/ui/progress.ts +2 -2
  521. package/src/ui/report.ts +7 -6
  522. package/src/ui/rollback-view.test.ts +819 -0
  523. package/src/ui/rollback-view.ts +1339 -0
  524. package/src/ui/selection-model.test.ts +33 -1
  525. package/src/ui/selection-model.ts +53 -0
  526. package/src/ui/session-inventory-view.test.ts +109 -0
  527. package/src/ui/session-inventory-view.ts +138 -3
  528. package/src/ui/session-issue-labels.test.ts +51 -0
  529. package/src/ui/session-issue-labels.ts +70 -0
  530. package/src/ui/sync-settings-view.test.ts +36 -0
  531. package/src/ui/sync-settings-view.ts +49 -0
  532. package/src/ui/types.ts +10 -4
  533. package/src/utils/atomic-write.ts +6 -5
  534. package/src/utils/bundle-scan.test.ts +2 -2
  535. package/src/utils/bundle-scan.ts +12 -1
  536. package/src/utils/env-lock.ts +83 -49
  537. package/src/utils/json.ts +7 -5
  538. package/src/utils/messages-utils.ts +232 -0
  539. package/src/utils/proxy.test.ts +25 -0
  540. package/src/utils/proxy.ts +19 -5
  541. package/src/utils/recursive-walk.test.ts +43 -0
  542. package/src/utils/recursive-walk.ts +75 -1
  543. package/src/utils/session-log-repair.ts +10 -9
  544. package/src/utils/zip.ts +27 -26
  545. package/lib/core/checkpoint.d.ts +0 -362
  546. package/lib/core/checkpoint.js +0 -1309
  547. package/src/core/checkpoint.test.ts +0 -665
  548. package/src/core/checkpoint.ts +0 -1614
@@ -14,6 +14,9 @@ export const zh = {
14
14
  'common.cancel': '取消',
15
15
  'common.retry': '重试',
16
16
  'common.unknownError': '未知错误',
17
+ // 折叠开关的可访问名(维护与诊断的折叠卡头)
18
+ 'common.expand': '展开',
19
+ 'common.collapse': '收起',
17
20
  // 入口 / tab
18
21
  'view.recovery': '恢复',
19
22
  // 全局 SAFE MODE 横幅(聚合优化:恢复并入「备份与快照」,跨 tab 可见兜底)
@@ -171,25 +174,54 @@ export const zh = {
171
174
  'sessions.scan': '开始体检',
172
175
  'sessions.noRoot': '本机没有会话数据目录(还没产生过会话,或 DSH home 不是这个)。',
173
176
  'sessions.empty': '没有发现任何会话。',
174
- 'sessions.summary.total': '共 {count} 条会话',
175
- 'sessions.count.blocksStartup': '阻断启动 {count}',
176
- 'sessions.count.unloadable': '不可加载 {count}',
177
- 'sessions.count.nextRequestFails': '下次请求会失败 {count}',
178
- 'sessions.count.invisible': '看不见 {count}',
179
- 'sessions.targetFormat': '本机会话格式 v{version}',
177
+ 'sessions.metric.total': '会话总数',
178
+ 'sessions.metric.attention': '需要处理',
179
+ 'sessions.metric.format': '会话格式',
180
+ 'sessions.severityShort.blocksStartup': '阻断启动',
181
+ 'sessions.severityShort.unloadable': '不可加载',
182
+ 'sessions.severityShort.nextRequestFails': '下次请求失败',
183
+ 'sessions.severityShort.invisible': '看不见',
184
+ 'sessions.rescan': '重新体检',
185
+ 'sessions.section.attention': '需要处理的会话',
186
+ 'sessions.section.attentionNone': '列表里没有需要处理的会话。',
187
+ 'sessions.issues.more': '还有 {count} 条',
188
+ 'sessions.hidden.healthyShort': '另有 {count} 条正常会话(已隐藏)',
189
+ 'sessions.repair.availableShort': '可应用内修复 {count} 条',
190
+ 'sessions.repair.noneShort': '本次没有可应用内修复的会话。',
180
191
  'sessions.unverified': '已做深度校验 {verified} 条,未做深度校验 {count} 条(未验证 ≠ 没问题)',
181
192
  'sessions.untested': '另有 {count} 条超出扫描上限,本次未体检。',
182
193
  'sessions.unreadableEntries': '有 {count} 个目录/文件读不出来(已如实计入,未当作正常)。',
183
- 'sessions.col.session': '会话',
184
- 'sessions.col.severity': '严重级',
185
- 'sessions.col.issues': '问题',
186
- 'sessions.col.meta': '版本 / 体积 / 最近活动',
194
+ // 问题码 → 用户可读短标签(known-gaps G-24 剩余未做项 ③):此前直接把机器码
195
+ // (location-mismatch / duplicate-id / format-newer …)拼上屏,中英用户都读不懂;
196
+ // 映射表在 src/ui/session-issue-labels.ts,未知码回落 sessions.issue.unknown,绝不裸渲染。
197
+ 'sessions.issue.duplicateId': '同一会话 id 出现在多个工作区',
198
+ 'sessions.issue.locationMismatch': '日志位置与所属工作区不一致',
199
+ 'sessions.issue.headerUnreadable': '会话头读不出来',
200
+ 'sessions.issue.tornTail': '末尾帧被截断(DSH 会自愈)',
201
+ 'sessions.issue.corruptFrame': '容器里有非法帧',
202
+ 'sessions.issue.unparsableEvent': '有事件无法解析',
203
+ 'sessions.issue.replayDuplicateRows': '重复写入的行(可零损失清理)',
204
+ 'sessions.issue.syntheticCloser': '合成收尾块与真实续写冲突',
205
+ 'sessions.issue.seqGap': '事件序号有空洞',
206
+ 'sessions.issue.missingMessageId': '消息缺少 id',
207
+ 'sessions.issue.emptyToolCallId': '工具调用 id 为空',
208
+ 'sessions.issue.danglingToolCall': '工具调用没有配对结果',
209
+ 'sessions.issue.toolResultIdMismatch': '工具结果与调用 id 不一致',
210
+ 'sessions.issue.duplicateToolCallId': '同一步里工具调用 id 重复',
211
+ 'sessions.issue.invalidSettlement': '助手段落的结算字段非法',
212
+ 'sessions.issue.closedTurnContinued': '已关闭的轮次被继续',
213
+ 'sessions.issue.formatNewer': '会话格式高于本机 DSH(会被静默跳过)',
214
+ 'sessions.issue.unregisteredWorkspace': '没有工作区记录指向它',
215
+ 'sessions.issue.subagentWithoutParent': '子代理会话缺少父对话',
216
+ 'sessions.issue.unknown': '未识别的问题',
187
217
  'sessions.severity.blocksStartup': '会让 DSH 起不来',
188
218
  'sessions.severity.invisible': '看不见',
189
219
  'sessions.severity.nextRequestFails': '下次请求会失败',
190
220
  'sessions.severity.ok': '正常',
191
221
  'sessions.severity.unloadable': '会话不可加载',
192
222
  'sessions.subagent': '子代理',
223
+ // 空白会话(从未提问):DSH 自己的标题就是这个占位名(session.new),不用 uuid 冒充标题
224
+ 'sessions.blank': '新会话',
193
225
  'sessions.truncated': '列表已截断:还有 {count} 条未显示(重新体检或按需缩小范围)。',
194
226
  'sessions.repair.title': '怎么修',
195
227
  'sessions.repair.desc': '容器非法 / header 不可读 / 格式超前这类应用内不动的问题,先在终端预览再落盘;需要写入时建议先关闭 DSH。',
@@ -206,7 +238,6 @@ export const zh = {
206
238
  'sessions.repair.none': '本次没有可应用内修复的会话。应用内可修:重放重复行 / 可证明的合成收尾块(零损失)、seq 空洞与不可解析行(截断,有损,需确认);其余问题见下方离线命令。',
207
239
  'common.close': '关闭',
208
240
  'sessions.dialog.open': '打开会话体检',
209
- 'sessions.dialog.hint': '打开后扫描本机存量会话:哪些读不出来、哪些能应用内修复,都在弹窗里处理(扫描只读)。',
210
241
  'sessions.dialog.lastScan': '上次扫描:共 {total} 条,需处理 {issues} 条,本机修复记录 {repairs} 条。',
211
242
  'sessions.repair.action': '修复',
212
243
  'sessions.repair.available': '有 {count} 条会话可应用内修复;点行内「修复」先看计划,或用右上角「一键修复全部」按顺序处理。',
@@ -239,7 +270,6 @@ export const zh = {
239
270
  // F-2:官方 session.export 通道(原始日志 ZIP,由 DSH 自己打包,含子会话与附件)
240
271
  'sessions.zip.action': '下载原始日志 (ZIP)',
241
272
  'sessions.zip.hint': '由 DSH 自己打包这条会话的原始日志(含子会话与附件);不经过本插件,孤儿/多代日志一并带上。',
242
- 'sessions.zip.checking': '正在检查导出能力…',
243
273
  'sessions.zip.reason.serviceMissing': '当前 DSH 的会话导出通道不可用:持久化后端缺少必需的服务(session-query / session-persistence / attachments)。这条入口已停用(状态 {status})。',
244
274
  'sessions.zip.reason.authRequired': '无法确认导出通道的可用性:本机自请求被认证挡在门外(状态 {status})。这不代表本机不支持导出 —— 请重启 DSH 后再试。',
245
275
  'sessions.zip.reason.readFailed': '导出通道在线,但这条会话的日志读取失败(状态 {status})。请先用上方体检/修复处理这条会话。',
@@ -276,68 +306,223 @@ export const zh = {
276
306
  'sessions.repair.verify.unverified': '未验证:本机跑不了真 codec 复验,所以 DSH 现在能否读它并没有被证明 —— 未验证不等于已验证(写入是否生效另说)。',
277
307
  'sessions.repair.verify.rollbackFailed': '危险态:复验失败后自动回滚没有成功,目标当前仍是修复后的字节,需要人工处理。',
278
308
  'sessions.repair.rejectedNext': '原文件没有被改动。可以再点一次「开始体检」看最新状态;如果原因与「文件被改过/正在被写入」有关,等这条会话安静下来再试。',
279
- // C-1 Q4 检查点(t7):界面按 routes/recovery.ts 回传的 body.code / warn / guidance code 出文案,
280
- // 不靠 HTTP 状态码猜原因(code 组不自行拼接用户可见文本,全部经本字典)。
281
- 'checkpoint.code.storage-unavailable': '检查点存储不可用',
282
- 'checkpoint.code.ledger-write-failed': '检查点台账写不进去',
283
- 'checkpoint.code.confirmation-required': '需要显式确认才能回滚',
284
- 'checkpoint.code.record-not-found': '找不到该检查点',
285
- 'checkpoint.code.protected-checkpoint': '该检查点受保护:不可回滚、不可删除',
286
- 'checkpoint.code.record-incomplete': '该检查点三态不全,缺省不允许回滚',
287
- 'checkpoint.code.guard-failed': '回滚前的保护点没能拍下来,已中止(零写入)',
288
- 'checkpoint.code.invalid-input': '请求参数不合法',
289
- 'checkpoint.code.no-chunks': '没有给出工作区路径分块',
290
- 'checkpoint.code.chunk-not-absolute': '工作区分块必须是绝对路径',
291
- 'checkpoint.code.chunk-not-found': '工作区分块不存在或不可用',
292
- 'checkpoint.code.no-session-input': '没有指定会话(unitId 或 sessionLogPath)',
293
- 'checkpoint.code.session-missing': '会话日志不存在',
294
- 'checkpoint.code.session-out-of-root': '会话日志不在本机会话根内,已拒绝',
295
- 'checkpoint.code.session-too-large': '会话日志超过副本上限:只记下了游标,该态不能自动回滚',
296
- 'checkpoint.code.session-active': '会话日志刚被写过,为避免覆盖正在进行的对话而拒绝',
297
- 'checkpoint.code.session-diverged': '会话日志比捕获游标还短(被别的路径改写或截断过),拒绝覆盖',
298
- 'checkpoint.code.capture-degraded': '该态捕获不完整(有链接或读不到的内容被跳过,已逐条留痕)',
299
- 'checkpoint.code.capture-truncated': '该态捕获到达上限被截断(已如实标注)',
300
- 'checkpoint.code.capture-failed': '该态捕获失败',
301
- 'checkpoint.code.nothing-to-restore': '该检查点没有可恢复的内容',
302
- 'checkpoint.code.invalid-path': '记录里的路径越界(不在捕获分块内),已拒绝该条',
303
- 'checkpoint.code.object-missing': '内容副本缺失,已拒绝该条',
304
- 'checkpoint.code.object-hash-mismatch': '内容副本与记录指纹不一致,已拒绝该条',
305
- 'checkpoint.code.restore-failed': '恢复失败(详见逐条细节)',
306
- // 捕获 / 回滚过程中的告警(不阻断,但必须可见)
307
- 'checkpoint.warn.ledgerUnreadable': '检查点台账读不出来(已按空台账继续,未静默)',
308
- 'checkpoint.warn.trackMissing': '有一态没捕获到:该态本次不能回滚',
309
- 'checkpoint.warn.trackDegraded': '有一态捕获不完整(回滚可能不完整)',
310
- 'checkpoint.warn.protected': '该检查点受保护:不可回滚、不可删除',
311
- 'checkpoint.warn.guardFailed': '回滚前的保护点没拍成(已记告警后继续)',
312
- // 降级指引:存储栈缺失 / 数据目录不可写 / 只读兜底 / 探测细节 / 会话离线处置 / 已就地写回 / 工作区部分恢复
313
- 'checkpoint.guidance.mountStorage': '检查点存储栈缺失:把 @deepseek-ai/dsh-storage、dsh-storage-json、dsh-storage-domain 组合进当前 profile 后重试',
314
- 'checkpoint.guidance.fixDataDir': '插件数据目录不可写:{detail}',
315
- 'checkpoint.guidance.readOnlyFallback': '存储不可用期间:捕获与回滚都会干净失败,但现状列表仍可查看(不会崩,也不会假装成功)',
316
- 'checkpoint.guidance.probeDetail': '探测细节:{detail}',
317
- 'checkpoint.guidance.sessionOffline': '该会话日志超上限或副本缺失:请用离线命令处理(路径见 detail)',
318
- 'checkpoint.guidance.sessionRewritten': '已就地写回会话日志(路径见 detail);日志格式与目录位置不变',
319
- 'checkpoint.guidance.workspacePartial': '工作区可能已部分恢复:后续段(配置/会话)本次未执行',
320
- // 面板标签最小集(下一轮做 UI 时直接用;键已就位,漏键即编译失败)
321
- 'checkpoint.title': '检查点(会话 / 工作区 / 配置 三态同点)',
322
- 'checkpoint.desc': '一次捕获记下三态的 capturedAt 与指纹,「同点」按跨度可核验;回滚只覆盖捕获过的内容,检查点之后新建的文件一律不删。',
323
- 'checkpoint.capture': '拍一个检查点',
324
- 'checkpoint.capture.chunks': '工作区路径(每行一个,绝对路径)',
325
- 'checkpoint.capture.session': '关联会话',
326
- 'checkpoint.capture.protect': '保护这个检查点(不可回滚、不可删除)',
327
- 'checkpoint.list.empty': '还没有检查点',
328
- 'checkpoint.col.time': '时间',
329
- 'checkpoint.col.states': '三态',
330
- 'checkpoint.col.spread': '同点跨度',
331
- 'checkpoint.samePoint.yes': '同点(跨度 {ms} 毫秒)',
332
- 'checkpoint.samePoint.no': '非同点:{reason}',
333
- 'checkpoint.state.workspace': '工作区',
334
- 'checkpoint.state.session': '会话游标',
335
- 'checkpoint.state.config': '插件配置',
336
- 'checkpoint.preview': '预览回滚',
337
- 'checkpoint.rewind': '回滚到此处',
338
- 'checkpoint.rewind.confirm': '我确认:将用该检查点覆盖工作区 / 配置 / 会话(新建的文件不会被删除)',
339
- 'checkpoint.delete': '删除检查点',
340
- 'checkpoint.legacy.guard': '回滚前的保护点',
309
+ 'rollback.title': '工作区 + 会话回滚',
310
+ 'rollback.desc': '每个回合结束自动留一个回滚点:工作区快照 + 会话游标,不需要手动拍点。回退只覆盖该点记录在案的工作区路径,并删除「能证明是留点之后新建」且通过安全判据的路径(.git / node_modules / 插件数据区 / 超出单文件上限一律拒删;留点时被预算挡下的既有文件也在证明名单里,不会被当成新建;证明名单本身不完整时一条都不删);会话回退在目标回合之前的稳定边界 fork 一个新子会话,原会话日志只读、绝不被改写。',
311
+ 'rollback.list.empty': '还没有回滚点',
312
+ /* 2026-10-11 布局重构:工具栏只放数字摘要 + 刷新按钮;留存/淘汰口径仍在 rollback.retention.hint(改由 ⓘ 承载)。 */
313
+ 'rollback.summary': '共 {points} 个回滚点 · {sessions} 个会话',
314
+ 'rollback.refresh': '刷新',
315
+ // 台账弹窗(2026-10-12:卡不再折叠,台账整块搬进按会话分组的弹窗)
316
+ 'rollback.dialog.title': '回滚点台账',
317
+ 'rollback.dialog.open': '查看回滚点',
318
+ // 台账弹窗左侧栏(拥有回滚点的对话列表;默认按各组最新时间从新到旧)
319
+ 'rollback.sessions.title': '有回滚点的对话',
320
+ 'rollback.col.time': '时间',
321
+ 'rollback.col.session': '会话',
322
+ 'rollback.col.turn': '回合',
323
+ 'rollback.col.states': '两态',
324
+ 'rollback.turn': '第 {n} 回合',
325
+ 'rollback.turnUnknown': '回合未知',
326
+ 'rollback.badge.incomplete': '两态不全',
327
+ 'rollback.badge.protected': '受保护(只进不出)',
328
+ 'rollback.apiMissing': '当前宿主没有回滚点能力(插件版本过旧):界面不假装可用',
329
+ 'rollback.retention.hint': '自动点:每会话最多 {perSession} 个、全局最多 {total} 个,超出只淘汰最旧的自动点。回退前的救援点不占这两个额度、也不被它们淘汰,但用户删不掉,所以另有自己的两道上限:每会话最多 {guardsPerSession} 个、全局最多 {guards} 个(无论按哪道算,超出都淘汰最旧的救援点)。',
330
+ 'rollback.autopoint.hint': '回滚点由每个回合结束时自动留:工作区快照 + 会话游标(没有「手动拍点」入口)。',
331
+ 'rollback.state.workspace': '工作区',
332
+ 'rollback.state.session': '会话',
333
+ 'rollback.state.unknown': '未知状态',
334
+ 'rollback.mode.label': '回退范围',
335
+ 'rollback.mode.filesAndSession': '工作区文件 + 会话',
336
+ 'rollback.mode.filesOnly': '只回退工作区文件',
337
+ 'rollback.mode.sessionOnly': '只回退会话',
338
+ 'rollback.mode.unknown': '未知回退范围',
339
+ 'rollback.kind.auto': '自动留点',
340
+ 'rollback.kind.guard': '回退前救援点(可回退、不可删)',
341
+ /* 行内徽章短标签(完整语义见上一条,进 title) */
342
+ 'rollback.kind.guardShort': '救援点',
343
+ 'rollback.kind.unknown': '未知来源',
344
+ 'rollback.code.ok': '成功',
345
+ 'rollback.code.deleted': '已删除',
346
+ 'rollback.code.confirmation-required': '需要显式确认才能回退',
347
+ 'rollback.code.protected-point': '该回滚点被拒绝:受保护点不可回退、不可删除;救援点可作为回退目标,但用户不可删除',
348
+ 'rollback.code.record-not-found': '找不到该回滚点',
349
+ 'rollback.code.record-incomplete': '该回滚点两态不全,缺省不允许回退',
350
+ 'rollback.code.invalid-input': '请求参数不合法',
351
+ 'rollback.code.created-removals-limit': '待删除的「留点之后新建」路径超过上限:为免只删一半,本次回退整体拒绝、一条都没动(工作区与会话都未被改动)',
352
+ 'rollback.code.no-boundary': '该回合之前没有可用的稳定边界(会话回退不可用)',
353
+ 'rollback.code.storage-unavailable': '回滚点存储不可用',
354
+ 'rollback.code.mutation-locked': '有另一个写操作正在进行(或已进安全模式):稍后重试',
355
+ 'rollback.code.method-not-allowed': '该端点不支持这个请求方法',
356
+ 'rollback.code.ledger-unreadable': '回滚点台账读不出来(已按空台账继续,未静默)',
357
+ 'rollback.code.ledger-write-failed': '回滚点台账写不进去',
358
+ 'rollback.code.invalid-path': '记录里的路径越界(不在记录在案的分块内),已拒绝该条',
359
+ 'rollback.code.object-missing': '内容副本缺失,已拒绝该条',
360
+ 'rollback.code.object-hash-mismatch': '内容副本与记录指纹不一致,已拒绝该条',
361
+ 'rollback.code.restore-failed': '回退失败(详见逐条细节)',
362
+ 'rollback.code.verify-failed': '回退后逐文件校验未通过,已整体回滚到回退前状态',
363
+ 'rollback.code.nothing-to-restore': '该回滚点没有可恢复的内容',
364
+ /* T10/T11:工作区段 **files=0**(该点压根没记录任何文件)的独立码 —— 与
365
+ `nothing-to-restore`(记录了条目但全部已与快照一致)是**两件不同的事**,界面上必须说两句不同的话:
366
+ 前者「没有记录任何文件」,后者「工作区与该回退点一致」。只出现在 segments[].reasonCode。 */
367
+ 'rollback.code.no-recorded-files': '该回退点没有记录任何文件',
368
+ 'rollback.code.file-not-recorded': '这个路径不在该回滚点的记录里',
369
+ 'rollback.code.guard-failed': '回退前的救援点没能留下,已中止(零写入)',
370
+ 'rollback.code.session-missing': '会话日志不存在',
371
+ 'rollback.code.session-out-of-root': '会话日志不在本机会话根内,已拒绝',
372
+ 'rollback.code.session-too-large': '会话日志超过上限:只记下了游标(回退不依赖副本)',
373
+ // t7 定点修正(captain 授权;既有键措辞修正,不是新增键):该码现在有两种用法 ——
374
+ // ① preview.session.reasonCode(仅供参考);② 结果码:运行中的对话带文件段的回退被硬拒(工作区零改动)。
375
+ 'rollback.code.session-active': '这条对话正在运行(会话日志刚被写过):此时带文件段的回退会被拒绝、工作区一个字节都不动;只回溯消息不受影响(回退只 fork 子会话,不改写原日志)',
376
+ // t8(只追加,不动上面那条既有键):对话**正在运行**时 —— 默认只回溯消息,带工作区段的回退在路由层被拒
377
+ 'rollback.preview.sessionActive': '这条对话正在运行:本轮结束前只回溯消息(不改动工作区),避免与正在写盘的工具抢文件。等本轮结束后重新打开本弹窗,即可连文件一起回退。',
378
+ 'rollback.sessionActive.denied': '这条对话正在运行:本次一个文件都没有改动。请等本轮结束,或改成「只回溯消息」再试。',
379
+ 'rollback.code.session-fork-unavailable': '当前宿主没有提供会话 fork 能力,因此不能回退会话',
380
+ 'rollback.code.fork-failed': 'fork 子会话失败,会话段未回退',
381
+ 'rollback.code.capture-failed': '该态留点失败',
382
+ 'rollback.code.capture-truncated': '该态留点到达上限被截断(已如实标注)',
383
+ 'rollback.code.capture-degraded': '该态留点不完整(有链接或读不到的内容被跳过,已逐条留痕)',
384
+ 'rollback.code.no-chunks': '没有给出工作区路径分块',
385
+ 'rollback.code.chunk-not-absolute': '工作区分块必须是绝对路径',
386
+ 'rollback.code.chunk-not-found': '工作区分块不存在或不可用',
387
+ 'rollback.code.unknown': '未识别的错误码(技术细节见下方)',
388
+ 'rollback.guidance.mountStorage': '回滚点存储栈缺失:把 @deepseek-ai/dsh-storage、dsh-storage-json、dsh-storage-domain 组合进当前 profile 后重试',
389
+ 'rollback.guidance.fixDataDir': '插件数据目录不可写:{detail}',
390
+ 'rollback.guidance.readOnlyFallback': '存储不可用期间:留点与回退都会干净失败,但现状列表仍可查看(不会崩,也不会假装成功)',
391
+ 'rollback.guidance.probeDetail': '探测细节:{detail}',
392
+ 'rollback.guidance.workspaceRolledBack': '工作区写回失败后已整体回滚到回退前状态(会话段本次未执行)',
393
+ 'rollback.guidance.forkUnavailable': '当前宿主没有提供会话 fork 能力:会话段未回退(原会话日志未被改写)',
394
+ 'rollback.guidance.sessionForked': '已 fork 新子会话:{detail}(原会话日志只读、未被改写)',
395
+ 'rollback.guidance.sessionNotAttached': '新子会话没能挂回原工作区(会出现在侧栏「未分组」下;回退本身已生效)',
396
+ 'rollback.guidance.unknown': '未识别的指引(技术细节见下方)',
397
+ 'rollback.warn.ledgerUnreadable': '回滚点台账读不出来(已按空台账继续,未静默)',
398
+ 'rollback.warn.trackMissing': '有一态没留到:该态本次不能回退',
399
+ 'rollback.warn.trackDegraded': '有一态留得不完整(回退可能不完整)',
400
+ 'rollback.warn.protected': '该回滚点受保护(只进不出):不可回退、不可删除',
401
+ 'rollback.warn.guardFailed': '回退前的救援点没留成(已记告警后继续)',
402
+ 'rollback.warn.createdRefused': '本次不删除任何「留点后新建」的路径:捕获时的证明名单不完整(有整目录被忽略 / 路径索引缺失 / 目录读不出),无法证明哪些是新建的',
403
+ 'rollback.warn.unknown': '未识别的告警(技术细节见下方)',
404
+ 'rollback.preview': '预览回退',
405
+ /* 2026-10-10 用户第三条:预览对话框改简洁版,标题就叫「回退预览」(不再带副标题)。
406
+ 这个键只被两处预览界面使用(聊天入口的预览对话框 + 设置页回滚卡的预览弹窗),改的是同一句话。 */
407
+ 'rollback.preview.title': '回退预览',
408
+ 'rollback.preview.hint': '以下为只读预览(零写入)。确认后才会执行回退。',
409
+ 'rollback.preview.loading': '正在生成预览…',
410
+ 'rollback.preview.empty': '该回滚点没有可回退的内容。',
411
+ 'rollback.preview.none': '该点没有可回退的内容',
412
+ 'rollback.preview.reason.guard': '宿主这次没有允许回退该救援点(救援点通常可作为回退目标;详见下方细节)',
413
+ 'rollback.preview.reason.incomplete': '该点两态不全:需要显式允许部分回退',
414
+ 'rollback.preview.reason.hostRefused': '宿主拒绝了这次回退(原因见下方细节)',
415
+ 'rollback.preview.segments': '只勾可回退的段(只有工作区与会话两态)',
416
+ 'rollback.preview.overwrite': '覆盖 {n} 项',
417
+ 'rollback.preview.different': '其中 {n} 项内容不同',
418
+ 'rollback.preview.missing': '{n} 项当前已不存在(会被重新创建)',
419
+ 'rollback.preview.createdAfter': '{n} 项在留点时并不存在(留点之后新建;回退只会删除其中通过安全判据的)',
420
+ 'rollback.preview.removalsTitle': '回退会删除的文件',
421
+ 'rollback.preview.notice.skipped': '{n} 项在留点时被跳过(未记录;已计入证明名单 ⇒ 不参与删除判据)',
422
+ 'rollback.preview.notice.truncated': '该点捕获被上限截断(超单文件上限 / 字节预算 / 文件数预算);删除判据另看证明名单(路径索引)是否完整',
423
+ /* —— 简洁版预览:只列「会被回退的文件 + 新增/删除行数」(E1 的 preview.workspace.files)——
424
+ * 三条口径逐字对应引擎:① 只列**会被改动**的文件(内容已一致的一项都不列,空数组 ⇒ empty 那行);
425
+ * ② added/removed 可能是 null(单文件超限 / 非文本 / 对象仓读不出)⇒ 说「行数未统计」,
426
+ * **绝不写 0**(0 是一个会被证伪的具体数字,而「不知道」不是);③ 本清单只覆盖**记录在案**的
427
+ * 文件 —— 整文件删除走**另一条**清单(`rollback.preview.removalsTitle` / `createdSkipped`),
428
+ * 删除清单由引擎与执行阶段**共用同一个函数**算出(预览说会删的 = 实际删的)。 */
429
+ 'rollback.preview.files.title': '将被回退的文件:',
430
+ 'rollback.preview.files.empty': '工作区与该回退点一致,无需恢复',
431
+ /* —— T15:**预览态**(点「回退」之前那一屏)的四态文案 ——
432
+ * 判据只来自 preview 字段(available / overwrite / different / missing / reasonCode /
433
+ * session.restorable + 宿主 canRestore),**禁止解析 detail**;判定函数 = src/ui/rollback-view.ts 的
434
+ * rollbackPreviewNote()(一处实现,聊天对话框调用它)。
435
+ * ③a 与 ①② 的「会话仍会回退」变体都**不含**「无需恢复」——点下去真会 fork 子会话,
436
+ * 说「无需恢复」就是假尾句(假尾句禁令)。 */
437
+ 'rollback.preview.files.unavailable': '该回退点的工作区态没有可用的记录(原因:{reason}),无需恢复',
438
+ 'rollback.preview.files.unavailableSession': '该回退点的工作区态没有可用的记录(原因:{reason});会话仍会回退到该点之前',
439
+ 'rollback.preview.files.noFilesSession': '该回退点没有记录任何文件;会话仍会回退到该点之前',
440
+ 'rollback.preview.files.consistentSession': '工作区与该回退点一致(会话仍会回退到该点之前)',
441
+ 'rollback.preview.file.added': '+{n}',
442
+ 'rollback.preview.file.removed': '−{n}',
443
+ /** 整文件删除(用户 2026-10-12 第 6 条):写「删除」而不是 `+0 −N` —— 那是两件事。 */
444
+ 'rollback.preview.file.deleted': '删除',
445
+ 'rollback.preview.file.unknown': '行数未统计',
446
+ 'rollback.preview.file.unknownHint': '这些文件的行数没有统计出来(文件过大、不是文本或内容副本读不出来)—— 回退仍会照常覆盖它们。',
447
+ 'rollback.diff.viewHint': '查看差异:{path}',
448
+ 'rollback.diff.loading': '正在读取差异…',
449
+ 'rollback.diff.before': '当前',
450
+ 'rollback.diff.after': '回退后',
451
+ 'rollback.diff.identical': '该文件与回退点内容一致,回退不会改动它',
452
+ 'rollback.diff.degraded': '差异规模较大,已按整块替换展示(仍是完整结果,只是不是最小的编辑脚本)',
453
+ 'rollback.diff.truncated': '只画了前 {shown} 行(共 {total} 行),数据本身是完整的',
454
+ 'rollback.diff.reason.missingBlob': '这份内容副本读不出来,无法逐行对照(回退仍会尝试写回它)',
455
+ 'rollback.diff.reason.binary': '不是文本文件,没有逐行差异可看',
456
+ 'rollback.diff.reason.tooLarge': '文件超过展示上限,没有逐行差异可看(回退仍会照常覆盖它)',
457
+ 'rollback.diff.reason.unreadable': '当前文件读不出来(权限不足,或它不是普通文件),无法逐行对照',
458
+ 'rollback.diff.reason.unknown': '无法逐行对照,原因未知(宿主没有给出原因)',
459
+ 'rollback.diff.failed': '差异读取失败',
460
+ /* 结果一行摘要:nothing-to-restore 是**成功**(HTTP 200、恢复 0 项),不是失败 —— 说「无需恢复」。
461
+ T11:同一句「无需恢复」下**三种情形必须说三句不同的话**,纯按 segments[].status / reasonCode 分派
462
+ (禁解析 detail、禁自造码):
463
+ · noop = ③ 记录了条目且全部已与快照一致 ⇒「工作区与该回退点一致」;
464
+ · noRecord = ① 存在 status==='skipped' 的段(该态没有可用捕获)⇒「没有可用的记录(原因:…)」;
465
+ · noFiles = ② 存在 reasonCode==='no-recorded-files' 的段(该点没记录任何文件)。
466
+ {reason} 是**该段 reasonCode 的字典键文本**(未知码回落 unknown,绝不裸渲染机器码)。 */
467
+ 'rollback.preview.result.noop': '工作区与该回退点一致,无需恢复',
468
+ 'rollback.preview.result.noRecord': '该回退点没有可用的记录(原因:{reason}),无需恢复',
469
+ 'rollback.preview.result.noFiles': '该回退点没有记录任何文件,无需恢复',
470
+ 'rollback.preview.result.code': 'code:{code}',
471
+ 'rollback.preview.warningsTitle': '该点的告警',
472
+ 'rollback.preview.cursorBytes': '会话日志 {now} → {target} 字节',
473
+ 'rollback.group.label': '会话:{session}',
474
+ 'rollback.group.count': '{n} 个回滚点',
475
+ 'rollback.group.unknownSession': '未知会话',
476
+ 'rollback.row.files': '文件 {n} 个',
477
+ 'rollback.row.sessionCursor': '有会话游标',
478
+ 'rollback.row.noSessionCursor': '无会话游标(只能回退工作区)',
479
+ /* 2026-10-11 布局重构:两行式行卡上只放**短标签**,完整句仍在上面的键里(进 title)——
480
+ 事实一个字都不少,但不再逐行重复整句。 */
481
+ 'rollback.row.sessionShort': '会话可回退',
482
+ 'rollback.row.workspaceShort': '仅工作区',
483
+ 'rollback.row.notice.skipped': '{n} 项在留点时被跳过(未记录;已计入证明名单 ⇒ 不会被当成新建删掉)',
484
+ 'rollback.row.notice.truncated': '该点留点达到上限被截断(计数仍如实;删除判据另看证明名单是否完整)',
485
+ /* 告警徽章的短标签(完整句进 title;truncated 无计数) */
486
+ 'rollback.row.notice.skippedShort': '跳过 {n} 项',
487
+ 'rollback.row.notice.truncatedShort': '已截断',
488
+ 'rollback.preview.cursor': '会话边界 {from} → {to}',
489
+ 'rollback.preview.turns': '回退到第 {n} 回合之前',
490
+ 'rollback.preview.active': '该会话日志刚被写过(仅供参考:回退只 fork 子会话,不改写原日志)',
491
+ 'rollback.rewind': '回退到此处',
492
+ 'rollback.rewind.confirm': '我确认:按所选范围回退。工作区会写回该点记录在案的路径,并删除能证明是「留点之后新建」且通过安全判据的路径(.git / node_modules / 插件数据区 / 超出单文件上限一律拒删;证明名单不完整时一条都不删);会话在目标回合之前的稳定边界 fork 新子会话,原会话日志不改写。',
493
+ 'rollback.rewind.allowPartial': '允许部分回退(该回滚点两态不全)',
494
+ 'rollback.guard.require': '回退前必须留救援点(留不下就整体中止,零写入)',
495
+ 'rollback.guard.warn': '回退前留救援点(留不下记告警后继续)',
496
+ 'rollback.guard.off': '跳过回退前救援点',
497
+ 'rollback.guard.unknown': '未知的救援点策略',
498
+ 'rollback.rewind.result.title': '回退结果',
499
+ 'rollback.rewind.result.counts': '恢复 {restored} 项 / 删除 {removed} 项 / 失败 {failed} 项 / 留点时并不存在(之后新建)的共 {leftovers} 项',
500
+ 'rollback.rewind.result.child': '已回到该消息之前(当前对话:{id})',
501
+ // A(无感替换):原会话被收进「已归档」——列表里只剩这一条对话
502
+ 'rollback.rewind.archive.done': '原会话已收进「已归档」(需要时可在那儿恢复)',
503
+ // 第一条用户消息之前没有上一个回合 ⇒ 没有可 fork 的边界:开一条全新会话(对齐参照实现)
504
+ 'rollback.rewind.freshSession': '这是这条对话的第一条消息之前:已为你新建一条空会话(这条消息的文字可从上面那条气泡复制)',
505
+ 'rollback.rewind.freshSessionFailed': '这是第一条消息之前、没有可回退的对话边界,而本客户端没能新建空会话 —— 请手动点「新会话」',
506
+ 'rollback.rewind.archive.unavailable': '本客户端没有可用的归档入口,所以原会话仍留在会话列表里(内容未被改写)',
507
+ 'rollback.rewind.archive.active': '原会话还有进行中的工作,暂时不能归档 —— 它仍留在会话列表里(内容未被改写)',
508
+ 'rollback.rewind.archive.failed': '原会话归档失败,它仍留在会话列表里(内容未被改写)',
509
+ 'rollback.rewind.result.guard': '回退前救援点:{id}',
510
+ 'rollback.rewind.result.skipped': '未执行的段:{list}',
511
+ 'rollback.segment.restored': '已恢复',
512
+ 'rollback.segment.skipped': '已跳过',
513
+ 'rollback.segment.failed': '失败',
514
+ 'rollback.segment.unknown': '未知状态',
515
+ 'rollback.outcome.restored': '全部恢复',
516
+ 'rollback.outcome.partial': '部分恢复',
517
+ 'rollback.outcome.failed': '回退失败',
518
+ 'rollback.outcome.denied': '已拒绝(零写入)',
519
+ 'rollback.outcome.unknown': '未知结论',
520
+ 'rollback.delete': '删除回滚点',
521
+ 'rollback.delete.done': '已删除回滚点',
522
+ 'rollback.delete.confirmTitle': '删除这个回滚点?',
523
+ 'rollback.delete.confirmMessage': '只删除台账记录与内容副本,不会改动工作区与会话。受保护点与回退前救援点由引擎一律拒绝删除;救援点超出每会话上限 20 个或全局上限 50 个时会自动淘汰最旧的。',
524
+ 'rollback.delete.protected': '该回滚点不可删除:受保护点与救援点一律被引擎拒绝(救援点超出每会话上限 20 个或全局上限 50 个时自动淘汰最旧的)',
525
+ 'rollback.storage.unavailable': '回滚点存储不可用(现状列表仍可查看)',
341
526
  // t54:中断的档案复制残留(cross-F3)—— 独立形态 + 只给删除
342
527
  'recovery.incomplete.title': '未完成的档案副本',
343
528
  'recovery.incomplete.hint': '这些目录带「复制进行中」标记但没有 package.json,是复制被中断留下的半截副本;不能启动,只能删除(只列出有标记的目录 —— 用户手工建的目录不会出现在这里)。',
@@ -417,6 +602,8 @@ export const en: Record<keyof typeof zh, string> = {
417
602
  'common.cancel': 'Cancel',
418
603
  'common.retry': 'Retry',
419
604
  'common.unknownError': 'Unknown error',
605
+ 'common.expand': 'Expand',
606
+ 'common.collapse': 'Collapse',
420
607
  'view.recovery': 'Recovery',
421
608
  'recovery.banner': 'Configuration changes are protected: there is an unfinished recovery item. Resolve it before continuing.',
422
609
  'recovery.bannerAction': 'Go to recovery',
@@ -556,25 +743,53 @@ export const en: Record<keyof typeof zh, string> = {
556
743
  'sessions.scan': 'Run check',
557
744
  'sessions.noRoot': 'No sessions directory on this machine (no conversation yet, or DSH home is not this one).',
558
745
  'sessions.empty': 'No sessions found.',
559
- 'sessions.summary.total': '{count} session(s)',
560
- 'sessions.count.blocksStartup': '{count} block startup',
561
- 'sessions.count.unloadable': '{count} unloadable',
562
- 'sessions.count.nextRequestFails': '{count} will fail the next request',
563
- 'sessions.count.invisible': '{count} invisible',
564
- 'sessions.targetFormat': 'Local session format v{version}',
746
+ 'sessions.metric.total': 'Sessions',
747
+ 'sessions.metric.attention': 'Need attention',
748
+ 'sessions.metric.format': 'Session format',
749
+ 'sessions.severityShort.blocksStartup': 'Blocks startup',
750
+ 'sessions.severityShort.unloadable': 'Unloadable',
751
+ 'sessions.severityShort.nextRequestFails': 'Next request fails',
752
+ 'sessions.severityShort.invisible': 'Invisible',
753
+ 'sessions.rescan': 'Run check again',
754
+ 'sessions.section.attention': 'Sessions needing attention',
755
+ 'sessions.section.attentionNone': 'No session in the list needs attention.',
756
+ 'sessions.issues.more': '+{count} more',
757
+ 'sessions.hidden.healthyShort': '{count} healthy session(s) hidden',
758
+ 'sessions.repair.availableShort': '{count} repairable in-app',
759
+ 'sessions.repair.noneShort': 'No session can be repaired in-app this time.',
565
760
  'sessions.unverified': '{verified} deeply checked, {count} not deeply checked (unverified is not the same as healthy)',
566
761
  'sessions.untested': 'Another {count} session(s) exceeded the scan limit and were not checked.',
567
762
  'sessions.unreadableEntries': '{count} directory/file entries could not be read (counted as-is, never treated as fine).',
568
- 'sessions.col.session': 'Session',
569
- 'sessions.col.severity': 'Severity',
570
- 'sessions.col.issues': 'Issues',
571
- 'sessions.col.meta': 'Version / size / last activity',
763
+ // Issue code -> human label (known-gaps G-24 item 3): the panel used to print the raw machine
764
+ // code (location-mismatch / duplicate-id / format-newer ...); unknown codes fall back to
765
+ // sessions.issue.unknown, never the bare code.
766
+ 'sessions.issue.duplicateId': 'Same session id in several workspaces',
767
+ 'sessions.issue.locationMismatch': 'Log location does not match its workspace',
768
+ 'sessions.issue.headerUnreadable': 'Session header unreadable',
769
+ 'sessions.issue.tornTail': 'Truncated tail frame (DSH self-heals)',
770
+ 'sessions.issue.corruptFrame': 'Invalid frame in the container',
771
+ 'sessions.issue.unparsableEvent': 'Unparsable event',
772
+ 'sessions.issue.replayDuplicateRows': 'Replayed duplicate rows (lossless cleanup)',
773
+ 'sessions.issue.syntheticCloser': 'Synthetic closer conflicts with real continuation',
774
+ 'sessions.issue.seqGap': 'Sequence gap',
775
+ 'sessions.issue.missingMessageId': 'Message is missing its id',
776
+ 'sessions.issue.emptyToolCallId': 'Empty tool-call id',
777
+ 'sessions.issue.danglingToolCall': 'Tool call without a matching result',
778
+ 'sessions.issue.toolResultIdMismatch': 'Tool result id does not match its call',
779
+ 'sessions.issue.duplicateToolCallId': 'Duplicate tool-call id in one step',
780
+ 'sessions.issue.invalidSettlement': 'Invalid assistant settlement fields',
781
+ 'sessions.issue.closedTurnContinued': 'Closed turn continued',
782
+ 'sessions.issue.formatNewer': 'Session format newer than this DSH (skipped silently)',
783
+ 'sessions.issue.unregisteredWorkspace': 'No workspace record points at it',
784
+ 'sessions.issue.subagentWithoutParent': 'Subagent session without its parent',
785
+ 'sessions.issue.unknown': 'Unrecognized issue',
572
786
  'sessions.severity.blocksStartup': 'Blocks DSH startup',
573
787
  'sessions.severity.invisible': 'Invisible',
574
788
  'sessions.severity.nextRequestFails': 'Next request fails',
575
789
  'sessions.severity.ok': 'Healthy',
576
790
  'sessions.severity.unloadable': 'Session cannot load',
577
791
  'sessions.subagent': 'Subagent',
792
+ 'sessions.blank': 'New Session',
578
793
  'sessions.truncated': 'List truncated: {count} more not shown (run the check again or narrow the scope).',
579
794
  'sessions.repair.title': 'How to repair',
580
795
  'sessions.repair.desc': 'For classes the in-app path does not touch (invalid containers / unreadable headers / future formats ...), preview in a terminal first; close DSH before writing.',
@@ -591,7 +806,6 @@ export const en: Record<keyof typeof zh, string> = {
591
806
  'sessions.repair.none': 'No session can be repaired in-app this time. In-app repair covers: replayed duplicate rows / provable synthetic closer blocks (lossless), and seq gaps or unparsable rows (truncation, LOSSY, needs confirmation). Everything else is in the offline commands below.',
592
807
  'common.close': 'Close',
593
808
  'sessions.dialog.open': 'Open session health check',
594
- 'sessions.dialog.hint': 'Opens a scan of the sessions on this machine: which ones cannot be read and which can be repaired in-app (the scan itself is read-only).',
595
809
  'sessions.dialog.lastScan': 'Last scan: {total} sessions, {issues} need attention, {repairs} repair record(s).',
596
810
  'sessions.repair.action': 'Repair',
597
811
  'sessions.repair.available': '{count} session(s) can be repaired in-app; click Repair on a row to preview, or use "Repair all" to process them in order.',
@@ -624,7 +838,6 @@ export const en: Record<keyof typeof zh, string> = {
624
838
  // F-2: the official session.export channel (raw-log ZIP built by DSH, including descendants and attachments)
625
839
  'sessions.zip.action': 'Download raw log (ZIP)',
626
840
  'sessions.zip.hint': "DSH packages this session's raw log (including descendant sessions and attachments) itself; the plugin is not involved, so orphaned and multi-generation logs come along.",
627
- 'sessions.zip.checking': 'Checking export capability…',
628
841
  'sessions.zip.reason.serviceMissing': 'The current DSH session-export channel is unavailable: the persistence backend is missing required services (session-query / session-persistence / attachments). This entry is disabled (status {status}).',
629
842
  'sessions.zip.reason.authRequired': 'The export channel could not be verified: the loopback request was rejected by authentication (status {status}). This does not mean export is unsupported here — restart DSH and try again.',
630
843
  'sessions.zip.reason.readFailed': "The export channel is live, but this session's log could not be read (status {status}). Repair this session above first.",
@@ -661,69 +874,195 @@ export const en: Record<keyof typeof zh, string> = {
661
874
  'sessions.repair.verify.unverified': 'Unverified: this machine cannot run the real-codec re-check, so DSH being able to read it right now is NOT proven -- unverified is not the same as verified (that says nothing about whether the write landed).',
662
875
  'sessions.repair.verify.rollbackFailed': 'Dangerous state: after the re-check failed, the automatic rollback did NOT succeed; the target still holds the repaired bytes and needs manual handling.',
663
876
  'sessions.repair.rejectedNext': 'The original file was not modified. Run the health check again for the latest state; if the reason mentions a changed file or active writing, retry once that session is quiet.',
664
- // C-1 Q4 checkpoints (t7): the UI renders the body.code / warn / guidance codes returned by
665
- // routes/recovery.ts instead of guessing a reason from the HTTP status.
666
- 'checkpoint.code.storage-unavailable': 'Checkpoint storage unavailable',
667
- 'checkpoint.code.ledger-write-failed': 'Could not write the checkpoint ledger',
668
- 'checkpoint.code.confirmation-required': 'Explicit confirmation is required to rewind',
669
- 'checkpoint.code.record-not-found': 'Checkpoint not found',
670
- 'checkpoint.code.protected-checkpoint': 'This checkpoint is protected: it cannot be rewound or deleted',
671
- 'checkpoint.code.record-incomplete': 'This checkpoint is incomplete; rewinding is refused by default',
672
- 'checkpoint.code.guard-failed': 'Could not capture the pre-rewind protection point; aborted with zero writes',
673
- 'checkpoint.code.invalid-input': 'Invalid request',
674
- 'checkpoint.code.no-chunks': 'No workspace path chunks were provided',
675
- 'checkpoint.code.chunk-not-absolute': 'Workspace chunks must be absolute paths',
676
- 'checkpoint.code.chunk-not-found': 'Workspace chunk is missing or unusable',
677
- 'checkpoint.code.no-session-input': 'No session was specified (unitId or sessionLogPath)',
678
- 'checkpoint.code.session-missing': 'Session log not found',
679
- 'checkpoint.code.session-out-of-root': 'Session log is outside the local session root; refused',
680
- 'checkpoint.code.session-too-large': 'Session log exceeds the copy limit: only the cursor was captured, so this state cannot be rewound automatically',
681
- 'checkpoint.code.session-active': 'The session log was written recently; refused to avoid overwriting an active conversation',
682
- 'checkpoint.code.session-diverged': 'The session log is shorter than the captured cursor (rewritten or truncated elsewhere); refused',
683
- 'checkpoint.code.capture-degraded': 'This state was captured incompletely (skipped links or unreadable entries are listed)',
684
- 'checkpoint.code.capture-truncated': 'This state hit the capture limit and was truncated',
685
- 'checkpoint.code.capture-failed': 'Failed to capture this state',
686
- 'checkpoint.code.nothing-to-restore': 'This checkpoint has nothing to restore',
687
- 'checkpoint.code.invalid-path': 'A recorded path is outside its capture chunk; that entry was refused',
688
- 'checkpoint.code.object-missing': 'The content copy is missing; that entry was refused',
689
- 'checkpoint.code.object-hash-mismatch': 'The content copy does not match the recorded fingerprint; that entry was refused',
690
- 'checkpoint.code.restore-failed': 'Restore failed (see per-entry details)',
691
- // Warnings raised while capturing or rewinding (never blocking, always visible)
692
- 'checkpoint.warn.ledgerUnreadable': 'Checkpoint ledger unreadable (continuing with an empty ledger)',
693
- 'checkpoint.warn.trackMissing': 'A state was not captured: it cannot be rewound',
694
- 'checkpoint.warn.trackDegraded': 'A state was captured incompletely (the rewind may be incomplete)',
695
- 'checkpoint.warn.protected': 'This checkpoint is protected: no rewind, no delete',
696
- 'checkpoint.warn.guardFailed': 'The pre-rewind protection point failed (continued with a warning)',
697
- // Degradation guidance: missing storage stack / unwritable data dir / read-only fallback
698
- // / probe detail / session offline handling / written back in place / partially restored workspace
699
- 'checkpoint.guidance.mountStorage': 'Checkpoint storage stack missing: add @deepseek-ai/dsh-storage, dsh-storage-json and dsh-storage-domain to the current profile and retry',
700
- 'checkpoint.guidance.fixDataDir': 'Plugin data directory is not writable: {detail}',
701
- 'checkpoint.guidance.readOnlyFallback': 'While storage is unavailable, capture and rewind fail cleanly; the current list stays readable',
702
- 'checkpoint.guidance.probeDetail': 'Probe detail: {detail}',
703
- 'checkpoint.guidance.sessionOffline': 'This session log is over the limit or its copy is missing: handle it with the offline command (path in detail)',
704
- 'checkpoint.guidance.sessionRewritten': 'Session log was written back in place (path in detail); the log format and location are unchanged',
705
- 'checkpoint.guidance.workspacePartial': 'The workspace may be partially restored: later segments (config/session) were not attempted',
706
- // Minimal panel labels (used by the UI in a later round; keys are in place so a missing one fails the build)
707
- 'checkpoint.title': 'Checkpoints (session / workspace / config at one point)',
708
- 'checkpoint.desc': 'Each capture records capturedAt and a fingerprint per state, so same-point is verifiable; a rewind only overwrites captured content and never deletes files created later.',
709
- 'checkpoint.capture': 'Capture a checkpoint',
710
- 'checkpoint.capture.chunks': 'Workspace paths (one absolute path per line)',
711
- 'checkpoint.capture.session': 'Session',
712
- 'checkpoint.capture.protect': 'Protect this checkpoint (no rewind, no delete)',
713
- 'checkpoint.list.empty': 'No checkpoints yet',
714
- 'checkpoint.col.time': 'Time',
715
- 'checkpoint.col.states': 'States',
716
- 'checkpoint.col.spread': 'Same-point spread',
717
- 'checkpoint.samePoint.yes': 'Same point ({ms} ms spread)',
718
- 'checkpoint.samePoint.no': 'Not one point: {reason}',
719
- 'checkpoint.state.workspace': 'Workspace',
720
- 'checkpoint.state.session': 'Session cursor',
721
- 'checkpoint.state.config': 'Plugin config',
722
- 'checkpoint.preview': 'Preview rewind',
723
- 'checkpoint.rewind': 'Rewind to here',
724
- 'checkpoint.rewind.confirm': 'I confirm: this checkpoint will overwrite the workspace / config / session (files created later are not deleted)',
725
- 'checkpoint.delete': 'Delete checkpoint',
726
- 'checkpoint.legacy.guard': 'Pre-rewind protection point',
877
+ 'rollback.title': 'Workspace + session rollback',
878
+ 'rollback.desc': 'A rollback point is kept automatically at the end of every turn: a workspace snapshot plus the session cursor -- there is no manual capture form. A rewind only overwrites the workspace paths that point recorded, and deletes the paths proven to have been created after that point which pass the safety rules (.git / node_modules / the plugin data directory / oversized files are always refused; files that already existed but were passed over by the budget are in the proof list too, so they are never mistaken for new ones; nothing is removed while the proof list itself is incomplete). Session rollback forks a new child session at the stable boundary before the target turn, and the original session log stays read-only and is never rewritten.',
879
+ 'rollback.list.empty': 'No rollback points yet',
880
+ /* 2026-10-11 layout refactor: the toolbar keeps only the numeric summary + refresh;
881
+ retention/pruning wording stays in rollback.retention.hint (now carried by the ⓘ). */
882
+ 'rollback.summary': '{points} rollback points · {sessions} sessions',
883
+ 'rollback.refresh': 'Refresh',
884
+ // Rollback-point ledger dialog (2026-10-12: the card no longer folds; the ledger lives in this dialog, grouped by session)
885
+ 'rollback.dialog.title': 'Rollback point ledger',
886
+ 'rollback.dialog.open': 'View rollback points',
887
+ // Ledger dialog sidebar (conversations that own rollback points; newest first by default)
888
+ 'rollback.sessions.title': 'Conversations with rollback points',
889
+ 'rollback.col.time': 'Time',
890
+ 'rollback.col.session': 'Session',
891
+ 'rollback.col.turn': 'Turn',
892
+ 'rollback.col.states': 'States',
893
+ 'rollback.turn': 'Turn {n}',
894
+ 'rollback.turnUnknown': 'Turn unknown',
895
+ 'rollback.badge.incomplete': 'One of two states missing',
896
+ 'rollback.badge.protected': 'Protected (kept forever)',
897
+ 'rollback.apiMissing': 'This host has no rollback-point support (plugin too old): the UI does not pretend otherwise',
898
+ 'rollback.retention.hint': 'Automatic points: at most {perSession} per session and {total} in total; eviction only drops the oldest automatic points. Pre-rewind rescue points do not count against those two budgets, and those budgets do not evict them; but you cannot delete them, so they have their own two limits: at most {guardsPerSession} per session and {guards} in total (whichever limit is passed, the oldest rescue point is evicted).',
899
+ 'rollback.autopoint.hint': 'Rollback points are kept automatically at the end of each turn: workspace snapshot plus session cursor (there is no manual capture entry).',
900
+ 'rollback.state.workspace': 'Workspace',
901
+ 'rollback.state.session': 'Session',
902
+ 'rollback.state.unknown': 'Unknown state',
903
+ 'rollback.mode.label': 'Rollback scope',
904
+ 'rollback.mode.filesAndSession': 'Workspace files + session',
905
+ 'rollback.mode.filesOnly': 'Workspace files only',
906
+ 'rollback.mode.sessionOnly': 'Session only',
907
+ 'rollback.mode.unknown': 'Unknown rollback scope',
908
+ 'rollback.kind.auto': 'Automatic point',
909
+ 'rollback.kind.guard': 'Pre-rewind rescue point (rewindable, not deletable)',
910
+ 'rollback.kind.guardShort': 'Rescue point',
911
+ 'rollback.kind.unknown': 'Unknown origin',
912
+ 'rollback.code.ok': 'Success',
913
+ 'rollback.code.deleted': 'Deleted',
914
+ 'rollback.code.confirmation-required': 'Explicit confirmation is required before a rewind',
915
+ 'rollback.code.protected-point': 'This rollback point was refused: a protected point can be neither rewound nor deleted; a rescue point can be a rewind target, but you cannot delete it',
916
+ 'rollback.code.record-not-found': 'Rollback point not found',
917
+ 'rollback.code.record-incomplete': 'This point is missing one of its two states, so a rewind is refused by default',
918
+ 'rollback.code.invalid-input': 'Invalid request parameters',
919
+ 'rollback.code.created-removals-limit': 'The number of paths to delete that this point does not record exceeds the limit: to avoid deleting only half of them, the whole rewind was refused and nothing was touched (neither workspace nor session)',
920
+ 'rollback.code.no-boundary': 'No usable stable boundary before that turn (session rollback unavailable)',
921
+ 'rollback.code.storage-unavailable': 'Rollback-point storage is unavailable',
922
+ 'rollback.code.mutation-locked': 'Another write operation is running (or safe mode is on): retry later',
923
+ 'rollback.code.method-not-allowed': 'This endpoint does not support that request method',
924
+ 'rollback.code.ledger-unreadable': 'Rollback-point ledger unreadable (continuing with an empty ledger, not silently)',
925
+ 'rollback.code.ledger-write-failed': 'Could not write the rollback-point ledger',
926
+ 'rollback.code.invalid-path': 'A recorded path is outside its recorded chunk; that entry was refused',
927
+ 'rollback.code.object-missing': 'The content copy is missing; that entry was refused',
928
+ 'rollback.code.object-hash-mismatch': 'The content copy does not match the recorded fingerprint; that entry was refused',
929
+ 'rollback.code.restore-failed': 'Rewind failed (see the per-item details)',
930
+ 'rollback.code.verify-failed': 'Post-rewind per-file verification failed; everything was rolled back to the pre-rewind state',
931
+ 'rollback.code.nothing-to-restore': 'This rollback point has nothing to restore',
932
+ 'rollback.code.no-recorded-files': 'This rollback point recorded no files',
933
+ 'rollback.code.file-not-recorded': 'This path is not recorded in the rollback point',
934
+ 'rollback.code.guard-failed': 'The pre-rewind rescue point could not be kept, so the rewind was aborted (zero writes)',
935
+ 'rollback.code.session-missing': 'Session log not found',
936
+ 'rollback.code.session-out-of-root': 'Session log is outside the local session root; refused',
937
+ 'rollback.code.session-too-large': 'Session log exceeds the limit: only the cursor was recorded (the rewind does not depend on the copy)',
938
+ // t7 targeted wording fix (captain-authorized; existing key, not a new one): the code now has two uses —
939
+ // (1) preview.session.reasonCode (informational); (2) a *result* code: a files-including rewind is refused while the conversation runs.
940
+ 'rollback.code.session-active': 'This conversation is running (its session log was written recently): a rewind that includes files is refused and the workspace stays untouched; rewinding the conversation only is unaffected (the rewind forks a child session and never rewrites the log)',
941
+ // t8 (append-only; the key above keeps its own wording): the conversation is running
942
+ 'rollback.preview.sessionActive': 'This conversation is running: until the current turn finishes you can rewind the conversation only (no workspace changes), so nothing races with the tool that is writing files. Reopen this dialog after the turn to rewind files as well.',
943
+ 'rollback.sessionActive.denied': 'This conversation is running: no files were changed. Wait for the current turn to finish, or rewind the conversation only.',
944
+ 'rollback.code.session-fork-unavailable': 'This host provides no session fork capability, so the session cannot be rolled back',
945
+ 'rollback.code.fork-failed': 'Forking the child session failed; the session segment was not rolled back',
946
+ 'rollback.code.capture-failed': 'Failed to capture this state',
947
+ 'rollback.code.capture-truncated': 'This state hit the capture limit and was truncated (recorded honestly)',
948
+ 'rollback.code.capture-degraded': 'This state was captured incompletely (skipped links or unreadable entries are recorded)',
949
+ 'rollback.code.no-chunks': 'No workspace path chunks were provided',
950
+ 'rollback.code.chunk-not-absolute': 'Workspace chunks must be absolute paths',
951
+ 'rollback.code.chunk-not-found': 'Workspace chunk is missing or unusable',
952
+ 'rollback.code.unknown': 'Unrecognized error code (technical detail below)',
953
+ 'rollback.guidance.mountStorage': 'Rollback-point storage stack missing: combine @deepseek-ai/dsh-storage, dsh-storage-json and dsh-storage-domain into the current profile, then retry',
954
+ 'rollback.guidance.fixDataDir': 'The plugin data directory is not writable: {detail}',
955
+ 'rollback.guidance.readOnlyFallback': 'While storage is unavailable, keeping points and rewinding fail cleanly, but the current list is still viewable (nothing crashes and nothing pretends to succeed)',
956
+ 'rollback.guidance.probeDetail': 'Probe detail: {detail}',
957
+ 'rollback.guidance.workspaceRolledBack': 'After a workspace write failed, everything was rolled back to the pre-rewind state (the session segment was not attempted)',
958
+ 'rollback.guidance.forkUnavailable': 'This host provides no session fork capability: the session segment was not rolled back (the original log was not rewritten)',
959
+ 'rollback.guidance.sessionForked': 'Forked a new child session: {detail} (the original session log stays read-only and was not rewritten)',
960
+ 'rollback.guidance.sessionNotAttached': 'The new child session could not be filed back into its original workspace (it appears under "Ungrouped"; the rewind itself did apply)',
961
+ 'rollback.guidance.unknown': 'Unrecognized guidance (technical detail below)',
962
+ 'rollback.warn.ledgerUnreadable': 'Rollback-point ledger unreadable (continuing with an empty ledger, not silently)',
963
+ 'rollback.warn.trackMissing': 'A state was not captured: it cannot be rolled back this time',
964
+ 'rollback.warn.trackDegraded': 'A state was captured incompletely (the rewind may be incomplete)',
965
+ 'rollback.warn.protected': 'This rollback point is protected (kept forever): no rewind, no delete',
966
+ 'rollback.warn.guardFailed': 'The pre-rewind rescue point failed (continued with a warning)',
967
+ 'rollback.warn.createdRefused': 'Nothing created after this point was deleted: the capture-time proof list is incomplete (a whole directory was ignored / the path index is missing / a directory was unreadable), so newly created paths cannot be proven',
968
+ 'rollback.warn.unknown': 'Unrecognized warning (technical detail below)',
969
+ 'rollback.preview': 'Preview rewind',
970
+ 'rollback.preview.title': 'Rewind preview',
971
+ 'rollback.preview.hint': 'Read-only preview below (zero writes). The rewind only runs after you confirm.',
972
+ 'rollback.preview.loading': 'Generating preview…',
973
+ 'rollback.preview.empty': 'This rollback point has nothing to restore.',
974
+ 'rollback.preview.none': 'This point has nothing to restore',
975
+ 'rollback.preview.reason.guard': 'The host did not allow rewinding this rescue point (a rescue point can normally be a rewind target; see the details below)',
976
+ 'rollback.preview.reason.incomplete': 'This point is missing one of its two states: you must explicitly allow a partial rewind',
977
+ 'rollback.preview.reason.hostRefused': 'The host refused this rewind (see the details below)',
978
+ 'rollback.preview.segments': 'Tick the restorable segments only (there are just two states: workspace and session)',
979
+ 'rollback.preview.overwrite': 'Overwrite {n} item(s)',
980
+ 'rollback.preview.different': '{n} of them differ in content',
981
+ 'rollback.preview.missing': '{n} item(s) no longer exist (they will be recreated)',
982
+ 'rollback.preview.createdAfter': '{n} item(s) did not exist at that point (created afterwards; the rewind deletes only those that pass the safety rules)',
983
+ 'rollback.preview.removalsTitle': 'Files the rewind will delete',
984
+ 'rollback.preview.notice.skipped': '{n} item(s) were skipped at capture time (not recorded; they are in the proof list, so they are outside the deletion test)',
985
+ 'rollback.preview.notice.truncated': 'Capture was truncated by a limit (per-file / byte budget / file-count budget); whether anything is deleted also depends on whether the proof list (path index) is complete',
986
+ 'rollback.preview.files.title': 'Files that will be rewound:',
987
+ 'rollback.preview.files.empty': 'The workspace already matches this rollback point, nothing to restore',
988
+ 'rollback.preview.files.unavailable': 'The workspace state of this rollback point has no usable record (reason: {reason}), nothing to restore',
989
+ 'rollback.preview.files.unavailableSession': 'The workspace state of this rollback point has no usable record (reason: {reason}); the session will still be rewound to before this point',
990
+ 'rollback.preview.files.noFilesSession': 'This rollback point recorded no files; the session will still be rewound to before this point',
991
+ 'rollback.preview.files.consistentSession': 'The workspace already matches this rollback point (the session will still be rewound to before this point)',
992
+ 'rollback.preview.file.added': '+{n}',
993
+ 'rollback.preview.file.removed': '\u2212{n}',
994
+ 'rollback.preview.file.deleted': 'Delete',
995
+ 'rollback.preview.file.unknown': 'line counts not measured',
996
+ 'rollback.preview.file.unknownHint': 'Line counts could not be measured for these files (too large, not text, or the content copy is unreadable) \u2014 the rewind will still overwrite them.',
997
+ 'rollback.diff.viewHint': 'View diff: {path}',
998
+ 'rollback.diff.loading': 'Loading the diff\u2026',
999
+ 'rollback.diff.before': 'Current',
1000
+ 'rollback.diff.after': 'After rewind',
1001
+ 'rollback.diff.identical': 'This file already matches the rollback point, so the rewind will not change it',
1002
+ 'rollback.diff.degraded': 'The diff is large, so it is shown as a whole-block replacement (still complete, just not a minimal edit script)',
1003
+ 'rollback.diff.truncated': 'Only the first {shown} of {total} rows are drawn (the data itself is complete)',
1004
+ 'rollback.diff.reason.missingBlob': 'The stored content copy cannot be read, so no line-by-line comparison is possible (the rewind will still try to write it back)',
1005
+ 'rollback.diff.reason.binary': 'Not a text file, so there is no line-by-line diff to show',
1006
+ 'rollback.diff.reason.tooLarge': 'The file exceeds the display limit, so there is no line-by-line diff (the rewind will still overwrite it)',
1007
+ 'rollback.diff.reason.unreadable': 'The current file cannot be read (permissions, or it is not a regular file), so no line-by-line comparison is possible',
1008
+ 'rollback.diff.reason.unknown': 'No line-by-line comparison is possible, and the host gave no reason (cause unknown)',
1009
+ 'rollback.diff.failed': 'Could not read the diff',
1010
+ 'rollback.preview.result.noop': 'The workspace already matches this rollback point, nothing to restore',
1011
+ 'rollback.preview.result.noRecord': 'This rollback point has no usable record (reason: {reason}), nothing to restore',
1012
+ 'rollback.preview.result.noFiles': 'This rollback point recorded no files, nothing to restore',
1013
+ 'rollback.preview.result.code': 'code: {code}',
1014
+ 'rollback.preview.warningsTitle': 'Warnings for this point',
1015
+ 'rollback.preview.cursorBytes': 'Session log {now} → {target} bytes',
1016
+ 'rollback.group.label': 'Session: {session}',
1017
+ 'rollback.group.count': '{n} point(s)',
1018
+ 'rollback.group.unknownSession': 'Unknown session',
1019
+ 'rollback.row.files': '{n} file(s)',
1020
+ 'rollback.row.sessionCursor': 'Has session cursor',
1021
+ 'rollback.row.noSessionCursor': 'No session cursor (workspace only)',
1022
+ 'rollback.row.sessionShort': 'Session rewindable',
1023
+ 'rollback.row.workspaceShort': 'Workspace only',
1024
+ 'rollback.row.notice.skipped': '{n} item(s) were skipped at capture time (not recorded; they are in the proof list and are never mistaken for newly created ones)',
1025
+ 'rollback.row.notice.truncated': 'This point hit the capture limit and was truncated (counts are still reported; deletion also depends on whether the proof list is complete)',
1026
+ 'rollback.row.notice.skippedShort': '{n} skipped',
1027
+ 'rollback.row.notice.truncatedShort': 'Truncated',
1028
+ 'rollback.preview.cursor': 'Session boundary {from} → {to}',
1029
+ 'rollback.preview.turns': 'Roll back to before turn {n}',
1030
+ 'rollback.preview.active': 'This session log was written recently (informational only: the rewind forks a child session and does not rewrite the log)',
1031
+ 'rollback.rewind': 'Rewind to here',
1032
+ 'rollback.rewind.confirm': 'I confirm: rewind with the selected scope. The workspace writes back the paths this point recorded and deletes the paths proven to have been created after that point which pass the safety rules (.git / node_modules / the plugin data directory / oversized files are always refused; nothing is removed while the proof list itself is incomplete); the session is forked into a new child session at the stable boundary before the target turn, and the original session log is not rewritten.',
1033
+ 'rollback.rewind.allowPartial': 'Allow a partial rewind (this point is missing one of its two states)',
1034
+ 'rollback.guard.require': 'A rescue point is mandatory before the rewind (if it cannot be kept, everything is aborted with zero writes)',
1035
+ 'rollback.guard.warn': 'Keep a rescue point before the rewind (if it cannot be kept, warn and continue)',
1036
+ 'rollback.guard.off': 'Skip the pre-rewind rescue point',
1037
+ 'rollback.guard.unknown': 'Unknown rescue-point policy',
1038
+ 'rollback.rewind.result.title': 'Rewind result',
1039
+ 'rollback.rewind.result.counts': '{restored} restored / {removed} deleted / {failed} failed / {leftovers} entries that did not exist at that point (created afterwards)',
1040
+ 'rollback.rewind.result.child': 'Rewound to before that message (current conversation: {id})',
1041
+ // A (seamless replace): the original session is filed under "Archived" — only one conversation stays in the list
1042
+ 'rollback.rewind.archive.done': 'The original session is now archived (restore it from "Archived" whenever you need it)',
1043
+ // Before the first user message there is no turn boundary to fork from: open a brand-new session
1044
+ 'rollback.rewind.freshSession': 'This is before the first message of the conversation, so a brand-new empty session was opened (copy that message from the bubble above to continue)',
1045
+ 'rollback.rewind.freshSessionFailed': 'There is no conversation boundary to rewind to before the first message, and this client could not open a new session — use "New session" manually',
1046
+ 'rollback.rewind.archive.unavailable': 'This client exposes no archive entry, so the original session stays in the session list (its content was not rewritten)',
1047
+ 'rollback.rewind.archive.active': 'The original session still has work in progress and cannot be archived yet — it stays in the session list (its content was not rewritten)',
1048
+ 'rollback.rewind.archive.failed': 'Archiving the original session failed; it stays in the session list (its content was not rewritten)',
1049
+ 'rollback.rewind.result.guard': 'Pre-rewind rescue point: {id}',
1050
+ 'rollback.rewind.result.skipped': 'Segments not attempted: {list}',
1051
+ 'rollback.segment.restored': 'Restored',
1052
+ 'rollback.segment.skipped': 'Skipped',
1053
+ 'rollback.segment.failed': 'Failed',
1054
+ 'rollback.segment.unknown': 'Unknown status',
1055
+ 'rollback.outcome.restored': 'Fully restored',
1056
+ 'rollback.outcome.partial': 'Partially restored',
1057
+ 'rollback.outcome.failed': 'Rewind failed',
1058
+ 'rollback.outcome.denied': 'Refused (zero writes)',
1059
+ 'rollback.outcome.unknown': 'Unknown outcome',
1060
+ 'rollback.delete': 'Delete rollback point',
1061
+ 'rollback.delete.done': 'Rollback point deleted',
1062
+ 'rollback.delete.confirmTitle': 'Delete this rollback point?',
1063
+ 'rollback.delete.confirmMessage': 'This deletes only the ledger record and its content copies; the workspace and sessions are untouched. Protected points and pre-rewind rescue points are always refused by the engine; rescue points beyond their per-session limit of 20 or their global limit of 50 are evicted automatically (oldest first).',
1064
+ 'rollback.delete.protected': 'This rollback point cannot be deleted: protected points and rescue points are always refused by the engine (rescue points beyond their per-session limit of 20 or their global limit of 50 are evicted automatically, oldest first)',
1065
+ 'rollback.storage.unavailable': 'Rollback-point storage is unavailable (the current list remains viewable)',
727
1066
  'recovery.incomplete.title': 'Unfinished profile copy',
728
1067
  'recovery.incomplete.hint': 'These directories carry a "copy in progress" marker but no package.json: a half-finished copy left by an interrupted copy. They cannot be launched, only deleted (only marked directories are listed — hand-made directories never appear here).',
729
1068
  'recovery.incomplete.from': 'From: {name}',