@narumitw/pi-sync 0.51.0 → 0.52.0

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 (181) hide show
  1. package/README.md +30 -5
  2. package/dist/chunks/{chunk-7KHKWL3Q.ts → chunk-2CBF6SMT.ts} +8 -7
  3. package/dist/chunks/chunk-2CBF6SMT.ts.map +7 -0
  4. package/dist/chunks/{chunk-YFADF5ZQ.ts → chunk-2DI6PDZV.ts} +5 -1
  5. package/dist/chunks/chunk-2DI6PDZV.ts.map +7 -0
  6. package/dist/chunks/chunk-2GUXI2XT.ts +1702 -0
  7. package/dist/chunks/chunk-2GUXI2XT.ts.map +7 -0
  8. package/dist/chunks/{chunk-4MPIH46N.ts → chunk-3B4Z2SKV.ts} +40 -6
  9. package/dist/chunks/chunk-3B4Z2SKV.ts.map +7 -0
  10. package/dist/chunks/{chunk-YPXGU5OR.ts → chunk-4QVPTIZB.ts} +16 -6
  11. package/dist/chunks/{chunk-YPXGU5OR.ts.map → chunk-4QVPTIZB.ts.map} +2 -2
  12. package/dist/chunks/{chunk-UA7RXOIT.ts → chunk-4SBTAVFL.ts} +6 -6
  13. package/dist/chunks/{chunk-6546TS54.ts → chunk-5RZPINUQ.ts} +7 -4
  14. package/dist/chunks/{chunk-6546TS54.ts.map → chunk-5RZPINUQ.ts.map} +2 -2
  15. package/dist/chunks/chunk-76NB4R7U.ts +107 -0
  16. package/dist/chunks/chunk-76NB4R7U.ts.map +7 -0
  17. package/dist/chunks/{chunk-DM4X4FIA.ts → chunk-7D6RDCVC.ts} +8 -10
  18. package/dist/chunks/{chunk-DM4X4FIA.ts.map → chunk-7D6RDCVC.ts.map} +2 -2
  19. package/dist/chunks/chunk-A6HU4ERM.ts +124 -0
  20. package/dist/chunks/chunk-A6HU4ERM.ts.map +7 -0
  21. package/dist/chunks/chunk-AMJRUXFT.ts +17 -0
  22. package/dist/chunks/chunk-AMJRUXFT.ts.map +7 -0
  23. package/dist/chunks/chunk-ANWNQIR7.ts +1013 -0
  24. package/dist/chunks/chunk-ANWNQIR7.ts.map +7 -0
  25. package/dist/chunks/{chunk-T56RNRZJ.ts → chunk-BDB4IDM3.ts} +19 -10
  26. package/dist/chunks/chunk-BDB4IDM3.ts.map +7 -0
  27. package/dist/chunks/{chunk-2RZSQWBH.ts → chunk-CHSIAC4N.ts} +5 -5
  28. package/dist/chunks/{chunk-2RZSQWBH.ts.map → chunk-CHSIAC4N.ts.map} +2 -2
  29. package/dist/chunks/chunk-HUZ5CR4W.ts +110 -0
  30. package/dist/chunks/chunk-HUZ5CR4W.ts.map +7 -0
  31. package/dist/chunks/{chunk-QLSN7AGX.ts → chunk-IJ6VV6FN.ts} +81 -19
  32. package/dist/chunks/chunk-IJ6VV6FN.ts.map +7 -0
  33. package/dist/chunks/chunk-ORQRHEEO.ts +362 -0
  34. package/dist/chunks/chunk-ORQRHEEO.ts.map +7 -0
  35. package/dist/chunks/chunk-QE7NZZWT.ts +380 -0
  36. package/dist/chunks/chunk-QE7NZZWT.ts.map +7 -0
  37. package/dist/chunks/{chunk-HGJH56KR.ts → chunk-QHSQCK7L.ts} +4 -4
  38. package/dist/chunks/chunk-QOFMXPM5.ts +735 -0
  39. package/dist/chunks/chunk-QOFMXPM5.ts.map +7 -0
  40. package/dist/chunks/{chunk-DZUSTGQV.ts → chunk-UQ3ALBIB.ts} +4 -4
  41. package/dist/chunks/{chunk-Q32CQWPM.ts → chunk-V6VZ5TLG.ts} +13 -6
  42. package/dist/chunks/chunk-V6VZ5TLG.ts.map +7 -0
  43. package/dist/chunks/{chunk-E42GXD6D.ts → chunk-WKPX2SIC.ts} +4 -4
  44. package/dist/chunks/chunk-ZW5ZLGBI.ts +154 -0
  45. package/dist/chunks/chunk-ZW5ZLGBI.ts.map +7 -0
  46. package/dist/chunks/{chunk-6NT3YFGY.ts → chunk-ZX2Q6THB.ts} +8 -3
  47. package/dist/chunks/{chunk-6NT3YFGY.ts.map → chunk-ZX2Q6THB.ts.map} +2 -2
  48. package/dist/chunks/conflict-artifacts-BB2FZMIE.ts +27 -0
  49. package/dist/chunks/conflict-review-T3PB7ZSD.ts +175 -0
  50. package/dist/chunks/conflict-review-T3PB7ZSD.ts.map +7 -0
  51. package/dist/chunks/{file-selection-RAVHRYFG.ts → file-selection-WZX7HAY2.ts} +8 -7
  52. package/dist/chunks/{file-selection-RAVHRYFG.ts.map → file-selection-WZX7HAY2.ts.map} +1 -1
  53. package/dist/chunks/{git-backend-IAANDL3K.ts → git-backend-HLGKUQJT.ts} +19 -7
  54. package/dist/chunks/git-backend-HLGKUQJT.ts.map +7 -0
  55. package/dist/chunks/local-fields-6DGYOOIK.ts +22 -0
  56. package/dist/chunks/manager-result-dispatcher-VIDT3QBU.ts +23 -0
  57. package/dist/chunks/{manager-ui-763NEKJY.ts → manager-ui-HSMHS3OK.ts} +236 -57
  58. package/dist/chunks/manager-ui-HSMHS3OK.ts.map +7 -0
  59. package/dist/chunks/merge-baseline-store-EVQAAML6.ts +22 -0
  60. package/dist/chunks/remote-selection-ui-SMF5CLFO.ts +22 -0
  61. package/dist/chunks/{s3-backend-XGXUOPUN.ts → s3-backend-DJ2NT3SZ.ts} +16 -9
  62. package/dist/chunks/s3-backend-DJ2NT3SZ.ts.map +7 -0
  63. package/dist/chunks/{setup-switch-5G2VNJHL.ts → setup-switch-KTUWL6DE.ts} +9 -6
  64. package/dist/chunks/{setup-wizard-YIEWGX32.ts → setup-wizard-GSDPDFC4.ts} +10 -7
  65. package/dist/chunks/{setup-wizard-YIEWGX32.ts.map → setup-wizard-GSDPDFC4.ts.map} +1 -1
  66. package/dist/chunks/{snapshot-JL5DFSXI.ts → snapshot-THS5EY2L.ts} +6 -9
  67. package/dist/chunks/snapshot-THS5EY2L.ts.map +7 -0
  68. package/dist/chunks/{sync-inspection-P3ENJLNF.ts → sync-inspection-VBJF2FWC.ts} +10 -10
  69. package/dist/chunks/sync-inspection-VBJF2FWC.ts.map +7 -0
  70. package/dist/chunks/{sync-operations-AR76EB6C.ts → sync-operations-27VQUDH2.ts} +309 -508
  71. package/dist/chunks/sync-operations-27VQUDH2.ts.map +7 -0
  72. package/dist/chunks/{sync-state-VHJUZ4YG.ts → sync-state-CFL4RRRB.ts} +6 -6
  73. package/dist/chunks/sync-state-CFL4RRRB.ts.map +7 -0
  74. package/dist/chunks/{webdav-backend-3EGUONAT.ts → webdav-backend-ETQGSI6I.ts} +13 -7
  75. package/dist/chunks/webdav-backend-ETQGSI6I.ts.map +7 -0
  76. package/dist/index.ts +142 -43
  77. package/dist/index.ts.map +2 -2
  78. package/docs/content-merge-audit.md +156 -0
  79. package/docs/merge-implementation-audit.md +312 -0
  80. package/docs/settings-merge-audit.md +153 -0
  81. package/docs/settings.md +36 -0
  82. package/package.json +6 -6
  83. package/src/backends/git/git-backend.ts +2 -0
  84. package/src/backends/git/git-storage.ts +8 -3
  85. package/src/backends/s3/s3-backend.ts +2 -1
  86. package/src/backends/webdav/webdav-backend.ts +2 -1
  87. package/src/commands/command-execution.ts +10 -0
  88. package/src/commands/command.ts +4 -0
  89. package/src/settings/config.ts +12 -0
  90. package/src/settings/settings-management.ts +2 -2
  91. package/src/settings/settings-types.ts +14 -1
  92. package/src/settings/settings-validation.ts +24 -3
  93. package/src/snapshot/session-paths.ts +34 -7
  94. package/src/snapshot/snapshot-apply.ts +81 -32
  95. package/src/snapshot/snapshot-case-replacement.ts +80 -0
  96. package/src/snapshot/snapshot-codec.ts +6 -1
  97. package/src/snapshot/snapshot-root-transition.ts +154 -0
  98. package/src/snapshot/snapshot-transaction-plan.ts +72 -0
  99. package/src/snapshot/snapshot-transaction.ts +844 -102
  100. package/src/snapshot/snapshot-types.ts +5 -0
  101. package/src/state/json-file.ts +32 -5
  102. package/src/state/merge-baseline-store.ts +182 -0
  103. package/src/state/mutation-directory-sync.ts +34 -0
  104. package/src/state/state-types.ts +4 -0
  105. package/src/state/sync-state-store.ts +14 -1
  106. package/src/sync/automatic-sync.ts +11 -3
  107. package/src/sync/conflict-artifact-ownership.ts +63 -0
  108. package/src/sync/conflict-artifacts.ts +195 -0
  109. package/src/sync/conflict-groups.ts +68 -0
  110. package/src/sync/conflict-preview.ts +56 -0
  111. package/src/sync/conflict-resolution.ts +93 -0
  112. package/src/sync/conflict-retention.ts +72 -0
  113. package/src/sync/conflict-review.ts +95 -0
  114. package/src/sync/content-conflicts.ts +73 -0
  115. package/src/sync/field-migration.ts +39 -0
  116. package/src/sync/file-merge-planner.ts +143 -0
  117. package/src/sync/json-document.ts +184 -0
  118. package/src/sync/local-fields.ts +134 -0
  119. package/src/sync/merge-apply.ts +380 -0
  120. package/src/sync/merge-journal.ts +219 -0
  121. package/src/sync/merged-sync.ts +723 -0
  122. package/src/sync/partial-progress.ts +54 -0
  123. package/src/sync/remote-snapshot.ts +6 -0
  124. package/src/sync/session-merge.ts +194 -0
  125. package/src/sync/settings-conflicts.ts +52 -0
  126. package/src/sync/settings-merge.ts +57 -0
  127. package/src/sync/startup-check.ts +90 -5
  128. package/src/sync/sync-inspection.ts +1 -0
  129. package/src/sync/sync-local.ts +61 -0
  130. package/src/sync/sync-mutations.ts +256 -114
  131. package/src/sync/sync-queries.ts +6 -2
  132. package/src/sync/sync-state.ts +14 -5
  133. package/src/sync/text-merge.ts +133 -0
  134. package/src/sync-extension.ts +8 -0
  135. package/src/ui/manager-state.ts +12 -1
  136. package/src/ui/manager-ui.ts +10 -0
  137. package/src/ui/merge-review.ts +28 -0
  138. package/src/ui/remote-selection-ui.ts +1 -0
  139. package/src/ui/settings-ui.ts +183 -2
  140. package/src/ui/setup/setup-actions.ts +2 -2
  141. package/src/ui/setup/setup-switcher.ts +2 -1
  142. package/src/ui/storage-connections-ui.ts +11 -7
  143. package/src/ui/sync-attention.ts +3 -0
  144. package/src/ui/sync-format.ts +16 -12
  145. package/src/ui/sync-setups-ui.ts +1 -0
  146. package/src/ui/terminal-text.ts +9 -2
  147. package/dist/chunks/chunk-4AIHXRKE.ts +0 -197
  148. package/dist/chunks/chunk-4AIHXRKE.ts.map +0 -7
  149. package/dist/chunks/chunk-4MPIH46N.ts.map +0 -7
  150. package/dist/chunks/chunk-7KHKWL3Q.ts.map +0 -7
  151. package/dist/chunks/chunk-EEJPYTWE.ts +0 -348
  152. package/dist/chunks/chunk-EEJPYTWE.ts.map +0 -7
  153. package/dist/chunks/chunk-IVXEDPAD.ts +0 -370
  154. package/dist/chunks/chunk-IVXEDPAD.ts.map +0 -7
  155. package/dist/chunks/chunk-MSSW6F3R.ts +0 -12
  156. package/dist/chunks/chunk-MSSW6F3R.ts.map +0 -7
  157. package/dist/chunks/chunk-OVDTF5CH.ts +0 -90
  158. package/dist/chunks/chunk-OVDTF5CH.ts.map +0 -7
  159. package/dist/chunks/chunk-Q32CQWPM.ts.map +0 -7
  160. package/dist/chunks/chunk-QLSN7AGX.ts.map +0 -7
  161. package/dist/chunks/chunk-SWUXA3MH.ts +0 -18
  162. package/dist/chunks/chunk-SWUXA3MH.ts.map +0 -7
  163. package/dist/chunks/chunk-T56RNRZJ.ts.map +0 -7
  164. package/dist/chunks/chunk-YFADF5ZQ.ts.map +0 -7
  165. package/dist/chunks/git-backend-IAANDL3K.ts.map +0 -7
  166. package/dist/chunks/manager-result-dispatcher-OTUMQM26.ts +0 -23
  167. package/dist/chunks/manager-ui-763NEKJY.ts.map +0 -7
  168. package/dist/chunks/remote-selection-ui-TZTJF32Y.ts +0 -22
  169. package/dist/chunks/s3-backend-XGXUOPUN.ts.map +0 -7
  170. package/dist/chunks/sync-operations-AR76EB6C.ts.map +0 -7
  171. package/dist/chunks/webdav-backend-3EGUONAT.ts.map +0 -7
  172. /package/dist/chunks/{chunk-UA7RXOIT.ts.map → chunk-4SBTAVFL.ts.map} +0 -0
  173. /package/dist/chunks/{chunk-HGJH56KR.ts.map → chunk-QHSQCK7L.ts.map} +0 -0
  174. /package/dist/chunks/{chunk-DZUSTGQV.ts.map → chunk-UQ3ALBIB.ts.map} +0 -0
  175. /package/dist/chunks/{chunk-E42GXD6D.ts.map → chunk-WKPX2SIC.ts.map} +0 -0
  176. /package/dist/chunks/{manager-result-dispatcher-OTUMQM26.ts.map → conflict-artifacts-BB2FZMIE.ts.map} +0 -0
  177. /package/dist/chunks/{remote-selection-ui-TZTJF32Y.ts.map → local-fields-6DGYOOIK.ts.map} +0 -0
  178. /package/dist/chunks/{setup-switch-5G2VNJHL.ts.map → manager-result-dispatcher-VIDT3QBU.ts.map} +0 -0
  179. /package/dist/chunks/{snapshot-JL5DFSXI.ts.map → merge-baseline-store-EVQAAML6.ts.map} +0 -0
  180. /package/dist/chunks/{sync-inspection-P3ENJLNF.ts.map → remote-selection-ui-SMF5CLFO.ts.map} +0 -0
  181. /package/dist/chunks/{sync-state-VHJUZ4YG.ts.map → setup-switch-KTUWL6DE.ts.map} +0 -0
