@narumitw/pi-sync 0.57.10 → 0.57.12

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 (173) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +32 -22
  3. package/dist/chunks/{agent-candidate-EYB35AGL.ts → agent-candidate-U2NU3GUX.ts} +11 -11
  4. package/dist/chunks/{agent-plan-KO43GTOR.ts → agent-plan-3YXDIJXO.ts} +25 -22
  5. package/dist/chunks/agent-plan-3YXDIJXO.ts.map +7 -0
  6. package/dist/chunks/{agent-refusal-retry-C2HQROTP.ts → agent-refusal-retry-476YGNU5.ts} +10 -10
  7. package/dist/chunks/{agent-runner-WMSCKSCB.ts → agent-runner-FCRV2Z75.ts} +11 -10
  8. package/dist/chunks/agent-runner-FCRV2Z75.ts.map +7 -0
  9. package/dist/chunks/{agent-store-UT52DS7B.ts → agent-store-7GINKTS7.ts} +8 -8
  10. package/dist/chunks/{agent-store-UT52DS7B.ts.map → agent-store-7GINKTS7.ts.map} +2 -2
  11. package/dist/chunks/{agent-sync-VM3TXWA3.ts → agent-sync-P6AWQXGK.ts} +6 -6
  12. package/dist/chunks/{chunk-JXWTEJS5.ts → chunk-45COPAK7.ts} +10 -3
  13. package/dist/chunks/chunk-45COPAK7.ts.map +7 -0
  14. package/dist/chunks/{chunk-DOJRPBQU.ts → chunk-5TSCBBPP.ts} +4 -4
  15. package/dist/chunks/{chunk-CDQGSZL3.ts → chunk-6LD5KX55.ts} +6 -6
  16. package/dist/chunks/{chunk-MDUORMCK.ts → chunk-6O3EDBRL.ts} +2 -2
  17. package/dist/chunks/{chunk-AAPAHANE.ts → chunk-7A35ZAVO.ts} +320 -219
  18. package/dist/chunks/chunk-7A35ZAVO.ts.map +7 -0
  19. package/dist/chunks/{chunk-STVQQ5KG.ts → chunk-7ZIYU5QS.ts} +6 -6
  20. package/dist/chunks/{chunk-AO6EDUA6.ts → chunk-AKMV47YC.ts} +3 -3
  21. package/dist/chunks/{chunk-66GAUVDL.ts → chunk-BEST45H2.ts} +6 -6
  22. package/dist/chunks/{chunk-DO55XUNU.ts → chunk-CBLTWXAU.ts} +15 -5
  23. package/dist/chunks/chunk-CBLTWXAU.ts.map +7 -0
  24. package/dist/chunks/{chunk-IMMMZUFC.ts → chunk-CQD2TJHE.ts} +7 -7
  25. package/dist/chunks/{chunk-IMMMZUFC.ts.map → chunk-CQD2TJHE.ts.map} +2 -2
  26. package/dist/chunks/{chunk-73MDIVYJ.ts → chunk-DN4IA7MR.ts} +6 -6
  27. package/dist/chunks/{chunk-PHANBTVM.ts → chunk-ESKOPQVB.ts} +4 -4
  28. package/dist/chunks/{chunk-YZ5BQNRZ.ts → chunk-FHE2DCA2.ts} +2 -2
  29. package/dist/chunks/{chunk-YZ5BQNRZ.ts.map → chunk-FHE2DCA2.ts.map} +2 -2
  30. package/dist/chunks/{chunk-MRIPZGPR.ts → chunk-FLHCOJCD.ts} +3 -3
  31. package/dist/chunks/{chunk-5WJQ6XOW.ts → chunk-GNYGNHFF.ts} +2 -2
  32. package/dist/chunks/{chunk-DFJGTVPX.ts → chunk-KYTT7KV4.ts} +2 -2
  33. package/dist/chunks/{chunk-UGSSZWQ6.ts → chunk-MKSMJPVA.ts} +2 -2
  34. package/dist/chunks/{chunk-2VSN3MFX.ts → chunk-MMQIEDZ6.ts} +33 -27
  35. package/dist/chunks/chunk-MMQIEDZ6.ts.map +7 -0
  36. package/dist/chunks/{chunk-RAMHGOX2.ts → chunk-OJARL2TG.ts} +4 -4
  37. package/dist/chunks/{chunk-BJXSCGQK.ts → chunk-OKPPZA4D.ts} +148 -89
  38. package/dist/chunks/chunk-OKPPZA4D.ts.map +7 -0
  39. package/dist/chunks/{chunk-RX3WGWVL.ts → chunk-OVBAW7KV.ts} +177 -130
  40. package/dist/chunks/chunk-OVBAW7KV.ts.map +7 -0
  41. package/dist/chunks/{chunk-U77DWNRH.ts → chunk-Q23JJW7E.ts} +3 -3
  42. package/dist/chunks/{chunk-NLWS55B6.ts → chunk-QPC5HT3A.ts} +2 -2
  43. package/dist/chunks/{chunk-NLWS55B6.ts.map → chunk-QPC5HT3A.ts.map} +1 -1
  44. package/dist/chunks/{chunk-4RLATUP4.ts → chunk-S5OCYVPS.ts} +7 -7
  45. package/dist/chunks/{chunk-4RLATUP4.ts.map → chunk-S5OCYVPS.ts.map} +2 -2
  46. package/dist/chunks/{chunk-QWV3XKNJ.ts → chunk-TIMWN2RH.ts} +5 -5
  47. package/dist/chunks/{chunk-Z5O2TLNF.ts → chunk-TZC7GDJE.ts} +7 -7
  48. package/dist/chunks/{chunk-UCD7TQDT.ts → chunk-U6GXGY5G.ts} +5 -5
  49. package/dist/chunks/{chunk-PWA3YN5Z.ts → chunk-XEODG2PE.ts} +3 -3
  50. package/dist/chunks/{chunk-TO2GMX3K.ts → chunk-XJHQPB3E.ts} +2 -2
  51. package/dist/chunks/{chunk-JY2RVOUL.ts → chunk-Y4UVXOLQ.ts} +5 -5
  52. package/dist/chunks/{chunk-ODF2M4RF.ts → chunk-ZC3IHFNM.ts} +8 -8
  53. package/dist/chunks/{chunk-ODF2M4RF.ts.map → chunk-ZC3IHFNM.ts.map} +2 -2
  54. package/dist/chunks/{chunk-M5IV3KL2.ts → chunk-ZDDBZH72.ts} +3 -3
  55. package/dist/chunks/{command-handler-FO6F2APH.ts → command-handler-VKLZBRLO.ts} +26 -21
  56. package/dist/chunks/command-handler-VKLZBRLO.ts.map +7 -0
  57. package/dist/chunks/{config-B37W7FXW.ts → config-HYG7G5EM.ts} +6 -4
  58. package/dist/chunks/{conflict-artifacts-6XGSSU5S.ts → conflict-artifacts-J6WSDCWP.ts} +12 -12
  59. package/dist/chunks/{conflict-review-BQSHSJB3.ts → conflict-review-GXA6Y7KY.ts} +23 -23
  60. package/dist/chunks/{continuous-record-4DQ2EAZS.ts → continuous-record-EPHGR2DU.ts} +6 -6
  61. package/dist/chunks/{continuous-sync-ZT3UEQLK.ts → continuous-sync-TTOSJCQU.ts} +18 -18
  62. package/dist/chunks/{file-selection-FBLKM25V.ts → file-selection-IELTXSGZ.ts} +6 -7
  63. package/dist/chunks/{file-selection-FBLKM25V.ts.map → file-selection-IELTXSGZ.ts.map} +1 -1
  64. package/dist/chunks/{git-backend-7PCWECRN.ts → git-backend-KHD36W27.ts} +6 -7
  65. package/dist/chunks/{git-backend-7PCWECRN.ts.map → git-backend-KHD36W27.ts.map} +1 -1
  66. package/dist/chunks/{local-fields-B6JWLPAE.ts → local-fields-JEKAPZKD.ts} +6 -7
  67. package/dist/chunks/manager-result-dispatcher-6W52NM7F.ts +32 -0
  68. package/dist/chunks/{manager-ui-DGRTG76I.ts → manager-ui-INVMAFEN.ts} +245 -224
  69. package/dist/chunks/manager-ui-INVMAFEN.ts.map +7 -0
  70. package/dist/chunks/{merge-baseline-store-HL3VLMAM.ts → merge-baseline-store-65GKUMXI.ts} +6 -6
  71. package/dist/chunks/{remote-selection-ui-OHMIQ32X.ts → remote-selection-ui-TD4C4W3J.ts} +16 -12
  72. package/dist/chunks/{s3-backend-CJXBEWWU.ts → s3-backend-L24UZ6IK.ts} +12 -10
  73. package/dist/chunks/s3-backend-L24UZ6IK.ts.map +7 -0
  74. package/dist/chunks/{setup-switch-66GWMBNH.ts → setup-switch-KLGESLQX.ts} +6 -6
  75. package/dist/chunks/{setup-wizard-OHZXJVQZ.ts → setup-wizard-ZQAK35BP.ts} +7 -7
  76. package/dist/chunks/{snapshot-VRHNAUUH.ts → snapshot-RGNDYT6A.ts} +5 -6
  77. package/dist/chunks/{snapshot-transaction-EUGA4VX6.ts → snapshot-transaction-26BQO36S.ts} +8 -8
  78. package/dist/chunks/{sync-inspection-4WFQEJOE.ts → sync-inspection-MOMLGUTD.ts} +11 -11
  79. package/dist/chunks/{sync-operations-X4AOXMBT.ts → sync-operations-TMGQHBF5.ts} +26 -26
  80. package/dist/chunks/{sync-operations-X4AOXMBT.ts.map → sync-operations-TMGQHBF5.ts.map} +2 -2
  81. package/dist/chunks/{sync-state-SWLSMK4G.ts → sync-state-7L4NT7W2.ts} +7 -8
  82. package/dist/chunks/{webdav-backend-2Q5ZEBST.ts → webdav-backend-3ZCACILP.ts} +7 -8
  83. package/dist/chunks/{webdav-backend-2Q5ZEBST.ts.map → webdav-backend-3ZCACILP.ts.map} +1 -1
  84. package/dist/index.ts +20 -20
  85. package/docs/agent-continuous-hardening.md +5 -5
  86. package/docs/agent-mode-verification.md +3 -3
  87. package/docs/agent-quality-verification.md +3 -3
  88. package/docs/continuous-sync-verification.md +2 -2
  89. package/docs/settings.md +9 -5
  90. package/docs/tui-contract.md +20 -3
  91. package/package.json +3 -3
  92. package/skills/configure-pi-sync/SKILL.md +3 -1
  93. package/skills/configure-pi-sync/references/configuration.md +56 -38
  94. package/src/backends/s3/s3-client.ts +8 -1
  95. package/src/commands/command-execution.ts +11 -6
  96. package/src/commands/command.ts +1 -1
  97. package/src/paths.ts +6 -1
  98. package/src/settings/config-file.ts +205 -118
  99. package/src/settings/config.ts +1 -1
  100. package/src/settings/settings-management.ts +15 -1
  101. package/src/settings/settings-store.ts +0 -14
  102. package/src/settings/settings-types.ts +1 -1
  103. package/src/snapshot/session-paths.ts +15 -2
  104. package/src/snapshot/snapshot-mutation-queues.ts +142 -0
  105. package/src/snapshot/snapshot-transaction.ts +122 -125
  106. package/src/sync/agent-candidate.ts +3 -3
  107. package/src/sync/agent-plan.ts +11 -8
  108. package/src/sync/agent-runner.ts +10 -9
  109. package/src/sync/agent-store.ts +1 -1
  110. package/src/sync/agent-sync.ts +2 -2
  111. package/src/sync/continuous-record.ts +1 -1
  112. package/src/sync/merge-apply.ts +6 -0
  113. package/src/sync/merged-sync.ts +4 -4
  114. package/src/sync/sync-mutations.ts +1 -1
  115. package/src/ui/agent-mode-setting.ts +6 -6
  116. package/src/ui/cancellable-operation.ts +36 -13
  117. package/src/ui/manager-state.ts +4 -2
  118. package/src/ui/manager-ui.ts +76 -24
  119. package/src/ui/remote-selection-ui.ts +131 -82
  120. package/src/ui/settings-ui.ts +21 -56
  121. package/src/ui/setup/setup-switcher.ts +2 -2
  122. package/src/ui/sync-setups-ui.ts +2 -2
  123. package/dist/chunks/agent-plan-KO43GTOR.ts.map +0 -7
  124. package/dist/chunks/agent-runner-WMSCKSCB.ts.map +0 -7
  125. package/dist/chunks/chunk-2VSN3MFX.ts.map +0 -7
  126. package/dist/chunks/chunk-AAPAHANE.ts.map +0 -7
  127. package/dist/chunks/chunk-BJXSCGQK.ts.map +0 -7
  128. package/dist/chunks/chunk-DO55XUNU.ts.map +0 -7
  129. package/dist/chunks/chunk-JXWTEJS5.ts.map +0 -7
  130. package/dist/chunks/chunk-RX3WGWVL.ts.map +0 -7
  131. package/dist/chunks/command-handler-FO6F2APH.ts.map +0 -7
  132. package/dist/chunks/manager-result-dispatcher-NODX3KKH.ts +0 -28
  133. package/dist/chunks/manager-ui-DGRTG76I.ts.map +0 -7
  134. package/dist/chunks/s3-backend-CJXBEWWU.ts.map +0 -7
  135. /package/dist/chunks/{agent-candidate-EYB35AGL.ts.map → agent-candidate-U2NU3GUX.ts.map} +0 -0
  136. /package/dist/chunks/{agent-refusal-retry-C2HQROTP.ts.map → agent-refusal-retry-476YGNU5.ts.map} +0 -0
  137. /package/dist/chunks/{agent-sync-VM3TXWA3.ts.map → agent-sync-P6AWQXGK.ts.map} +0 -0
  138. /package/dist/chunks/{chunk-DOJRPBQU.ts.map → chunk-5TSCBBPP.ts.map} +0 -0
  139. /package/dist/chunks/{chunk-CDQGSZL3.ts.map → chunk-6LD5KX55.ts.map} +0 -0
  140. /package/dist/chunks/{chunk-MDUORMCK.ts.map → chunk-6O3EDBRL.ts.map} +0 -0
  141. /package/dist/chunks/{chunk-STVQQ5KG.ts.map → chunk-7ZIYU5QS.ts.map} +0 -0
  142. /package/dist/chunks/{chunk-AO6EDUA6.ts.map → chunk-AKMV47YC.ts.map} +0 -0
  143. /package/dist/chunks/{chunk-66GAUVDL.ts.map → chunk-BEST45H2.ts.map} +0 -0
  144. /package/dist/chunks/{chunk-73MDIVYJ.ts.map → chunk-DN4IA7MR.ts.map} +0 -0
  145. /package/dist/chunks/{chunk-PHANBTVM.ts.map → chunk-ESKOPQVB.ts.map} +0 -0
  146. /package/dist/chunks/{chunk-MRIPZGPR.ts.map → chunk-FLHCOJCD.ts.map} +0 -0
  147. /package/dist/chunks/{chunk-5WJQ6XOW.ts.map → chunk-GNYGNHFF.ts.map} +0 -0
  148. /package/dist/chunks/{chunk-DFJGTVPX.ts.map → chunk-KYTT7KV4.ts.map} +0 -0
  149. /package/dist/chunks/{chunk-UGSSZWQ6.ts.map → chunk-MKSMJPVA.ts.map} +0 -0
  150. /package/dist/chunks/{chunk-RAMHGOX2.ts.map → chunk-OJARL2TG.ts.map} +0 -0
  151. /package/dist/chunks/{chunk-U77DWNRH.ts.map → chunk-Q23JJW7E.ts.map} +0 -0
  152. /package/dist/chunks/{chunk-QWV3XKNJ.ts.map → chunk-TIMWN2RH.ts.map} +0 -0
  153. /package/dist/chunks/{chunk-Z5O2TLNF.ts.map → chunk-TZC7GDJE.ts.map} +0 -0
  154. /package/dist/chunks/{chunk-UCD7TQDT.ts.map → chunk-U6GXGY5G.ts.map} +0 -0
  155. /package/dist/chunks/{chunk-PWA3YN5Z.ts.map → chunk-XEODG2PE.ts.map} +0 -0
  156. /package/dist/chunks/{chunk-TO2GMX3K.ts.map → chunk-XJHQPB3E.ts.map} +0 -0
  157. /package/dist/chunks/{chunk-JY2RVOUL.ts.map → chunk-Y4UVXOLQ.ts.map} +0 -0
  158. /package/dist/chunks/{chunk-M5IV3KL2.ts.map → chunk-ZDDBZH72.ts.map} +0 -0
  159. /package/dist/chunks/{config-B37W7FXW.ts.map → config-HYG7G5EM.ts.map} +0 -0
  160. /package/dist/chunks/{conflict-artifacts-6XGSSU5S.ts.map → conflict-artifacts-J6WSDCWP.ts.map} +0 -0
  161. /package/dist/chunks/{conflict-review-BQSHSJB3.ts.map → conflict-review-GXA6Y7KY.ts.map} +0 -0
  162. /package/dist/chunks/{continuous-record-4DQ2EAZS.ts.map → continuous-record-EPHGR2DU.ts.map} +0 -0
  163. /package/dist/chunks/{continuous-sync-ZT3UEQLK.ts.map → continuous-sync-TTOSJCQU.ts.map} +0 -0
  164. /package/dist/chunks/{local-fields-B6JWLPAE.ts.map → local-fields-JEKAPZKD.ts.map} +0 -0
  165. /package/dist/chunks/{manager-result-dispatcher-NODX3KKH.ts.map → manager-result-dispatcher-6W52NM7F.ts.map} +0 -0
  166. /package/dist/chunks/{merge-baseline-store-HL3VLMAM.ts.map → merge-baseline-store-65GKUMXI.ts.map} +0 -0
  167. /package/dist/chunks/{remote-selection-ui-OHMIQ32X.ts.map → remote-selection-ui-TD4C4W3J.ts.map} +0 -0
  168. /package/dist/chunks/{setup-switch-66GWMBNH.ts.map → setup-switch-KLGESLQX.ts.map} +0 -0
  169. /package/dist/chunks/{setup-wizard-OHZXJVQZ.ts.map → setup-wizard-ZQAK35BP.ts.map} +0 -0
  170. /package/dist/chunks/{snapshot-VRHNAUUH.ts.map → snapshot-RGNDYT6A.ts.map} +0 -0
  171. /package/dist/chunks/{snapshot-transaction-EUGA4VX6.ts.map → snapshot-transaction-26BQO36S.ts.map} +0 -0
  172. /package/dist/chunks/{sync-inspection-4WFQEJOE.ts.map → sync-inspection-MOMLGUTD.ts.map} +0 -0
  173. /package/dist/chunks/{sync-state-SWLSMK4G.ts.map → sync-state-7L4NT7W2.ts.map} +0 -0
