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
@@ -0,0 +1,391 @@
1
+ /**
2
+ * 每条用户消息的「↺ 回退」入口 —— **接管 `conversation.chat.node` 的 `user` key 渲染器**。
3
+ *
4
+ * 为什么必须接管(而不是追加):该 key 没有 children 表(宿主只对 turn-tail 声明了子位),
5
+ * framework 没有「往用户消息里加 UI」的位子;唯一合法做法是 priority -1 遮蔽宿主的
6
+ * `UserMessageNodeView`,然后在自己的渲染里把那条气泡**复刻**一遍(宿主组件 / CSS Modules
7
+ * 全是 ui-chat 私有、不可导入 —— 外观靠本仓库 CSS 逼近,图片**必须**走 owner 给的
8
+ * `renderMessageImages`)。
9
+ *
10
+ * 三件硬约束(本文件是它们的落点):
11
+ * ① **用户消息绝不消失**:整个子树包在 `ChatEntryErrorBoundary` 里,抛错就降级成
12
+ * **纯文本气泡**(文本仍可见)+ ASCII `console.warn`;即使边界自己也没兜住,
13
+ * ui-renderer 的 per-entry 边界还会让我们的登记**abdicate**、退回宿主渲染器
14
+ * (SlotErrorBoundary → reportEntryError({ abdicate: true }) → outlet 重渲染到 cell 的下一个幸存者)。
15
+ * ② **不新造通道**:点开的是既有复核对话框 `RollbackReviewDialog`(T4/T20 的判定与写路径)。
16
+ * ③ 回合号只认 `node.location`(`chatNodeTurn`,与宿主 turnTail 同一条读法);取不到 → 如实禁用。
17
+ *
18
+ * 组件的全部判定在 `chat-entry-view.ts`(node 可测):本文件只做装配、订阅与渲染。
19
+ */
20
+ import { Component, Fragment, createElement, useCallback, useEffect, useMemo, useState, type ReactNode } from 'react'
21
+ import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
22
+ import type { TranslateNS } from '../client-types.ts'
23
+ import type { RollbackPointPort } from '../../ui/rollback-view.ts'
24
+ import { CopyStateIcon, RefreshIcon } from '../common/Icon.tsx'
25
+ import { copyTextToClipboard } from '../common/clipboard.ts'
26
+ import { RollbackReviewDialog } from './RollbackReviewDialog.tsx'
27
+ import type { RollbackDiffOpenResult } from './rollback-diff-tab.ts'
28
+ import {
29
+ buildChatEntrySafeRow,
30
+ buildChatEntryView,
31
+ chatEntryButtonState,
32
+ chatEntryTurnPhase,
33
+ planChatEntryAttachments,
34
+ lookupPointForTurn,
35
+ rawBlockText,
36
+ CHAT_ENTRY_WARN_PREFIX,
37
+ type ChatEntryBubbleView,
38
+ type ChatEntryFallbackRow,
39
+ type ChatEntryFile,
40
+ type ChatPointLookup,
41
+ } from './chat-entry-view.ts'
42
+ import { ChatPointWindowController, type ChatPointWindowState } from './chat-point-window.ts'
43
+ import type { ArchiveOutcome } from './rollback-entry-view.ts'
44
+ import type { ChildOpenState } from './rollback-entry-view.ts'
45
+ import type { RollbackFileIconRenderer } from './RollbackFileIcon.tsx'
46
+ // 槽位契约的类型镜像(SlotMap 的 conversation.chat.node entry),见该文件头部说明。
47
+ import type {} from './chat-slots.ts'
48
+ import css from '../config-manager.module.css'
49
+
50
+ const NEWLINE = String.fromCharCode(10)
51
+
52
+ /**
53
+ * 图片渲染位(宿主 `conversation.message.images` 的 renderSlot 绑定)。
54
+ *
55
+ * 为什么签名这么宽:真实 props 合并了 ui-primitives / ui-session 的类型,本仓库**没有安装**它们;
56
+ * 我们只承诺「原样转发 `{ images, align, compact, loadImage }`」(逐字对齐宿主
57
+ * `renderMessageImages = (owner) => renderSlot('conversation.message.images', { ...owner, loadImage })`),
58
+ * 返回值当作不透明 ReactNode 渲染 —— 绝不自己实现图片渲染。
59
+ */
60
+ export type MessageImagesRenderer = (owner: unknown) => ReactNode
61
+
62
+ /** 本条目经 inject 面拿到的业务能力(与回合尾入口同一个 inject 形状)。 */
63
+ export interface ChatEntryViewInjected {
64
+ /** 回退端口(T2 的 RecoveryApi;组件**不直接 fetch**)。 */
65
+ port: RollbackPointPort
66
+ /** recovery 字典(rollback.* 词汇单一所有)。 */
67
+ rt: TranslateNS<'config-manager-recovery'>
68
+ /** 打开 fork 出来的子会话(降级态由对话框如实呈现)。 */
69
+ openChildSession: (childId: string) => Promise<ChildOpenState>
70
+ /** A(无感替换):回退后把原会话收进「已归档」;失败一律结构化返回,由对话框如实呈现。 */
71
+ archiveParentSession: (sessionId: string) => Promise<ArchiveOutcome>
72
+ /** 第一条消息之前没有可 fork 的边界:开一条全新会话(uiWorkspace.startSession 同一条路)。 */
73
+ startFreshSession: () => Promise<{ ok: true } | { ok: false; code: string }>
74
+ /**
75
+ * 「查看」:把该文件的差异开进 DSH 右侧栏(装配点注入)。侧栏不可用时返回 no-sidebar,
76
+ * 由预览对话框如实降级为弹窗内展示 —— 绝不给一个点了没反应的入口。
77
+ */
78
+ openDiff: (pointId: string, filePath: string) => RollbackDiffOpenResult
79
+ /**
80
+ * 文件类型图标的外部来源(可选):装了 dsh-better-sidebar 时用它的 `betterSidebar.fileIcon`,
81
+ * 否则对话框回落到 DSH 自带的 `FileTypeIcon`(第 7 条,详见 RollbackFileIcon.tsx)。
82
+ */
83
+ fileIcon?: RollbackFileIconRenderer
84
+ }
85
+
86
+ /**
87
+ * 完整 props:owner/keyProps 面(`PropsRuntime` 从 SlotMap 推导出 node / renderMessageImages 等)
88
+ * + session 标准 props + 本插件字典(`t`)+ inject 面。
89
+ *
90
+ * 注意 `props.node` 的静态类型是 `unknown`(镜像里刻意不写死宿主节点形状);真正读它的地方全在
91
+ * `chat-entry-view.ts` 的宽度接收纯函数里。
92
+ */
93
+ export type ChatEntryViewProps = PropsRuntime<'conversation.chat.node', 'user'> & PropsLocale<'config-manager-chat'> & ChatEntryViewInjected
94
+
95
+ /** 文件卡:文件名 + 扩展名徽标 + 体积(扩展名徽标 = 类型图标的诚实兜底,见能力对照表)。 */
96
+ function FileCard({ file, size }: { file: ChatEntryFile; size: string | null }) {
97
+ return (
98
+ <span className={css.chatFileCard} title={file.name !== '' ? file.name : undefined}>
99
+ {file.ext !== null && <span className={css.chatFileExt}>{file.ext}</span>}
100
+ <span className={css.chatFileName}>{file.name !== '' ? file.name : '?'}</span>
101
+ {size !== null && <span className={css.chatFileMeta}>{size}</span>}
102
+ </span>
103
+ )
104
+ }
105
+
106
+ /** 未知块:宿主用 JsonBlock,这里用原生 `<details>` + 预格式化 JSON(零新增依赖、零新增通道)。 */
107
+ function RawBlock({ payload, label, truncatedLabel }: { payload: unknown; label: string; truncatedLabel: (total: number) => string }) {
108
+ const text = rawBlockText(payload)
109
+ const max = 4000
110
+ const shown = text.length > max ? text.slice(0, max) + NEWLINE + truncatedLabel(text.length) : text
111
+ return (
112
+ <details className={css.chatRawBlock}>
113
+ <summary className={css.chatRawSummary}>{label}</summary>
114
+ <pre className={`${css.chatRawPre} ${css.mono}`}>{shown}</pre>
115
+ </details>
116
+ )
117
+ }
118
+
119
+ /** 复制动作按钮(成功才切「已复制」;失败不给假信号 —— 与 common/CopyButton 同语义)。 */
120
+ function ChatEntryCopyAction({ text, t }: { text: string; t: TranslateNS<'config-manager-chat'> }) {
121
+ const [copied, setCopied] = useState(false)
122
+ useEffect(() => {
123
+ if (!copied) return undefined
124
+ const timer = setTimeout(() => { setCopied(false) }, 1000)
125
+ return () => { clearTimeout(timer) }
126
+ }, [copied])
127
+ const onCopy = useCallback(() => {
128
+ if (copied) return
129
+ void copyTextToClipboard(text).then((ok) => { if (ok) setCopied(true) })
130
+ }, [copied, text])
131
+ return (
132
+ <button
133
+ type="button"
134
+ className={css.chatActionBtn}
135
+ aria-label={copied ? t('chat.entry.copied') : t('chat.entry.copy')}
136
+ title={copied ? t('chat.entry.copied') : t('chat.entry.copy')}
137
+ onClick={onCopy}
138
+ >
139
+ <CopyStateIcon done={copied} size={13} />
140
+ </button>
141
+ )
142
+ }
143
+
144
+ interface ChatEntryBodyProps extends ChatEntryViewInjected {
145
+ node: unknown
146
+ sessionId: string
147
+ /** 宿主图片渲染位(SlotMap 的 keyProps 把返回值声明成 unknown ⇒ 用它当 `MessageImagesRenderer` 必须过一层窄化)。 */
148
+ renderMessageImages?: unknown
149
+ t: TranslateNS<'config-manager-chat'>
150
+ }
151
+
152
+ /**
153
+ * 一条用户消息的行主体:气泡(文本/图片/文件卡/时间/复制)+ 我们那颗「回退」按钮。
154
+ *
155
+ * 与宿主 UserStyleBubble 的逐条对照见任务 output 的能力对照表。**下面按现状分别写**(t12 修正:
156
+ * 旧注释把「引用」与「技能」并成同一句结论,已与本文件下方的渲染不符 —— 前者已复刻、后者仍未做):
157
+ * · **引用(会话引用)**:**已复刻**——线格式 `@[label](dsh-session:…)` 在纯投影层换成 label 文本
158
+ * (`projectChatEntryText`),且 `data.referenceLabels` 非空时渲染**宿主的引用汇总行**
159
+ * (见下方 `css.chatEntryReference` + `t('chat.entry.referenceSummary')` 那段,文案与宿主
160
+ * `引用会话 · {labels}` 逐字同义)。**仍有的差距**只有 chip 的**外观与可点开**(已追认)。
161
+ * · **技能(skillNames)**:**仍不渲染** —— 宿主交给 ui-primitives 的 `projectUserText` 在正文内部
162
+ * 把它渲染成 chip(包私有、不可导入)⇒ 我们不为技能产出任何界面元素(不编造内容)。
163
+ * · 文件类型图标:宿主用 ui-primitives 的 FileTypeIcon(包私有)⇒ 我们用**扩展名徽标**兜底;
164
+ * · 气泡底色:宿主直接用 `--dsw-specific-bubble`,本仓库 token 层没有这一档 ⇒ 用 `--cm-surface-2`
165
+ * (亮/暗主题下都是「比底更强的面」),不改 DESIGN 体系、不新增 token。
166
+ */
167
+ function ChatEntryBody({ node, sessionId, renderMessageImages, t, rt, port, openChildSession, archiveParentSession, startFreshSession, openDiff, fileIcon }: ChatEntryBodyProps) {
168
+ const view = useMemo(() => buildChatEntryView(node), [node])
169
+ const [lookup, setLookup] = useState<ChatPointLookup | null>(null)
170
+ /** 有界重试窗口是否还开着(**状态诚实**的唯一开关,R1)—— 由状态机的 `onState` 驱动。 */
171
+ const [windowOpen, setWindowOpen] = useState(false)
172
+ const [dialogTurn, setDialogTurn] = useState<number | null>(null)
173
+ const turn = view.turnNumber
174
+ const phase = chatEntryTurnPhase(turn === null ? null : { turn, status: view.turnStatus })
175
+ /**
176
+ * 「还没拿到结论」⇒ 按钮如实显示「正在读取回滚点…」(**不许**说成「没有回滚点」):
177
+ * · 窗口开着(首读在途 / 读到空还会再读 / 读失败还会再读)→ `pointsPending`;
178
+ * · 窗口关掉且仍为空 → `noPoint`(此时才是「已证实没有」)。
179
+ * 判据只用状态机给的两个事实(`windowOpen` + `lookup`),没有第三个需要手工同步的标志。
180
+ */
181
+ const retrying = windowOpen && (lookup === null || lookup.point === null)
182
+ const button = chatEntryButtonState({ node, sessionId, lookup, pending: false, retrying })
183
+
184
+ /**
185
+ * 读回滚点列表:**收尾之后的有界重试**(R1),不是一次性的惰取。
186
+ *
187
+ * 为什么必须重试:引擎在回合**收尾之后**才写点 —— 真机实测 turn/end 之后 **7.8 s**
188
+ * (session-e3a7953c…:turn/end 20:03:38.658Z → 该回合的点 createdAt 20:03:46.434Z)。
189
+ * 旧实现的 effect 条件是「读到过就永不重查」,于是首读撞上那个空窗就**永久禁用**;
190
+ * 现在按 `CHAT_POINT_RETRY_DELAYS_MS`(0.5/1.5/3/6/12 s)在 `CHAT_POINT_LOOKUP_WINDOW_MS`
191
+ * 窗口内再读,**命中即停**(命中后一个请求都不再发)。
192
+ *
193
+ * **一次 effect 调用 = 一个新实例**(T9 / RR2-A):React StrictMode 会走
194
+ * `effect → cleanup → effect` 双调用。实例若跨调用复用(此前放在 `useMemo` 里),第二次 `start()`
195
+ * 会因为 `started` 已置位而变成 no-op,而被 dispose 的那个循环又永不回调 ⇒ 窗口**永久**停在
196
+ * `open: true`、按钮永久停在「正在读取回滚点…」禁用(与原始症状同一类)。每次 effect 各建实例即无此路径。
197
+ *
198
+ * 又因为依赖里**没有任何 React 状态**(只有 `canLookup` / `port` / `sessionId` / `turn`),
199
+ * 状态回调不会把 effect 再跑一遍 ⇒ 同时满足 F1「一个挂载只跑一轮窗口」(不周期性重开)。
200
+ *
201
+ * 只在「回合已收尾」时才读 —— 收尾未知/未结束的回合注定没有点,不浪费这次请求(按钮此时已如实禁用)。
202
+ * 「用户再次触发」= 重新挂载(切档 / 重进会话)或会话/回合变化 ⇒ 新实例;不需要刷新页面。
203
+ */
204
+ const canLookup = turn !== null && phase === 'closed' && sessionId !== ''
205
+ useEffect(() => {
206
+ if (!canLookup || turn === null) return undefined
207
+ const controller = new ChatPointWindowController({
208
+ attempt: (force) => lookupPointForTurn({ port, sessionId, turn, force }),
209
+ onState: (state: ChatPointWindowState) => {
210
+ setLookup(state.lookup)
211
+ setWindowOpen(state.open)
212
+ },
213
+ })
214
+ controller.start()
215
+ // F2:取消通道(循环立即 settle)。dispose **不回调** onState —— 这里正是卸载/换会话,
216
+ // 回调只会在卸载后 setState(RR2-B:语义与注释对齐,见 chat-point-window.ts 的 dispose 文档)。
217
+ return () => { controller.dispose() }
218
+ }, [canLookup, port, sessionId, turn])
219
+
220
+ const openDialog = useCallback(() => {
221
+ if (button.kind !== 'enabled') return
222
+ setDialogTurn(button.turn)
223
+ }, [button])
224
+
225
+ const bubble: ChatEntryBubbleView = view.bubble
226
+ /**
227
+ * 附件按**content 原始顺序**输出(宿主 `attachments.map` 同构):图片走 owner 的
228
+ * `renderMessageImages`(每张一次调用),文件走文件卡 —— 两类**共用同一条序列**,
229
+ * 绝不重排成「先图片后文件」(t4 复核 D2)。
230
+ */
231
+ const canRenderImages = typeof renderMessageImages === 'function'
232
+ const attachmentNodes = planChatEntryAttachments(bubble).map((item) => item.kind === 'image'
233
+ ? (canRenderImages
234
+ ? (
235
+ <Fragment key={'image:' + String(item.index)}>
236
+ {(renderMessageImages as MessageImagesRenderer)(item.owner)}
237
+ </Fragment>
238
+ )
239
+ : null)
240
+ : <FileCard key={'file:' + String(item.index)} file={item.file} size={item.size} />)
241
+ const hasAttachments = attachmentNodes.some((node) => node !== null)
242
+ const reasonKey = button.kind === 'disabled' ? button.key : null
243
+
244
+ return (
245
+ <div className={css.chatEntryScope}>
246
+ <div className={css.chatEntryRow}>
247
+ <div className={css.chatEntryStack}>
248
+ {hasAttachments && (
249
+ <div className={css.chatAttachmentRow} data-message-attachments="">
250
+ {attachmentNodes}
251
+ </div>
252
+ )}
253
+ {bubble.showBubble && (
254
+ <div className={css.chatBubble}>
255
+ {bubble.text !== '' && <span className={css.chatBubbleText}>{bubble.text}</span>}
256
+ {bubble.raw.map((block) => (
257
+ <RawBlock
258
+ key={'raw-' + block.id}
259
+ payload={block.payload}
260
+ label={t('chat.entry.extraBlock')}
261
+ truncatedLabel={(total) => t('chat.entry.truncated', { total })}
262
+ />
263
+ ))}
264
+ </div>
265
+ )}
266
+ {!canRenderImages && bubble.images.length > 0 && (
267
+ <div className={css.chatEntryNotice}>{t('chat.entry.noImageRenderer')}</div>
268
+ )}
269
+ {/* 引用会话汇总行:宿主在气泡下方渲染 referenceSummary(`引用会话 · {labels}`)。
270
+ 我们复刻这一行(文案在 chat 字典里,**不依赖 ui-primitives**);`data-ref-chip` 供探针定位。 */}
271
+ {view.referenceLabels.length > 0 && (
272
+ <div className={css.chatEntryReference} data-ref-chip="">
273
+ {t('chat.entry.referenceSummary', {
274
+ labels: view.referenceLabels.join(t('chat.entry.referenceSeparator')),
275
+ })}
276
+ </div>
277
+ )}
278
+ </div>
279
+ <div className={css.chatEntryActions}>
280
+ {/* 时间在动作行**最前**(宿主把 MessageIconActions 的 clock 设为 "start",与它同构) */}
281
+ {view.clock !== null && <span className={css.chatEntryTime}>{view.clock}</span>}
282
+ <ChatEntryCopyAction text={bubble.text} t={t} />
283
+ {/* 回退入口:**只有图标**(无文字标签、无边框按钮样式),与时间戳/复制图标同一行同观感 ——
284
+ 2026-10-10 用户第二条要求「回退按钮只有一个 icon 而不是一个按钮」。可见文案退到
285
+ aria-label/title;禁用原因是原生 title 里的一句话(InfoHint 的 Portal 钉在插件设置页
286
+ 根节点,聊天区不存在)。按钮始终留在原地(禁用而不是消失):入口可见性不随判据漂移。 */}
287
+ <button
288
+ type="button"
289
+ className={css.chatActionBtn}
290
+ disabled={button.kind !== 'enabled'}
291
+ aria-label={t('chat.entry.label')}
292
+ title={reasonKey === null ? t('chat.entry.label') : t(reasonKey as 'chat.entry.disabled.noTurn')}
293
+ onClick={openDialog}
294
+ /* 只读判据(零样式、零文案):重试窗口开着时置位 —— 排障时一眼看出「正在读取」是暂态还是终态 */
295
+ data-chat-entry-window-open={windowOpen ? '' : undefined}
296
+ data-chat-entry-window-closed={windowOpen ? undefined : ''}
297
+ >
298
+ <RefreshIcon size={13} />
299
+ </button>
300
+ </div>
301
+ </div>
302
+ {dialogTurn !== null && (
303
+ <RollbackReviewDialog
304
+ sessionId={sessionId}
305
+ turn={dialogTurn}
306
+ port={port}
307
+ rt={rt}
308
+ t={t}
309
+ openChildSession={openChildSession}
310
+ archiveParentSession={archiveParentSession}
311
+ startFreshSession={startFreshSession}
312
+ openDiff={openDiff}
313
+ renderFileIcon={fileIcon}
314
+ onClose={() => { setDialogTurn(null) }}
315
+ />
316
+ )}
317
+ </div>
318
+ )
319
+ }
320
+
321
+ interface ChatEntryBoundaryProps {
322
+ node: unknown
323
+ t: TranslateNS<'config-manager-chat'>
324
+ /** 被保护的子树(`createElement` 的第三参给进来,故这里可选)。 */
325
+ children?: ReactNode
326
+ }
327
+
328
+ interface ChatEntryBoundaryState {
329
+ failed: boolean
330
+ row: ChatEntryFallbackRow | null
331
+ }
332
+
333
+ /**
334
+ * 错误边界:**用户消息绝不消失**。
335
+ *
336
+ * 为什么必须是 class 而不是函数组件:React 18 只有 class 能声明 `getDerivedStateFromError` /
337
+ * `componentDidCatch`(本仓库「函数组件 + hooks,无 class」规范的**唯一例外**,且例外点只有这一个)。
338
+ *
339
+ * 三件事:
340
+ * ① `getDerivedStateFromError` 先切失败态;真正的内容在 `componentDidCatch` 里由
341
+ * `buildChatEntrySafeRow` 现算 —— 它自己**不抛**(内部 try/catch + 只认文本与 JSON 短表示),
342
+ * 所以降级气泡一定有内容;连节点都读不出文本时给空字符串(仍然渲染出一条气泡行,不是「整条消失」)。
343
+ * ② `componentDidCatch` 打 ASCII `console.warn`(诊断用,绝不当作界面文案)。
344
+ * ③ 节点换了(重挂 / 分页)就清掉失败态(`componentDidUpdate`),不让一次崩溃永久钉死这条消息。
345
+ *
346
+ * 与宿主 ui-renderer 的 per-entry 边界的关系:那一层会在我们崩溃时把我们的登记 **abdicate**,
347
+ * 让 outlet 退回宿主渲染器(消息也能活);但它会先渲染一个 `<div data-slot-error>` 空壳,
348
+ * 所以本边界是「消息不消失」的第一道,宿主边界是最后一道。
349
+ */
350
+ export class ChatEntryErrorBoundary extends Component<ChatEntryBoundaryProps, ChatEntryBoundaryState> {
351
+ override state: ChatEntryBoundaryState = { failed: false, row: null }
352
+
353
+ static getDerivedStateFromError(): ChatEntryBoundaryState {
354
+ return { failed: true, row: null }
355
+ }
356
+
357
+ override componentDidCatch(error: unknown): void {
358
+ const row = buildChatEntrySafeRow({ node: this.props.node, project: buildChatEntryView })
359
+ console.warn(CHAT_ENTRY_WARN_PREFIX + (error instanceof Error ? error.message : String(error)))
360
+ this.setState({ row })
361
+ }
362
+
363
+ override componentDidUpdate(previous: ChatEntryBoundaryProps): void {
364
+ if (previous.node !== this.props.node && this.state.failed) this.setState({ failed: false, row: null })
365
+ }
366
+
367
+ override render(): ReactNode {
368
+ if (!this.state.failed) return this.props.children
369
+ const text = this.state.row === null ? '' : this.state.row.text
370
+ const names = this.state.row === null ? [] : this.state.row.fileNames
371
+ return (
372
+ <div
373
+ className={`${css.chatEntryScope} ${css.chatEntryRow} ${css.chatBubble} ${css.chatFallbackBubble}`}
374
+ data-chat-entry-fallback=""
375
+ >
376
+ <span className={css.chatBubbleText}>{text}</span>
377
+ {names.length > 0 && <span className={css.chatFileMeta}>{names.join(' / ')}</span>}
378
+ </div>
379
+ )
380
+ }
381
+ }
382
+
383
+ /**
384
+ * 注册到 `conversation.chat.node` 的 `user` cell(priority -1,遮蔽宿主渲染器)。
385
+ *
386
+ * 已核实:**steering 是另一个 key**(宿主对 user 与 steering 各注册了一次同一个 UserMessageNodeView,
387
+ * 但那是两条独立的 keyed cell);本入口只注册 `user`,steering 一律保持宿主的渲染器。
388
+ */
389
+ export function ChatEntryNodeView(props: ChatEntryViewProps) {
390
+ return createElement(ChatEntryErrorBoundary, { node: props.node, t: props.t }, createElement(ChatEntryBody, props))
391
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * DSH 右侧栏「回滚差异」标签页的正文(只读;内容 = 该回退点里一个记录在案文件的前后对照)。
3
+ *
4
+ * 目标来自标签页自己的**资源地址**(`dsh-resource://cm-rollback-diff/<pointId>/<path>`)——
5
+ * 地址由 seat 通过 `useTabInfo` 给出,所以同一个文件开两个标签、或从历史布局恢复标签都能各自取到
6
+ * 自己的目标;**拿不到地址**时如实说「读不出来」,绝不用全局变量猜一个文件给用户看。
7
+ *
8
+ * 地址解析与打开动作在 `rollback-diff-tab.ts`(纯函数、node 单测);本文件只做取数与装配。
9
+ */
10
+ import { useEffect, useState } from 'react'
11
+ import type { TranslateNS } from '../client-types.ts'
12
+ import type { RollbackFileDiffResult, RollbackPointPort } from '../../ui/rollback-view.ts'
13
+ import { Empty } from '../common/ui.tsx'
14
+ import { parseRollbackDiffAddress } from './rollback-diff-tab.ts'
15
+ import { RollbackDiffPane, type RollbackDiffState } from '../recovery/RollbackDiffPane.tsx'
16
+
17
+ /** 装配点注入面(端口的读差异方法 + recovery 字典)。 */
18
+ export interface RollbackDiffTabInjected {
19
+ port: RollbackPointPort
20
+ rt: TranslateNS<'config-manager-recovery'>
21
+ }
22
+
23
+ /** 完整 props:注入面 + 侧栏 seat 提供的 tab 信息钩子(可能不存在)。 */
24
+ export type RollbackDiffTabProps = RollbackDiffTabInjected & {
25
+ useTabInfo?: () => { tab?: { contentId?: unknown } }
26
+ }
27
+
28
+ /** 从 seat 的 tab 信息钩子里读资源地址(钩子缺失 / 抛错一律当「读不到」,不猜)。 */
29
+ function readContentId(useTabInfo: RollbackDiffTabProps['useTabInfo']): unknown {
30
+ if (typeof useTabInfo !== 'function') return undefined
31
+ try {
32
+ return useTabInfo()?.tab?.contentId
33
+ } catch {
34
+ return undefined
35
+ }
36
+ }
37
+
38
+ export function RollbackDiffTab({ port, rt, useTabInfo }: RollbackDiffTabProps) {
39
+ const contentId = readContentId(useTabInfo)
40
+ const target = parseRollbackDiffAddress(contentId)
41
+ const [state, setState] = useState<RollbackDiffState>({ status: 'loading' })
42
+ useEffect(() => {
43
+ if (target === null) return
44
+ const read = port.rollbackPointFileDiff
45
+ if (typeof read !== 'function') {
46
+ // 端口没实现这条能力(旧宿主):如实说读不出来,绝不显示空白装成「没有差异」
47
+ setState({ status: 'error', message: null })
48
+ return
49
+ }
50
+ let alive = true
51
+ setState({ status: 'loading' })
52
+ const pending: Promise<RollbackFileDiffResult> = read.call(port, target.pointId, target.path)
53
+ pending.then(
54
+ (diff) => { if (alive) setState({ status: 'ready', diff }) },
55
+ (error: unknown) => { if (alive) setState({ status: 'error', message: error instanceof Error ? error.message : String(error) }) },
56
+ )
57
+ return () => { alive = false }
58
+ }, [port, contentId])
59
+ if (target === null) return <Empty>{rt('rollback.diff.failed')}</Empty>
60
+ return <RollbackDiffPane state={state} rt={rt} />
61
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * 文件类型图标 —— 「DSH 侧边栏怎么画,就怎么画」(用户 2026-10-12 第 7 条)。
3
+ *
4
+ * 两级来源,优先级从高到低:
5
+ * ① **dsh-better-sidebar**(装了才用):它把文件图标注册表作为 cordis 服务 `betterSidebar` 发布
6
+ * (`ctx.betterSidebar.fileIcon(path, size)`;v0.19.0 起还有 `registerFileIcon` / `fileIcons`),
7
+ * 并且它自己的缺省档**就是** DSH 的 `FileTypeIcon` 画法 —— 装了它就等于同时拿到用户的扩展图标。
8
+ * 装配点在 src/client/index.ts 的 `fileIconFor`(惰取 + bind,形态不符返回 null);
9
+ * ② **DSH 自带**:`@deepseek-ai/dsh-client-ui-primitives` 的 `FileTypeIcon` —— 这正是 DSH 左侧文件树
10
+ * (`dsh-client-ui-sidebar-files` 的 FilesBody)逐字使用的那颗组件(同一份按类型着色的画集)。
11
+ *
12
+ * ## 为什么这里是「惰取 + try/catch」而不是顶层 import(**关键**)
13
+ * 该包由 DSH shell 的模块表作为 **static module seed** 预置(`dsh-web-frontend` 的 `staticModules`
14
+ * 里就有它),插件 bundle 的 `require` 能直接命中 —— 第三方插件 dsh-better-sidebar 走的就是这条路。
15
+ * 但那是**较新宿主**才有的模块表:老宿主加载本插件时 `require` 会抛(missed the module table)。
16
+ * 顶层 `import` 会被打包器改写成**模块体顶层**的 `require`,那一刻抛错会让**整个浏览器半加载失败**
17
+ * (设置页一起没)—— 为了「一颗图标」赔掉整插件,绝不。所以这里:
18
+ * · 只在**真要画 icon 时**才 require(帧内),
19
+ * · 拿不到 / 形态不符 / 抛错一律回落一颗**极简的本地 SVG 文件图标**(`fallbackFileGlyph`),
20
+ * 既不打挂任何东西,也不空着。
21
+ * 该包因此不进 bundle:源码里只有 `require` 调用(bundle 白名单在 src/utils/bundle-scan.ts 登记)。
22
+ */
23
+ import type { ComponentType, ReactNode } from 'react'
24
+ import { Icon } from '../common/Icon.tsx'
25
+
26
+ /** 装配点注入的「外部图标来源」(dsh-better-sidebar 的 `fileIcon`);返回 null = 它不认这一条。 */
27
+ export type RollbackFileIconRenderer = (path: string) => ReactNode | null
28
+
29
+ /** `FileTypeIcon` 我们真的用到的入参面(只声明消费的字段,不复制第三方类型)。 */
30
+ interface DshFileTypeIconProps {
31
+ path: string
32
+ size?: number
33
+ className?: string
34
+ }
35
+
36
+ /** 惰取结果的三态缓存(undefined = 还没试过;null = 试过了,这台宿主没有)。 */
37
+ let cachedDshFileTypeIcon: ComponentType<DshFileTypeIconProps> | null | undefined
38
+
39
+ /**
40
+ * 惰取 DSH 自带的 `FileTypeIcon`(**每次进程只试一次**,失败就记 null 不再重试)。
41
+ *
42
+ * 为什么不用 `import()`:动态 import 在 cjs 产物里同样要走模块表,且会把整个包拖进 chunk;
43
+ * 这里的 `require` 是 DSH loader 交给插件工厂的那一个(banner 里 `factory: (require) => {...}`)。
44
+ */
45
+ function dshFileTypeIcon(): ComponentType<DshFileTypeIconProps> | null {
46
+ if (cachedDshFileTypeIcon !== undefined) return cachedDshFileTypeIcon
47
+ cachedDshFileTypeIcon = null
48
+ try {
49
+ const loaded = (typeof require === 'function' ? require('@deepseek-ai/dsh-client-ui-primitives') : null) as
50
+ | { FileTypeIcon?: unknown }
51
+ | null
52
+ const icon = loaded?.FileTypeIcon
53
+ if (typeof icon === 'function' || typeof icon === 'object') {
54
+ cachedDshFileTypeIcon = icon as ComponentType<DshFileTypeIconProps>
55
+ }
56
+ } catch {
57
+ cachedDshFileTypeIcon = null
58
+ }
59
+ return cachedDshFileTypeIcon
60
+ }
61
+
62
+ /**
63
+ * 最后的兜底:共享图标原语的通用「文件」图标(`common/Icon.tsx` 的 `name="file"`)。
64
+ * 只有「没有 better-sidebar + 宿主模块表里没有 ui-primitives」两条同时成立时才会出现 ——
65
+ * 那时宁可用一颗通用文件图标,也不空着、更不打挂这一行。
66
+ * 走共享原语而不是就地写 `<svg>`:DESIGN.md §「图标」把 `common/Icon.tsx` 定为唯一出口。
67
+ */
68
+ function fallbackFileGlyph(size: number): ReactNode {
69
+ return <Icon name="file" size={size} />
70
+ }
71
+
72
+ export interface RollbackFileIconProps {
73
+ /** 文件绝对路径(图标按扩展名/文件名分类;路径越完整越准)。 */
74
+ path: string
75
+ /** 边长(px);缺省 14 = DSH 文件树行内图标尺寸。 */
76
+ size?: number
77
+ /** 外部来源(可选):返回 null / 抛错都回落 DSH 自带画法。 */
78
+ render?: RollbackFileIconRenderer
79
+ }
80
+
81
+ /** 一颗装饰性文件类型图标(可访问名由调用方负责:行内文件名的文字就是它的名字)。 */
82
+ export function RollbackFileIcon({ path, size = 14, render }: RollbackFileIconProps) {
83
+ if (render !== undefined) {
84
+ let node: ReactNode | null = null
85
+ try {
86
+ node = render(path)
87
+ } catch {
88
+ node = null
89
+ }
90
+ // null / undefined / false = 这一条外部来源接不住 ⇒ 回落 DSH 自带画法(不猜、不空着)
91
+ if (node !== null && node !== undefined && node !== false) return <>{node}</>
92
+ }
93
+ const Dsh = dshFileTypeIcon()
94
+ if (Dsh !== null) return <Dsh path={path} size={size} />
95
+ return fallbackFileGlyph(size)
96
+ }