@@ -0,0 +1,23 @@
1
+ // @generated by scripts/build-runtime.mjs; do not edit.
2
+ // @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
3
+ import {
4
+ dispatchManagerResult
5
+ } from "./chunk-WKPX2SIC.ts";
6
+ import "./chunk-2CBF6SMT.ts";
7
+ import "./chunk-V6VZ5TLG.ts";
8
+ import "./chunk-QHSQCK7L.ts";
9
+ import "./chunk-K3RIEL6J.ts";
10
+ import "./chunk-AMJRUXFT.ts";
11
+ import "./chunk-CHSIAC4N.ts";
12
+ import "./chunk-3B4Z2SKV.ts";
13
+ import "./chunk-2JZFHNLM.ts";
14
+ import "./chunk-T2SJD3N7.ts";
15
+ import "./chunk-7D5SF7P3.ts";
16
+ import "./chunk-A6HU4ERM.ts";
17
+ import "./chunk-QOFMXPM5.ts";
18
+ import "./chunk-ZW5ZLGBI.ts";
19
+ import "./chunk-HUZ5CR4W.ts";
20
+ export {
21
+ dispatchManagerResult
22
+ };
23
+ //# sourceMappingURL=manager-result-dispatcher-VIDT3QBU.ts.map
@@ -1,37 +1,5 @@
1
1
  // @generated by scripts/build-runtime.mjs; do not edit.