@@ -7,8 +7,8 @@ The configuration skill provides guidance only; the user performs settings chang
7
7
  - [Required backend shapes](#required-backend-shapes)
8
8
  - [Status display](#status-display)
9
9
  - [Secret scanning](#secret-scanning)
10
- - [Experimental Agent mode](#experimental-agent-mode)
11
- - [Experimental Continuous sync](#experimental-continuous-sync)
10
+ - [Autonomous sync](#autonomous-sync)
11
+ - [Continuous sync](#continuous-sync)
12
12
  - [Included content](#included-content)
13
13
  - [Unsupported old settings and recovery](#unsupported-old-settings-and-recovery)
14
14
 
@@ -26,11 +26,16 @@ Missing settings load as unconfigured without creating an agent directory, file,
26
26
  An explicit setup creates the file atomically.
27
27
  On POSIX, pi-sync creates and replaces it with mode `0600`.
28
28
  Pi-sync processes coordinate settings access through `pi-sync.json.mutation-lock`; lock-unaware editors are outside that serialization boundary and should not save the file while a pi-sync settings operation is running.
29
+ Within one process, settings reads wait for earlier writes, including writes that have not acquired the lock yet.
30
+ Replacement writes and flushes a private temporary file before renaming it over the canonical file; the active file is not moved away first.
29
31
  Credentials stay in this canonical private file and are never shown in menus, reviews, status, notifications, errors, logs, or completion metadata.
30
32
 
31
33
  A private `pi-sync.local.json` containing a valid version 3 document is copied byte-for-byte to `pi-sync.json`.
32
34
  The old file remains as a recovery copy.
33
35
  If both paths exist, `pi-sync.json` wins and the legacy file remains untouched.
36
+ If an older interrupted replacement left `pi-sync.json` missing and a hidden `.pi-sync.json.*.schema-migration-source` or `.pi-sync.json.*.migration-retired` file remains, settings access stops instead of loading the legacy copy.
37
+ Close every Pi process, preserve all recovery files, and review the retained settings before restoring the correct document to `pi-sync.json` with private permissions.
38
+ Do not delete recovery evidence or accept defaults to bypass this check.
34
39
 
35
40
  ### Settings ownership and required fields
36
41
 
@@ -50,14 +55,20 @@ Global settings apply to all setups on this device; they are not remote policy.
50
55
  | Content/partial policy | `sync.mergeContent`, `sync.partialSync` | Enabling either requires version 5. |
51
56
 
52
57
  Do not downgrade version 4 or 5 merely because an option is turned off.
53
- The four automatic controls have separate effects: `automatic` enables startup observation and selected-content shutdown pushes when sessions are included; `automaticTransfer` enables one eligible startup transfer; `agentMode` authorizes autonomous merged sync and model disclosure; `continuous` adds recurring work only with Agent mode enabled.
58
+ The four automatic controls have separate effects:
59
+
60
+ - **Automatic sync** (`sync.automatic`) enables startup observation and selected-content shutdown pushes when sessions are included.
61
+ - **Startup transfer** (`sync.automaticTransfer`) enables one eligible startup transfer.
62
+ - **Autonomous sync** (`sync.agentMode`, unchanged) authorizes eligible startup and **Sync now** transfers without another transfer confirmation, plus model assistance and disclosure for supported conflicts.
63
+ - **Continuous sync** (`sync.continuous`) adds recurring work only with Autonomous sync enabled.
64
+
54
65
  None of these controls is enabled by invoking the configuration skill.
55
66
 
56
67
  ### Editing in the manager
57
68
 
58
- Open `/sync` → **Settings** and choose **Sync content**, **Sync behavior**, **Storage & setups**, or **Advanced**.
59
- **All settings (search)** provides cross-category typing-to-search over the same rows.
60
- **Included paths** and **Compare included paths** are adjacent in Sync content; the latter compares path policies, not file contents.
69
+ Open `/sync` → **Settings** to go directly to **Pi Sync Settings**.
70
+ The screen has one searchable flat list with all 16 rows in their existing order and no category navigation or headings.
71
+ **Included paths** and **Compare included paths** are adjacent; the latter compares path policies, not file contents.
61
72
  **Storage location** edits the current setup's coordinates, while **Manage sync setups** and **Manage storage connections** retain the catalog and affected-setup reviews.
62
73
  Returning from catalog management refreshes the rows; switching or removing the owning setup closes its old Settings screen.
63
74
 
@@ -65,7 +76,7 @@ Descriptions identify the owner: this setup, all setups on this device, or share
65
76
  Simple choices report Saving and the durable Saved or failed/rejected result, preserve the selected row and search during refresh, and restore their previous value if saving fails.
66
77
  Escape closes the screen without undoing successful saves.
67
78
  Complex values retain their input/review flows.
68
- This navigation change does not alter schema, defaults, automatic-sync behavior, or experimental opt-ins.
79
+ This navigation change does not alter schema, defaults, automatic-sync behavior, or explicit opt-ins.
69
80
 
70
81
  The manager separates current state from transfer, inspection, and management actions.
71
82
  **Check remote status** and **View differences** invoke the existing read/check routes only when selected.
@@ -174,7 +185,7 @@ This check does not contact the server or discover setups configured only on oth
174
185
  Every setup requires `sync.include` and explicit `sync.automatic`.
175
186
  Setup asks for automatic sync with Off first; existing values are not rewritten.
176
187
  When enabled, the current setup receives one background startup check in TUI/RPC, including reload/new/resume/fork.
177
- Startup no longer performs automatic transfers or opens dialogs; review changes through `/sync`.
188
+ Automatic sync alone does not perform startup transfers or open dialogs; review changes through `/sync`.
178
189
  Print/JSON skip startup checks.
179
190
  Checks compare local hashes and remote metadata against the last sync baseline, so revision changes and missing baselines require review rather than proving a file conflict.
180
191
 
@@ -214,14 +225,14 @@ It accepts:
214
225
  ### Status display
215
226
 
216
227
  The global `showStatus` setting accepts a boolean and defaults to `true` when omitted from an existing version 3 document.
217
- Set it through **/sync → Settings → Advanced → Show sync status**; changes are saved and applied immediately for every setup.
228
+ Set it through **/sync → Settings → Show sync status**; changes are saved and applied immediately for every setup.
218
229
  When `false`, pi-sync clears and suppresses status text for background checks, manual and automatic transfers, and review attention.
219
230
  TUI review widgets and TUI/RPC notifications remain available, and lifecycle cleanup still clears any stale status owned by pi-sync.
220
231
 
221
232
  ### Debug startup timings
222
233
 
223
234
  The top-level user setting `debug` accepts only `true` or `false` and defaults to `false`.
224
- Edit `<getAgentDir()>/pi-sync.json` (normally `~/.pi/agent/pi-sync.json`) or use **/sync → Settings → Advanced → Debug startup timings**.
235
+ Edit `<getAgentDir()>/pi-sync.json` (normally `~/.pi/agent/pi-sync.json`) or use **/sync → Settings → Debug startup timings**.
225
236
  UI changes save immediately through the existing ordered, atomic settings writer; both manual and UI changes take effect on the next session start, including `/reload`, new, resume, and fork.
226
237
  There is no project or environment override.
227
238
 
@@ -250,7 +261,7 @@ Set `debug` to `false` or remove it, then start a new session to disable reports
250
261
  ### Secret scanning
251
262
 
252
263
  The global `skipSecretScan` setting accepts a boolean and defaults to `false`, including when omitted from an existing version 3 document.
253
- Set it through **/sync → Settings → Advanced → Skip secret scan**; changes are saved immediately and apply to subsequent pushes for every sync setup, including automatic pushes.
264
+ Set it through **/sync → Settings → Skip secret scan**; changes are saved immediately and apply to subsequent pushes for every sync setup, including automatic pushes.
254
265
  When `true`, pushes skip the local secret scan; other safety checks and confirmations remain unchanged.
255
266
  Enable it only after reviewing the destination and selected content because files containing secrets can be uploaded.
256
267
  `/sync doctor` still scans and reports possible secrets regardless of this setting.
@@ -290,8 +301,10 @@ RPC remains a read-only summary with the manual `sync.include` path.
290
301
 
291
302
  Every new snapshot stores the normalized included-content selection separately from the files that happened to exist.
292
303
  This preserves selected-but-missing paths without syncing `pi-sync.json`, storage credentials, automatic-sync preferences, or setup names.
293
- **Settings → Sync content → Compare included paths** opens the same review-first flow as a manager operation when local and remote lists differ.
294
- Adoption revalidates the remote head, immutable snapshot, reviewed storage coordinates, and local include list before one atomic settings update.
304
+ **Settings → Compare included paths** opens the same review-first flow as a manager operation when local and remote lists differ.
305
+ Adoption revalidates the remote head, immutable snapshot, and complete reviewed setup configuration, including the shared connection and credentials, before one atomic settings update.
306
+ Cancelling the network check leaves the content list unchanged.
307
+ A settings save that has started its atomic replacement can still finish; later cancellation does not undo it.
295
308
  The saved state changes only `sync.include`, preserves unknown settings fields, and never pulls files or writes sync state.
296
309
  Its explicit Continue action starts a fresh **Sync now** route, while **Done** leaves the reviewed settings change saved without implying a file operation.
297
310
  Keeping this device's list opens the reviewed force-push path and explicitly replaces the remote policy while preserving eligible unmanaged remote files.
@@ -310,7 +323,7 @@ Pull protects the currently open session file; restart Pi or resume a pulled ses
310
323
  ### Automatic startup transfer
311
324
 
312
325
  `sync.automaticTransfer` is optional and accepts only a boolean; absent means `false`.
313
- Set it through **/sync → Settings → Sync behavior → Startup transfer**.
326
+ Set it through **/sync → Settings → Startup transfer**.
314
327
  It is separate from `sync.automatic`: the legacy setting retains observation and selected-content shutdown behavior.
315
328
 
316
329
  When explicitly enabled, one startup transfer waits for an idle boundary in TUI/RPC.
@@ -338,27 +351,30 @@ Turning **Startup transfer** off does not revert transfers or remove backups/jou
338
351
  For interrupted operations, follow [merge recovery](https://github.com/narumiruna/pi-extensions/blob/main/packages/pi-sync/docs/merge-implementation-audit.md).
339
352
  Unknown fields remain preserved and malformed/invalid files block saves, just as for the existing policy.
340
353
 
341
- ### Experimental Agent mode
354
+ <a id="experimental-agent-mode"></a>
342
355
 
343
- `sync.agentMode` is an optional per-setup boolean, default `false`, in settings versions 3, 4, and 5.
344
- Open **Settings → Sync behavior → Agent mode (experimental)** and review the setup, destination, selected paths, model disclosure, costs, and autonomous writes before enabling.
356
+ ### Autonomous sync
357
+
358
+ Autonomous sync uses the unchanged `sync.agentMode` key, an optional per-setup boolean, default `false`, in settings versions 3, 4, and 5.
359
+ Open **Settings → Autonomous sync** and review the setup, destination, selected paths, transfer authorization, model disclosure, costs, and autonomous writes before enabling.
345
360
  RPC users can explicitly set the same field in the private settings file.
346
361
  Setting it to `true` is authorization, not just a display preference.
347
362
 
348
- Agent mode authorizes one eligible merged sync at the next session start and the same autonomous flow from **Sync now** or `/sync sync`.
349
- It can publish and replace/delete selected content without another transfer confirmation.
363
+ Autonomous sync authorizes one eligible merged sync at the next session startup and the same flow from **Sync now** or `/sync sync`, without another transfer confirmation.
364
+ It can publish, replace, or delete selected local and remote content.
365
+ The selected Pi model resolves supported text and settings conflicts; eligible conflict content is sent to its provider and may incur costs.
350
366
  It is independent of `sync.automatic` and `sync.automaticTransfer`; neither existing preference is changed.
351
- Disabling Startup transfer alone does not disable Agent mode.
367
+ Disabling Startup transfer alone does not disable Autonomous sync.
352
368
  Saving never starts a transfer from Settings.
353
369
  The atomic write cannot be cancelled after its final cancellation check.
354
370
  A completed save remains in effect after cancellation or session replacement; reopen Settings to read the saved value.
355
371
  TUI/RPC are supported; print/JSON and shutdown do not start a resolver.
356
- Agent mode alone adds no polling or daemon.
372
+ Autonomous sync alone adds no polling or daemon.
357
373
  Separately enabled Continuous sync provides recurring work while Pi remains open.
358
374
 
359
375
  An accepted baseline, matching included-content/local-field policies, and conditional or lease-protected publication remain required.
360
376
  First-source selection, policy migrations, explicit push/pull, and rollback keep their existing reviewed flows.
361
- `--yes` does not enable Agent mode or authorize model disclosure.
377
+ `--yes` does not enable Autonomous sync or authorize model disclosure.
362
378
  Completed transfers never reload resources automatically.
363
379
 
364
380
  The resolver uses the initiating session's selected model through Pi's public provider/authentication registry.
@@ -371,14 +387,14 @@ Parent model prompts and tools are not changed.
371
387
  This is configuration isolation, not an OS sandbox or proof of semantic correctness.
372
388
 
373
389
  Eligible text is global `AGENTS.md` and `.md`/`.txt` files under `prompts/` or `skills/`.
374
- Global `settings.json` uses the existing strict JSON, numeric, coupled-field, unknown-field, and machine-local-field policies. In Agent mode, `packages` merges by source identity and selects the higher explicit SemVer version for each npm package present on both sides, even when one side has not changed from the verified ancestor. No registry lookup occurs. Equal SemVer precedence does not select a winner: one-sided source edits are preserved, and divergent edits require atomic selection. Package filters must match. An existing local or remote array keeps its JSON spelling when it matches the merged result. Accepted settings files keep their bytes unless a package override is required; that override changes only the `packages` value. The ordered npm identity list can change on one side, or identically on both sides; conflicting order or membership changes retain atomic selection. This preserves insertion positions and resource precedence. Non-npm entries must remain unchanged across all three versions for per-package merging. Changed Git refs and local path aliases retain atomic selection instead of being combined as separate sources. Delete/upgrade conflicts, duplicate identities, incompatible filters, version ranges and tags also retain the existing atomic selection rules when deterministic merging is not possible. Other arrays and nested objects remain atomic.
390
+ Global `settings.json` uses the existing strict JSON, numeric, coupled-field, unknown-field, and machine-local-field policies. With Autonomous sync enabled, `packages` merges by source identity and selects the higher explicit SemVer version for each npm package present on both sides, even when one side has not changed from the verified ancestor. No registry lookup occurs. Equal SemVer precedence does not select a winner: one-sided source edits are preserved, and divergent edits require atomic selection. Package filters must match. An existing local or remote array keeps its JSON spelling when it matches the merged result. Accepted settings files keep their bytes unless a package override is required; that override changes only the `packages` value. The ordered npm identity list can change on one side, or identically on both sides; conflicting order or membership changes retain atomic selection. This preserves insertion positions and resource precedence. Non-npm entries must remain unchanged across all three versions for per-package merging. Changed Git refs and local path aliases retain atomic selection instead of being combined as separate sources. Delete/upgrade conflicts, duplicate identities, incompatible filters, version ranges and tags also retain the existing atomic selection rules when deterministic merging is not possible. Other arrays and nested objects remain atomic.
375
391
  Code/scripts, keybindings, binaries, unknown formats, session histories, deletion/modification conflicts, path collisions, and session-root transitions are not model-resolved.
376
392
  Existing deterministic merges still follow their own explicit settings.
377
393
  Unsupported or refused groups remain unresolved; only an explicitly enabled `partialSync` policy permits progress around them.
378
394
  Preserving versions does not mean a conflict is resolved.
379
395
 
380
396
  A verified accepted ancestor is required.
381
- Enabling Agent mode cannot recover historical ancestor bytes that were never retained.
397
+ Enabling Autonomous sync cannot recover historical ancestor bytes that were never retained.
382
398
  If the original accepted bytes are still available on both sides, a conflict-free sync can seed the text ancestors for future runs; otherwise use the existing reviewed recovery flow rather than inventing a base from current edits.
383
399
 
384
400
  Bounds are 8 KiB per input file, 16 conflict paths/units, 24 KiB of serialized model evidence, 16 KiB of candidate output, 8,192 maximum output tokens (or the selected model's smaller positive output limit), and one provider request per candidate attempt. A separately confirmed refusal retry can request the same candidate evidence again.
@@ -391,7 +407,7 @@ These limits are not an exact currency spending cap; in-flight disclosure or cha
391
407
 
392
408
  The existing possible-secret scanner always checks model evidence, even when `skipSecretScan` permits uploads without scanning.
393
409
  It is a heuristic, not a guarantee that all secrets are detected.
394
- Do not enable Agent mode for content you cannot disclose to the selected provider.
410
+ Do not enable Autonomous sync for content you cannot disclose to the selected provider.
395
411
  Provider errors and model reasoning are not copied into status or persistent records.
396
412
 
397
413
  Pi sessions sharing one agent directory use one local resolver claim.
@@ -410,7 +426,7 @@ They can contain merged text; retain them as private data.
410
426
  Different candidates are retained without automatic retention pruning.
411
427
  Ordinary **Sync now** and `/sync sync` reuse a valid cached refusal without another provider request.
412
428
  The manager's **Retry refused model result…** action separately reviews the setup, destination, conflict paths, selected model, disclosure and cost before one fresh request for that exact refusal.
413
- It requires foreground TUI/RPC, Agent mode, an accepted baseline and no pending journal; use ordinary Sync now or reviewed recovery first when those prerequisites are absent.
429
+ It requires foreground TUI/RPC, Autonomous sync, an accepted baseline and no pending journal; use ordinary Sync now or reviewed recovery first when those prerequisites are absent.
414
430
  Changed evidence, settings, model, session or cached bytes invalidate the review without disclosure.
415
431
  A repeated refusal, invalid response or failed pre-publication replacement retains the original result.
416
432
  A complete validated result replaces the active candidate only after the original refusal is archived privately in the same directory.
@@ -425,23 +441,25 @@ Pi-sync's cross-process operation lock excludes other pi-sync transfers, not arb
425
441
  Pi file mutation queues coordinate participating writers only within one process.
426
442
  Checks near rename reduce stale writes but do not provide an atomic compare-and-swap against another Pi process or editor.
427
443
  Session JSONL is not rewritten by the model; other active session writers remain subject to the existing protection limitations.
428
- Do not use Agent mode as a guarantee of lossless live editing.
444
+ Do not use Autonomous sync as a guarantee of lossless live editing.
429
445
 
430
- Turn Agent mode Off to prevent subsequent requests and invalidate pending authorization.
446
+ Turn Autonomous sync Off to prevent subsequent requests and invalidate pending authorization.
431
447
  Keep backups, snapshots, conflicts and pending journals; disabling does not undo completed work or erase history.
432
448
  Unknown publication outcomes use the existing journal reconciliation and never authorize blind republishing.
433
449
  Pending journals can be reconciled on a read/check/write/verify backend because recovery does not publish.
434
- New autonomous transfers still require conditional or lease-protected publication; turn Agent mode Off before a reviewed manual transfer on a weaker backend.
450
+ New autonomous transfers still require conditional or lease-protected publication; turn Autonomous sync Off before a reviewed manual transfer on a weaker backend.
435
451
  If recovery is blocked, retain evidence and use the existing reviewed recovery routes.
436
452
  Finish/reconcile pending merge journals before a downgrade.
437
453
  No Pi core changes or other extension are required.
438
454
 
439
- ### Experimental Continuous sync
455
+ <a id="experimental-continuous-sync"></a>
456
+
457
+ ### Continuous sync
440
458
 
441
459
  `sync.continuous` is an optional per-setup boolean, default `false`, in settings versions 3, 4 and 5.
442
- Open **Settings → Sync behavior → Continuous sync (experimental)** after enabling Agent mode.
460
+ Open **Settings → Continuous sync** after enabling Autonomous sync.
443
461
  RPC users can set the field explicitly in the private settings file.
444
- Existing Agent mode consent does not enable recurring requests.
462
+ Existing Autonomous sync consent does not enable recurring requests.
445
463
 
446
464
  The separate review names the setup, storage connection, destination, included paths, recurring costs and automatic writes.
447
465
  An elected idle session uses its selected Pi model; a different session can use a different provider/model.
@@ -453,8 +471,8 @@ Reopen Settings to read the saved value.
453
471
 
454
472
  Monitoring starts at an eligible TUI/RPC session startup, after awaited local transaction recovery, or after a foreground `/sync` command or manager closes.
455
473
  It replaces the duplicate one-shot startup transfer, not the legacy shutdown policy.
456
- Turning Continuous sync off stops its work without disabling Agent mode.
457
- Turning Agent mode off also stops monitoring; a saved Continuous sync On value remains ineffective until Agent mode is enabled again.
474
+ Turning Continuous sync off stops its work without disabling Autonomous sync.
475
+ Turning Autonomous sync off also stops monitoring; a saved Continuous sync On value remains ineffective until Autonomous sync is enabled again.
458
476
 
459
477
  A control-file change during monitoring is detected by a watcher or the five-second local control check.
460
478
  If monitoring is already off, external enablement requires the next session start or a foreground `/sync` command.
@@ -553,14 +571,14 @@ Preserve journals, backups and unresolved versions when proof is unavailable.
553
571
 
554
572
  Turn Continuous sync off and drain its owner before downgrade.
555
573
  Reconcile pending transfers with the current release.
556
- Existing Agent mode and shutdown preferences remain separately effective.
574
+ Existing Autonomous sync and shutdown preferences remain separately effective.
557
575
  Private scheduler records are separate from baseline/journal formats and remain retained; no automatic filesystem rollback or state migration is performed.
558
576
 
559
577
  For implementation evidence and test scope, see [Continuous sync verification](https://github.com/narumiruna/pi-extensions/blob/main/packages/pi-sync/docs/continuous-sync-verification.md).
560
578
 
561
579
  ### Settings field merge and machine-local fields
562
580
 
563
- `sync.mergeSettings` is experimental, defaults to `false`, and is available in the owning Settings screen.
581
+ `sync.mergeSettings` is an explicit opt-in, defaults to `false`, and is available through **Settings → Merge settings fields** for the current setup.
564
582
  With a verified accepted ancestor, global `settings.json` root fields merge conservatively: absent differs from null, arrays/nested objects are atomic, and provider/model and other coupled fields form one unit.
565
583
  Unknown fields are preserved.
566
584
  Pi accepts plain UTF-8 JSON with an optional BOM, not JSONC; duplicate/prototype-sensitive keys, invalid encodings, migration-dependent syntax, and over-bound inputs require directional review.
@@ -609,8 +627,8 @@ Never reinterpret portable snapshots with an old binary.
609
627
 
610
628
  ### Content merge and partial progress
611
629
 
612
- `sync.mergeContent` and `sync.partialSync` are independent experimental booleans, both default `false`.
613
- The owning **Advanced → Content merge / partial progress** setting offers Off, Merge only, Merge + partial, and Partial only and confirms an explicit settings-version-5 upgrade.
630
+ `sync.mergeContent` and `sync.partialSync` are independent opt-in booleans, both default `false`.
631
+ The owning **Settings → Content merge / partial progress** setting offers Off, Merge only, Merge + partial, and Partial only and confirms an explicit settings-version-5 upgrade.
614
632
  Manual configuration must also declare version 5.
615
633
  Settings version 5 retains the portable-policy behavior of version 4; moving from a legacy setup can require the existing separately confirmed directional policy migration.
616
634
  Disabling options retains the schema and recovery evidence.
@@ -193,7 +193,12 @@ export class S3Client {
193
193
  extraHeaders: Record<string, string> = {},
194
194
  ) {
195
195
  const url = new URL(this.endpoint.toString());
196
- url.pathname = posixJoin(url.pathname, this.config.destination.bucket, encodeKey(key));
196
+ // Preserve escaped endpoint bytes without decoding or double-encoding them.
197
+ // SigV4 requires uppercase escape hex, including existing endpoint escapes.
198
+ const endpointPath = url.pathname
199
+ .replace(/%(?![0-9A-Fa-f]{2})|[^A-Za-z0-9._~/%-]/gu, encodeKey)
200
+ .replace(/%[0-9A-Fa-f]{2}/gu, (escaped) => escaped.toUpperCase());
201
+ url.pathname = posixJoin(endpointPath, encodeKey(this.config.destination.bucket), encodeKey(key));
197
202
  const send = async (sessionToken: string | undefined) => {
198
203
  const transportSignal = this.transportSignal();
199
204
  const headers = await signedHeaders({
@@ -213,6 +218,8 @@ export class S3Client {
213
218
  method,
214
219
  headers,
215
220
  body: body ? new Uint8Array(body) : undefined,
221
+ // Never forward signed headers or snapshot bodies to an unreviewed URL.
222
+ redirect: "error",
216
223
  signal: transportSignal,
217
224
  });
218
225
  } catch (error) {
@@ -231,8 +231,12 @@ async function migrateStateDirectory(ctx: ExtensionCommandContext, options: Comm
231
231
 
232
232
  async function initConfig(ctx: ExtensionCommandContext, signal?: AbortSignal) {
233
233
  const configPath = localConfigPath();
234
- if (await readLocalConfigObject()) {
235
- ctx.ui.notify(`Config already exists: ${await activeLocalConfigPath()}`, "info");
234
+ const existing = await readLocalConfigObject();
235
+ throwIfAborted(signal);
236
+ if (existing) {
237
+ const activePath = await activeLocalConfigPath();
238
+ throwIfAborted(signal);
239
+ ctx.ui.notify(`Config already exists: ${activePath}`, "info");
236
240
  return;
237
241
  }
238
242
 
@@ -242,7 +246,8 @@ async function initConfig(ctx: ExtensionCommandContext, signal?: AbortSignal) {
242
246
  await showSetupWizard(ctx, signal);
243
247
  return;
244
248
  }
245
- await createLocalConfigDocument(localConfigTemplate());
249
+ await createLocalConfigDocument(localConfigTemplate(), signal);
250
+ throwIfAborted(signal);
246
251
  ctx.ui.notify(`Created ${configPath}. Add a storage connection and sync setup before syncing.`, "info");
247
252
  }
248
253
 
@@ -261,11 +266,11 @@ async function showConfig(ctx: ExtensionCommandContext, options: CommandOptions)
261
266
  ...storageLines,
262
267
  `storage path: ${config.storagePath}`,
263
268
  `automatic sync: ${automaticSyncSummary(config.automatic)}`,
264
- `settings field merge: ${config.mergeSettings ? "On (experimental)" : "Off"}`,
269
+ `settings field merge: ${config.mergeSettings ? "On" : "Off"}`,
265
270
  `machine-local settings.json fields: ${(config.localFields ?? []).map(safeTerminalText).join(", ") || "none"}`,
266
271
  `startup transfer: ${startupTransferSummary(config)}`,
267
- `agent mode: ${config.agentMode ? "On (experimental)" : "Off"}`,
268
- `continuous sync: ${config.continuous ? (config.agentMode ? "On (experimental)" : "paused; Agent mode is Off") : "Off"}`,
272
+ `autonomous sync: ${config.agentMode ? "On" : "Off"}`,
273
+ `continuous sync: ${config.continuous ? (config.agentMode ? "On" : "paused; Autonomous sync is Off") : "Off"}`,
269
274
  `included content: ${config.include.join(", ") || "none"}`,
270
275
  `sessions: ${config.include.includes("sessions") ? "included" : "not included"}`,
271
276
  `settings file: ${localConfigPath()}`,
@@ -236,7 +236,7 @@ export function usage() {
236
236
  `Automatic sync: ${AUTOMATIC_SYNC_DESCRIPTION}`,
237
237
  `Startup transfer: ${STARTUP_TRANSFER_DESCRIPTION}`,
238
238
  "Automatic transfer at startup is a separate opt-in: one conflict-free established-baseline transfer at idle in TUI/RPC, with conditional/lease publication and no resource reload.",
239
- "Experimental Continuous sync is a separate default-Off opt-in in Settings. It requires Agent mode, monitors while Pi is open, and can incur recurring network/model costs. Safety blocks and durable automatic request limits remain.",
239
+ "Continuous sync is a separate default-Off opt-in in Settings. It requires Autonomous sync, monitors while Pi is open, and can incur recurring network/model costs. Safety blocks and durable automatic request limits remain.",
240
240
  "Settings: use /sync init or edit storage connections and sync setups in ~/.pi/agent/pi-sync.json (or the configured Pi agent directory). Version 1 and version 2 settings are unsupported and are never rewritten.",
241
241
  ].join("\n");
242
242
  }
package/src/paths.ts CHANGED
@@ -45,7 +45,12 @@ export function assertWithinRoot(root: string, target: string, label = target) {
45
45
  }
46
46
 
47
47
  export function encodeKey(key: string) {
48
- return key.split("/").map(encodeURIComponent).join("/");
48
+ // SigV4 permits only RFC 3986 unreserved characters and key separators.
49
+ return key
50
+ .split("/")
51
+ .map(encodeURIComponent)
52
+ .join("/")
53
+ .replace(/[!'()*]/gu, (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`);
49
54
  }
50
55
 
51
56
  export function posixJoin(...parts: string[]) {