2
2
  // @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
3
- import {
4
- dispatchManagerResult
5
- } from "./chunk-E42GXD6D.ts";
6
- import {
7
- runCancellableOperation
8
- } from "./chunk-7KHKWL3Q.ts";
9
- import {
10
- SETUP_SWITCH_ACTION_OPTIONS,
11
- saveOnSwitch,
12
- setupSwitchActionFromLabel,
13
- setupSwitchActionLabel,
14
- useSyncSetup
15
- } from "./chunk-DZUSTGQV.ts";
16
- import "./chunk-Q32CQWPM.ts";
17
- import "./chunk-HGJH56KR.ts";
18
- import {
19
- observationMatchesConfig,
20
- observationSummary,
21
- syncAttentionMatchesConfig
22
- } from "./chunk-6546TS54.ts";
23
- import {
24
- inspectLock,
25
- isLockGuardHeld,
26
- isStaleLock
27
- } from "./chunk-QLSN7AGX.ts";
28
- import {
29
- configureSyncStatus
30
- } from "./chunk-K3RIEL6J.ts";
31
- import {
32
- readStateForConfig,
33
- syncStateFingerprint
34
- } from "./chunk-YPXGU5OR.ts";
35
3
  import {
36
4
  AUTOMATIC_SYNC_DESCRIPTION,
37
5
  applyS3CredentialUpdate,
@@ -52,21 +20,56 @@ import {
52
20
  showEditS3Target,
53
21
  showEditWebDavStorageProfile,
54
22
  showEditWebDavTarget
55
- } from "./chunk-UA7RXOIT.ts";
23
+ } from "./chunk-4SBTAVFL.ts";
24
+ import {
25
+ dispatchManagerResult
26
+ } from "./chunk-WKPX2SIC.ts";
27
+ import {
28
+ runCancellableOperation
29
+ } from "./chunk-2CBF6SMT.ts";
30
+ import "./chunk-V6VZ5TLG.ts";
31
+ import "./chunk-QHSQCK7L.ts";
32
+ import {
33
+ SETUP_SWITCH_ACTION_OPTIONS,
34
+ saveOnSwitch,
35
+ setupSwitchActionFromLabel,
36
+ setupSwitchActionLabel,
37
+ useSyncSetup
38
+ } from "./chunk-UQ3ALBIB.ts";
39
+ import {
40
+ observationMatchesConfig,
41
+ observationSummary,
42
+ syncAttentionMatchesConfig
43
+ } from "./chunk-5RZPINUQ.ts";
44
+ import {
45
+ captureMutationOwner,
46
+ inspectLock,
47
+ isLockGuardHeld,
48
+ isStaleLock
49
+ } from "./chunk-IJ6VV6FN.ts";
50
+ import "./chunk-PWMWOJP4.ts";
56
51
  import {
57
52
  automaticSyncSummary
58
53
  } from "./chunk-ECYCNZOE.ts";
59
- import "./chunk-YFADF5ZQ.ts";
54
+ import {
55
+ configureSyncStatus
56
+ } from "./chunk-K3RIEL6J.ts";
57
+ import "./chunk-2DI6PDZV.ts";
60
58
  import {
61
59
  safeTerminalText
62
- } from "./chunk-MSSW6F3R.ts";
60
+ } from "./chunk-AMJRUXFT.ts";
61
+ import {
62
+ readStateForConfig,
63
+ syncStateFingerprint
64
+ } from "./chunk-4QVPTIZB.ts";
65
+ import "./chunk-BVVP33U5.ts";
63
66
  import {
64
67
  addStorageConnection,
65
68
  removeStorageConnection,
66
69
  removeSyncSetup,
67
70
  updateStorageConnection,
68
71
  updateSyncSetup
69
- } from "./chunk-2RZSQWBH.ts";
72
+ } from "./chunk-CHSIAC4N.ts";
70
73
  import {
71
74
  activeLocalConfigPath,
72
75
  isCloudflareR2Endpoint,
@@ -79,23 +82,26 @@ import {
79
82
  readLocalConfigObject,
80
83
  syncConfigReviewIdentity,
81
84
  updateLocalConfig
82
- } from "./chunk-4MPIH46N.ts";
85
+ } from "./chunk-3B4Z2SKV.ts";
83
86
  import "./chunk-2JZFHNLM.ts";
84
- import "./chunk-BVVP33U5.ts";
85
87
  import "./chunk-T2SJD3N7.ts";
88
+ import "./chunk-ZX2Q6THB.ts";
86
89
  import {
87
90
  syncErrorGuidance
88
91
  } from "./chunk-G4RUXRWZ.ts";
89
92
  import {
90
93
  errorMessage
91
94
  } from "./chunk-7D5SF7P3.ts";
92
- import "./chunk-EEJPYTWE.ts";
93
- import "./chunk-SWUXA3MH.ts";
94
- import "./chunk-OVDTF5CH.ts";
95
+ import {
96
+ normalizeLocalFields,
97
+ sameLocalFields
98
+ } from "./chunk-A6HU4ERM.ts";
95
99
  import {
96
100
  compareSyncInclude,
97
101
  syncIncludeSelection
98
- } from "./chunk-IVXEDPAD.ts";
102
+ } from "./chunk-QOFMXPM5.ts";
103
+ import "./chunk-ZW5ZLGBI.ts";
104
+ import "./chunk-HUZ5CR4W.ts";
99
105
 
100
106
  // src/ui/manager-ui.ts
101
107
  import { defineMenu as defineMenu5, runMenu as runMenu5 } from "@narumitw/pi-tui-kit";
@@ -166,7 +172,7 @@ function blockedSyncMenuItem(label, manager) {
166
172
  };
167
173
  }
168
174
  async function showManagerAttention(ctx, attention, runRoute, signal, onSelectionResolved) {
169
- const { showRemoteSelectionReview } = await import("./remote-selection-ui-TZTJF32Y.ts");
175
+ const { showRemoteSelectionReview } = await import("./remote-selection-ui-SMF5CLFO.ts");
170
176
  if (signal?.aborted) return "close";
171
177
  const review = await showRemoteSelectionReview(ctx, attention.decision.setupName, signal, void 0, {
172
178
  decision: attention.decision,
@@ -388,6 +394,7 @@ async function loadSetupMenuState(selectedName, signal) {
388
394
  `Included content: ${selection.builtIns.length} built-in groups \xB7 ${selection.custom.length} extra paths`,
389
395
  `Sessions: ${selection.sessions ? "On \u2014 privacy-sensitive" : "Off"}`,
390
396
  `Automatic sync: ${config.automatic ? "On" : "Off"}`,
397
+ `Automatic transfer at startup: ${config.automaticTransfer ? "On \u2014 may upload, replace, or delete selected files; no reload" : "Off"}`,
391
398
  isCurrent ? "Edit included content and automatic sync in /sync \u2192 Settings." : "To edit included content or automatic sync, make this setup current, then open /sync \u2192 Settings."
392
399
  ];
393
400
  } catch (error) {
@@ -474,7 +481,7 @@ async function describeManagerState(signal, attention, inspectOperation = inspec
474
481
  };
475
482
  }
476
483
  const configuredTargets = ownRecord(raw.syncSetups);
477
- if (raw.version === 3 && configuredTargets && Object.keys(configuredTargets).length === 0) {
484
+ if ((raw.version === 3 || raw.version === 4 || raw.version === 5) && configuredTargets && Object.keys(configuredTargets).length === 0) {
478
485
  return {
479
486
  title: [
480
487
  "Manage sync",
@@ -516,7 +523,12 @@ async function describeManagerState(signal, attention, inspectOperation = inspec
516
523
  `Storage: ${backendStorageDescription(config)}`,
517
524
  `Included: ${selection.builtIns.length} built-in group${selection.builtIns.length === 1 ? "" : "s"} \xB7 ${selection.custom.length} extra path${selection.custom.length === 1 ? "" : "s"} \xB7 Sessions ${selection.sessions ? "on" : "off"}`,
518
525
  `Automatic sync: ${automaticSyncSummary(config.automatic)}`,
526
+ `Automatic transfer at startup: ${config.automaticTransfer ? "On \u2014 upload/replace/delete at idle; no reload" : "Off"}`,
519
527
  `Last applied: ${lastAppliedSnapshot}`,
528
+ ...syncState?.unresolved?.length ? [
529
+ `Unresolved: ${syncState.unresolved.length} dependency groups \xB7 review History & recovery`,
530
+ `Last observed: ${safeTerminalText(syncState.lastObservedSnapshot ?? "unknown")}`
531
+ ] : [],
520
532
  ...currentAttention ? [
521
533
  currentAttention.decision.setupName === config.setupName ? "Sync status: Review needed" : `Sync status: Review needed for setup ${safeTerminalText(currentAttention.decision.setupName)}`,
522
534
  attentionComparison?.remoteOnly.length === 0 && attentionComparison.localOnly.length === 0 ? "Only the synced-content order differs." : `Remote-only paths: ${attentionComparison?.remoteOnly.length ?? 0} \xB7 Device-only paths: ${attentionComparison?.localOnly.length ?? 0}`,
@@ -663,11 +675,143 @@ async function showSyncSettings(ctx, runRoute, signal) {
663
675
  description: "Review this device and remote content lists before choosing either one.",
664
676
  currentValue: "Review",
665
677
  action: "remote-include"
678
+ },
679
+ {
680
+ id: "automaticTransfer",
681
+ label: "Automatic transfer at startup",
682
+ description: "May upload, replace, or delete selected files once at idle startup in TUI/RPC. Requires a baseline and conditional/lease publication; never reloads resources. Turning off cancels pending work.",
683
+ currentValue: state.automaticTransfer ? "On" : "Off",
684
+ values: ["On", "Off"],
685
+ action: "automatic-transfer"
686
+ },
687
+ {
688
+ id: "mergeSettings",
689
+ label: "Settings field merge (experimental)",
690
+ description: "Combine independent global settings.json fields using a verified private ancestor; arrays and nested objects remain atomic. No reload.",
691
+ currentValue: state.mergeSettings ? "On" : "Off",
692
+ values: ["On", "Off"],
693
+ action: "merge-settings"
694
+ },
695
+ {
696
+ id: "localFields",
697
+ label: "Machine-local settings fields",
698
+ description: "Root field names omitted from future portable snapshots. Policy changes require directional migration; old history remains.",
699
+ currentValue: `${state.localFields?.length ?? 0} fields \xB7 Edit`,
700
+ action: "local-fields"
701
+ },
702
+ {
703
+ id: "contentPolicy",
704
+ label: "Content / partial sync (experimental)",
705
+ description: "Version-5 opt-in: bounded text and prefix-only sessions; partial progress keeps full withheld versions and old baseline hashes.",
706
+ currentValue: state.mergeContent ? state.partialSync ? "Content & partial" : "Content only" : state.partialSync ? "Partial only" : "Off",
707
+ values: ["Off", "Content only", "Content & partial", "Partial only"],
708
+ action: "content-policy"
666
709
  }
667
710
  ]
668
711
  })
669
712
  },
670
713
  actions: {
714
+ "content-policy": async ({ value, signal: actionSignal }) => {
715
+ const mutationSignal = signal ? AbortSignal.any([signal, actionSignal]) : actionSignal;
716
+ const validate = captureMutationOwner(ctx, mutationSignal);
717
+ try {
718
+ const previous = await loadConfig(setupName);
719
+ validate();
720
+ const mergeContent = value === "Content only" || value === "Content & partial";
721
+ const partialSync = value === "Partial only" || value === "Content & partial";
722
+ if (!await ctx.ui.confirm(
723
+ "Save experimental content policy?",
724
+ "This explicitly upgrades settings to version 5; partial snapshots use version 3 and older clients must refuse them. Text/session merge is conservative. Partial sync preserves withheld versions and old baselines. Disabling does not clear unresolved groups or recovery evidence. Review a directional migration first if portable policy changes.",
725
+ { signal: mutationSignal }
726
+ ))
727
+ return { kind: "rejected" };
728
+ validate();
729
+ await updateLocalConfig((current) => {
730
+ validate();
731
+ const setup = current.syncSetups[setupName];
732
+ if (!setup || Boolean(setup.sync.mergeContent) !== Boolean(previous.mergeContent) || Boolean(setup.sync.partialSync) !== Boolean(previous.partialSync))
733
+ throw new Error("Content policy changed during review.");
734
+ return {
735
+ ...current,
736
+ version: 5,
737
+ syncSetups: {
738
+ ...current.syncSetups,
739
+ [setupName]: { ...setup, sync: { ...setup.sync, mergeContent, partialSync } }
740
+ }
741
+ };
742
+ }, mutationSignal);
743
+ validate();
744
+ return { kind: "stay" };
745
+ } catch (error) {
746
+ if (!mutationSignal.aborted) notifySaveFailure(ctx, error);
747
+ return { kind: "rejected" };
748
+ }
749
+ },
750
+ "merge-settings": async ({ value, signal: actionSignal }) => {
751
+ const mutationSignal = signal ? AbortSignal.any([signal, actionSignal]) : actionSignal;
752
+ const validate = captureMutationOwner(ctx, mutationSignal);
753
+ try {
754
+ validate();
755
+ await updateSyncSetup(
756
+ setupName,
757
+ (setup) => ({ ...setup, sync: { ...setup.sync, mergeSettings: value === "On" } }),
758
+ { signal: mutationSignal }
759
+ );
760
+ validate();
761
+ return { kind: "stay" };
762
+ } catch (error) {
763
+ if (!mutationSignal.aborted) notifySaveFailure(ctx, error);
764
+ return { kind: "rejected" };
765
+ }
766
+ },
767
+ "local-fields": async ({ signal: actionSignal }) => {
768
+ const mutationSignal = signal ? AbortSignal.any([signal, actionSignal]) : actionSignal;
769
+ const validate = captureMutationOwner(ctx, mutationSignal);
770
+ try {
771
+ const previous = await loadConfig(setupName);
772
+ validate();
773
+ const input = await ctx.ui.input(
774
+ "Machine-local settings.json root fields (JSON string array)",
775
+ JSON.stringify(previous.localFields ?? []),
776
+ { signal: mutationSignal }
777
+ );
778
+ validate();
779
+ if (input === void 0) return { kind: "rejected" };
780
+ let fields;
781
+ try {
782
+ fields = normalizeLocalFields(JSON.parse(input));
783
+ } catch {
784
+ throw new Error("Invalid localFields JSON array; no policy was changed.");
785
+ }
786
+ if (sameLocalFields(previous.localFields, fields)) return { kind: "stay" };
787
+ const confirmed = await ctx.ui.confirm(
788
+ "Save portable field policy?",
789
+ "This opts into settings/snapshot version 4/2; older clients must refuse them. Future snapshots omit these fields, but old remote history is NOT erased. Review an explicit force push/pull migration before further sync. Removed rules can expose or replace local-only values.",
790
+ { signal: mutationSignal }
791
+ );
792
+ validate();
793
+ if (!confirmed) return { kind: "rejected" };
794
+ await updateLocalConfig((current) => {
795
+ validate();
796
+ const setup = current.syncSetups[setupName];
797
+ if (!setup || !sameLocalFields(setup.sync.localFields, previous.localFields))
798
+ throw new Error("Field policy changed while under review; reopen settings.");
799
+ return {
800
+ ...current,
801
+ version: current.version === 5 ? 5 : 4,
802
+ syncSetups: {
803
+ ...current.syncSetups,
804
+ [setupName]: { ...setup, sync: { ...setup.sync, localFields: fields } }
805
+ }
806
+ };
807
+ }, mutationSignal);
808
+ validate();
809
+ return { kind: "stay" };
810
+ } catch (error) {
811
+ if (!mutationSignal.aborted) notifySaveFailure(ctx, error);
812
+ return { kind: "rejected" };
813
+ }
814
+ },
671
815
  automatic: async ({ value, signal: actionSignal }) => {
672
816
  const automatic = value === "On";
673
817
  const mutationSignal = signal ? AbortSignal.any([signal, actionSignal]) : actionSignal;
@@ -689,6 +833,24 @@ async function showSyncSettings(ctx, runRoute, signal) {
689
833
  return { kind: "rejected" };
690
834
  }
691
835
  },
836
+ "automatic-transfer": async ({ value, signal: actionSignal }) => {
837
+ const automaticTransfer = value === "On";
838
+ const mutationSignal = signal ? AbortSignal.any([signal, actionSignal]) : actionSignal;
839
+ try {
840
+ await updateSyncSetup(setupName, (setup) => ({ ...setup, sync: { ...setup.sync, automaticTransfer } }), {
841
+ signal: mutationSignal
842
+ });
843
+ if (mutationSignal.aborted) return { kind: "rejected" };
844
+ ctx.ui.notify(
845
+ `Automatic startup transfer ${automaticTransfer ? "enabled for the next session start" : "disabled"}. Completed transfers are not undone.`,
846
+ "info"
847
+ );
848
+ return { kind: "stay" };
849
+ } catch (error) {
850
+ if (!mutationSignal.aborted) notifySaveFailure(ctx, error);
851
+ return { kind: "rejected" };
852
+ }
853
+ },
692
854
  "skip-secret-scan": async ({ value, signal: actionSignal }) => {
693
855
  const skipSecretScan = value === "On";
694
856
  const mutationSignal = signal ? AbortSignal.any([signal, actionSignal]) : actionSignal;
@@ -746,7 +908,7 @@ async function showSyncSettings(ctx, runRoute, signal) {
746
908
  },
747
909
  "remote-include": async ({ signal: actionSignal }) => {
748
910
  const reviewSignal = signal ? AbortSignal.any([signal, actionSignal]) : actionSignal;
749
- const { showRemoteSelectionReview } = await import("./remote-selection-ui-TZTJF32Y.ts");
911
+ const { showRemoteSelectionReview } = await import("./remote-selection-ui-SMF5CLFO.ts");
750
912
  if (reviewSignal.aborted) return { kind: "rejected" };
751
913
  const review = await showRemoteSelectionReview(ctx, setupName, reviewSignal, void 0, {
752
914
  origin: "settings",
@@ -768,7 +930,10 @@ async function showSyncSettings(ctx, runRoute, signal) {
768
930
  });
769
931
  }
770
932
  function notifySaveFailure(ctx, error) {
771
- ctx.ui.notify(`Pi Sync settings save failed: ${error instanceof Error ? error.message : String(error)}`, "error");
933
+ ctx.ui.notify(
934
+ `Pi Sync settings save failed: ${safeTerminalText(error instanceof Error ? error.message : String(error))}`,
935
+ "error"
936
+ );
772
937
  }
773
938
 
774
939
  // src/ui/storage-connections-ui.ts
@@ -786,8 +951,8 @@ async function showStorageConnections(ctx, signal) {
786
951
  return {
787
952
  kind: "actions",
788
953
  title: "Storage connections",
789
- lines: state.version3 ? ["Server addresses and sign-in details, reusable across sync setups."] : ["Create version 3 settings before managing storage connections."],
790
- items: state.version3 ? [
954
+ lines: state.supportedVersion ? ["Server addresses and sign-in details, reusable across sync setups."] : ["Create supported pi-sync settings before managing storage connections."],
955
+ items: state.supportedVersion ? [
791
956
  { id: "add", label: "Add storage connection", action: "add" },
792
957
  ...names.map((name, index) => {
793
958
  const id = `connection:${index}`;
@@ -882,11 +1047,15 @@ async function loadStorageMenuState(selectedName, signal) {
882
1047
  const profiles = ownRecord(raw?.storageConnections) ?? {};
883
1048
  const profile = selectedName ? ownRecord(profiles[selectedName]) : void 0;
884
1049
  if (!selectedName || !profile) {
885
- return { version3: raw?.version === 3, profiles, selected: void 0 };
1050
+ return {
1051
+ supportedVersion: raw?.version === 3 || raw?.version === 4 || raw?.version === 5,
1052
+ profiles,
1053
+ selected: void 0
1054
+ };
886
1055
  }
887
1056
  const usedBy = referencingSetups(raw, selectedName);
888
1057
  return {
889
- version3: raw?.version === 3,
1058
+ supportedVersion: raw?.version === 3 || raw?.version === 4 || raw?.version === 5,
890
1059
  profiles,
891
1060
  selected: {
892
1061
  name: selectedName,
@@ -911,7 +1080,7 @@ function notifyConnectionError(ctx, name, error, signal) {
911
1080
  async function editStorageConnection(ctx, name, profile, usedBy, signal) {
912
1081
  if (ctx.mode !== "tui") {
913
1082
  ctx.ui.notify(
914
- "Editing storage connections requires TUI mode for safe credential handling. Edit the private version 3 settings file instead.",
1083
+ "Editing storage connections requires TUI mode for safe credential handling. Edit the private pi-sync settings file instead.",
915
1084
  "warning"
916
1085
  );
917
1086
  return;
@@ -984,7 +1153,7 @@ async function editStorageConnection(ctx, name, profile, usedBy, signal) {
984
1153
  async function showAddStorageConnection(ctx, signal) {
985
1154
  if (ctx.mode !== "tui") {
986
1155
  ctx.ui.notify(
987
- "Adding storage connections requires TUI mode for safe credential handling. Edit the private version 3 settings file instead.",
1156
+ "Adding storage connections requires TUI mode for safe credential handling. Edit the private pi-sync settings file instead.",
988
1157
  "warning"
989
1158
  );
990
1159
  return false;
@@ -1129,7 +1298,7 @@ async function selectSetupForSwitch(ctx, raw, targets, active, signal) {
1129
1298
  async function showSetupSwitcher(ctx, runRoute, selectedName, signal) {
1130
1299
  const raw = await readLocalConfigObject();
1131
1300
  if (signal?.aborted) return false;
1132
- if (raw?.version !== 3) {
1301
+ if (raw?.version !== 3 && raw?.version !== 4 && raw?.version !== 5) {
1133
1302
  ctx.ui.notify("Add a second sync setup before switching setups.", "info");
1134
1303
  return false;
1135
1304
  }
@@ -1174,6 +1343,7 @@ async function showSetupSwitcher(ctx, runRoute, selectedName, signal) {
1174
1343
  `Storage: ${backendStorageDescription(config)}`,
1175
1344
  `Included content: ${config.include.length} paths`,
1176
1345
  `Automatic sync: ${automaticSyncSummary(config.automatic)} \xB7 Sessions: ${config.include.includes("sessions") ? "On" : "Off"}`,
1346
+ `Automatic transfer at startup: ${config.automaticTransfer ? "On \u2014 may upload, replace, or delete selected files; no reload" : "Off"}`,
1177
1347
  "",
1178
1348
  switchEffect
1179
1349
  ].join("\n"),
@@ -1248,7 +1418,7 @@ async function showAddTarget(ctx, signal) {
1248
1418
  let raw = await readLocalConfigObject();
1249
1419
  if (signal?.aborted) return;
1250
1420
  if (!raw) return void ctx.ui.notify("Set up the first sync setup before adding another.", "info");
1251
- if (raw.version !== 3) {
1421
+ if (raw.version !== 3 && raw.version !== 4 && raw.version !== 5) {
1252
1422
  ctx.ui.notify("Version 1 and version 2 settings are unsupported and are never migrated.", "error");
1253
1423
  return;
1254
1424
  }
@@ -1295,7 +1465,7 @@ async function showEditTarget(ctx, name, signal) {
1295
1465
  const partial = await loadPartialConfig(name);
1296
1466
  if (signal?.aborted) return;
1297
1467
  if (!partial.setupName) {
1298
- ctx.ui.notify("Create version 3 settings before editing a named sync setup.", "info");
1468
+ ctx.ui.notify("Create supported pi-sync settings before editing a named sync setup.", "info");
1299
1469
  return;
1300
1470
  }
1301
1471
  if (partial.storageKind === "webdav") {
@@ -1374,6 +1544,7 @@ async function showSyncManager(ctx, runRoute, sessionSignal, options = {}) {
1374
1544
  title: "History & recovery",
1375
1545
  items: [
1376
1546
  { id: "history", label: "Browse history", action: "history" },
1547
+ { id: "conflicts", label: "Review unresolved conflicts", action: "conflicts" },
1377
1548
  {
1378
1549
  id: "doctor",
1379
1550
  label: "Check setup",
@@ -1392,7 +1563,7 @@ async function showSyncManager(ctx, runRoute, sessionSignal, options = {}) {
1392
1563
  if (!attention) {
1393
1564
  const observation = options.getObservation?.();
1394
1565
  if (!observation) return { kind: "stay" };
1395
- const { showRemoteSelectionReview } = await import("./remote-selection-ui-TZTJF32Y.ts");
1566
+ const { showRemoteSelectionReview } = await import("./remote-selection-ui-SMF5CLFO.ts");
1396
1567
  if (sessionSignal?.aborted) return { kind: "close" };
1397
1568
  const config = await loadConfig();
1398
1569
  if (sessionSignal?.aborted) return { kind: "close" };
@@ -1508,6 +1679,14 @@ async function showSyncManager(ctx, runRoute, sessionSignal, options = {}) {
1508
1679
  await runRoute("init");
1509
1680
  return { kind: "stay" };
1510
1681
  },
1682
+ conflicts: async () => {
1683
+ const result = await runCancellableOperation(ctx, "Reviewing private conflicts\u2026", "conflicts", runRoute, {
1684
+ commitAware: true,
1685
+ signal: sessionSignal
1686
+ });
1687
+ const disposition = await dispatchManagerResult(ctx, result, "sync", runRoute, sessionSignal);
1688
+ return disposition.kind === "close" ? { kind: "close" } : { kind: "stay" };
1689
+ },
1511
1690
  back: async () => ({ kind: "back" })
1512
1691
  }
1513
1692
  });
@@ -1550,4 +1729,4 @@ function syncMainMenuItem(label) {
1550
1729
  export {
1551
1730
  showSyncManager
1552
1731
  };
1553
- //# sourceMappingURL=manager-ui-763NEKJY.ts.map
1732
+ //# sourceMappingURL=manager-ui-HSMHS3OK.ts.map