@narumitw/pi-sync 0.57.11 → 0.58.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.
- package/CHANGELOG.md +14 -0
- package/README.md +29 -21
- package/dist/chunks/{agent-candidate-OCDPRI5G.ts → agent-candidate-U2NU3GUX.ts} +2 -2
- package/dist/chunks/{agent-plan-P5B5RGZ7.ts → agent-plan-3YXDIJXO.ts} +13 -10
- package/dist/chunks/agent-plan-3YXDIJXO.ts.map +7 -0
- package/dist/chunks/{agent-runner-WMSCKSCB.ts → agent-runner-FCRV2Z75.ts} +11 -10
- package/dist/chunks/agent-runner-FCRV2Z75.ts.map +7 -0
- package/dist/chunks/{agent-store-RJSMNOYP.ts → agent-store-7GINKTS7.ts} +2 -2
- package/dist/chunks/{agent-store-RJSMNOYP.ts.map → agent-store-7GINKTS7.ts.map} +2 -2
- package/dist/chunks/{agent-sync-EUK2CPSS.ts → agent-sync-2R4GG2MF.ts} +3 -3
- package/dist/chunks/{chunk-67INJPKR.ts → chunk-CQD2TJHE.ts} +4 -4
- package/dist/chunks/{chunk-67INJPKR.ts.map → chunk-CQD2TJHE.ts.map} +2 -2
- package/dist/chunks/{chunk-ZDDBZH72.ts → chunk-JYILGR3K.ts} +2 -2
- package/dist/chunks/{chunk-OKPPZA4D.ts → chunk-MG66XM5U.ts} +3 -3
- package/dist/chunks/{chunk-OKPPZA4D.ts.map → chunk-MG66XM5U.ts.map} +2 -2
- package/dist/chunks/{chunk-7OLLPPLU.ts → chunk-MMQIEDZ6.ts} +6 -6
- package/dist/chunks/{chunk-7OLLPPLU.ts.map → chunk-MMQIEDZ6.ts.map} +2 -2
- package/dist/chunks/{chunk-Q23JJW7E.ts → chunk-MW6KUQYA.ts} +7 -5
- package/dist/chunks/chunk-MW6KUQYA.ts.map +7 -0
- package/dist/chunks/{chunk-NLWS55B6.ts → chunk-QPC5HT3A.ts} +2 -2
- package/dist/chunks/{chunk-NLWS55B6.ts.map → chunk-QPC5HT3A.ts.map} +1 -1
- package/dist/chunks/{chunk-DRTC4PKM.ts → chunk-S5OCYVPS.ts} +2 -2
- package/dist/chunks/{chunk-DRTC4PKM.ts.map → chunk-S5OCYVPS.ts.map} +2 -2
- package/dist/chunks/{chunk-3IYAPMST.ts → chunk-U6GXGY5G.ts} +2 -2
- package/dist/chunks/{chunk-F7E3VFXU.ts → chunk-WQMYV4DW.ts} +8 -8
- package/dist/chunks/chunk-WQMYV4DW.ts.map +7 -0
- package/dist/chunks/{chunk-CPT2BN6N.ts → chunk-XEODG2PE.ts} +2 -2
- package/dist/chunks/chunk-YIPK23JW.ts +171 -0
- package/dist/chunks/chunk-YIPK23JW.ts.map +7 -0
- package/dist/chunks/{command-handler-F2E7A5T5.ts → command-handler-KCB6MCWS.ts} +12 -12
- package/dist/chunks/{command-handler-F2E7A5T5.ts.map → command-handler-KCB6MCWS.ts.map} +2 -2
- package/dist/chunks/{conflict-review-2QMO2K6Q.ts → conflict-review-GXA6Y7KY.ts} +2 -2
- package/dist/chunks/{continuous-record-AH7JGYKM.ts → continuous-record-EPHGR2DU.ts} +2 -2
- package/dist/chunks/{continuous-sync-VRMXCA5X.ts → continuous-sync-FAFNLEUW.ts} +19 -11
- package/dist/chunks/continuous-sync-FAFNLEUW.ts.map +7 -0
- package/dist/chunks/{manager-result-dispatcher-6W52NM7F.ts → manager-result-dispatcher-XCYPW7RC.ts} +4 -4
- package/dist/chunks/{manager-ui-LM7DSJ2Y.ts → manager-ui-FMZXKPX2.ts} +153 -183
- package/dist/chunks/manager-ui-FMZXKPX2.ts.map +7 -0
- package/dist/chunks/{remote-selection-ui-TD4C4W3J.ts → remote-selection-ui-MYM2YCPF.ts} +3 -3
- package/dist/chunks/{setup-switch-PIPXYPCW.ts → setup-switch-KLGESLQX.ts} +3 -3
- package/dist/chunks/{setup-wizard-RR5U3SUB.ts → setup-wizard-ZQAK35BP.ts} +3 -3
- package/dist/chunks/{sync-operations-CV3TULLD.ts → sync-operations-45APGOL3.ts} +9 -9
- package/dist/chunks/sync-operations-45APGOL3.ts.map +7 -0
- package/dist/index.ts +80 -65
- package/dist/index.ts.map +2 -2
- package/docs/agent-continuous-hardening.md +5 -5
- package/docs/agent-mode-verification.md +3 -3
- package/docs/agent-quality-verification.md +3 -3
- package/docs/continuous-sync-verification.md +2 -2
- package/docs/settings.md +9 -5
- package/docs/tui-contract.md +57 -3
- package/package.json +3 -3
- package/skills/configure-pi-sync/SKILL.md +3 -1
- package/skills/configure-pi-sync/references/configuration.md +61 -38
- package/src/commands/command-execution.ts +3 -3
- package/src/commands/command.ts +1 -1
- package/src/settings/settings-types.ts +1 -1
- package/src/sync/agent-candidate.ts +3 -3
- package/src/sync/agent-plan.ts +11 -8
- package/src/sync/agent-runner.ts +10 -9
- package/src/sync/agent-store.ts +1 -1
- package/src/sync/agent-sync.ts +3 -3
- package/src/sync/continuous-record.ts +1 -1
- package/src/sync/continuous-sync.ts +15 -6
- package/src/sync/merged-sync.ts +4 -4
- package/src/sync/startup-check.ts +1 -1
- package/src/sync/sync-mutations.ts +3 -3
- package/src/sync/sync-queries.ts +2 -2
- package/src/sync-extension.ts +71 -57
- package/src/ui/agent-mode-setting.ts +6 -6
- package/src/ui/manager-state.ts +1 -1
- package/src/ui/remote-selection-ui.ts +1 -1
- package/src/ui/settings-ui.ts +19 -55
- package/src/ui/setup/setup-switcher.ts +2 -2
- package/src/ui/sync-attention.ts +5 -3
- package/src/ui/sync-setups-ui.ts +2 -2
- package/src/ui/sync-status.ts +214 -3
- package/dist/chunks/agent-plan-P5B5RGZ7.ts.map +0 -7
- package/dist/chunks/agent-runner-WMSCKSCB.ts.map +0 -7
- package/dist/chunks/chunk-F7E3VFXU.ts.map +0 -7
- package/dist/chunks/chunk-K3RIEL6J.ts +0 -20
- package/dist/chunks/chunk-K3RIEL6J.ts.map +0 -7
- package/dist/chunks/chunk-Q23JJW7E.ts.map +0 -7
- package/dist/chunks/continuous-sync-VRMXCA5X.ts.map +0 -7
- package/dist/chunks/manager-ui-LM7DSJ2Y.ts.map +0 -7
- package/dist/chunks/sync-operations-CV3TULLD.ts.map +0 -7
- /package/dist/chunks/{agent-candidate-OCDPRI5G.ts.map → agent-candidate-U2NU3GUX.ts.map} +0 -0
- /package/dist/chunks/{agent-sync-EUK2CPSS.ts.map → agent-sync-2R4GG2MF.ts.map} +0 -0
- /package/dist/chunks/{chunk-ZDDBZH72.ts.map → chunk-JYILGR3K.ts.map} +0 -0
- /package/dist/chunks/{chunk-3IYAPMST.ts.map → chunk-U6GXGY5G.ts.map} +0 -0
- /package/dist/chunks/{chunk-CPT2BN6N.ts.map → chunk-XEODG2PE.ts.map} +0 -0
- /package/dist/chunks/{conflict-review-2QMO2K6Q.ts.map → conflict-review-GXA6Y7KY.ts.map} +0 -0
- /package/dist/chunks/{continuous-record-AH7JGYKM.ts.map → continuous-record-EPHGR2DU.ts.map} +0 -0
- /package/dist/chunks/{manager-result-dispatcher-6W52NM7F.ts.map → manager-result-dispatcher-XCYPW7RC.ts.map} +0 -0
- /package/dist/chunks/{remote-selection-ui-TD4C4W3J.ts.map → remote-selection-ui-MYM2YCPF.ts.map} +0 -0
- /package/dist/chunks/{setup-switch-PIPXYPCW.ts.map → setup-switch-KLGESLQX.ts.map} +0 -0
- /package/dist/chunks/{setup-wizard-RR5U3SUB.ts.map → setup-wizard-ZQAK35BP.ts.map} +0 -0
|
@@ -84,7 +84,7 @@ export function createStartupCheck(
|
|
|
84
84
|
const captured = config;
|
|
85
85
|
const identity = syncCheckConfigFingerprint(captured);
|
|
86
86
|
checking = true;
|
|
87
|
-
setSyncStatus(ctx, "sync ...");
|
|
87
|
+
setSyncStatus(ctx, "sync ...", { activity: "checking" });
|
|
88
88
|
const { inspectSync } = await timings.measure("inspection import", () => loaders.inspection());
|
|
89
89
|
throwIfAborted(signal);
|
|
90
90
|
const currentConfig = await loadConfigForCheck();
|
|
@@ -118,7 +118,7 @@ export async function push(
|
|
|
118
118
|
const state = input?.state ?? (await readStateForConfig(config));
|
|
119
119
|
validateMutation();
|
|
120
120
|
requireResolvedDirectionalProgress(state, options);
|
|
121
|
-
setSyncStatus(ctx, `pushing ${config.setupName}
|
|
121
|
+
setSyncStatus(ctx, `pushing ${config.setupName}`, { activity: "pushing" });
|
|
122
122
|
const backend = input?.backend ?? (await factory(config));
|
|
123
123
|
throwIfAborted(options.signal);
|
|
124
124
|
const localRaw =
|
|
@@ -272,7 +272,7 @@ export async function pull(
|
|
|
272
272
|
const config = await loadConfig(options.setup);
|
|
273
273
|
throwIfAborted(options.signal);
|
|
274
274
|
await requireNoMergeJournal(config, options);
|
|
275
|
-
setSyncStatus(ctx, `pulling ${config.setupName}
|
|
275
|
+
setSyncStatus(ctx, `pulling ${config.setupName}`, { activity: "pulling" });
|
|
276
276
|
const backend = await factory(config);
|
|
277
277
|
const state = await readStateForConfig(config);
|
|
278
278
|
validateMutation();
|
|
@@ -433,7 +433,7 @@ export async function syncBoth(
|
|
|
433
433
|
options.background?.validate(config);
|
|
434
434
|
if (options.agentRetryRefusal) {
|
|
435
435
|
if (!config.agentMode || options.auto || options.background || !ctx.hasUI)
|
|
436
|
-
throw new Error("Enable
|
|
436
|
+
throw new Error("Enable Autonomous sync and use the foreground manager to retry a cached refusal.");
|
|
437
437
|
const state = await readStateForConfig(config);
|
|
438
438
|
validateMutation();
|
|
439
439
|
const pending = await readMergeJournal(config);
|
package/src/sync/sync-queries.ts
CHANGED
|
@@ -27,7 +27,7 @@ export async function status(
|
|
|
27
27
|
) {
|
|
28
28
|
const config = await loadConfig(options.setup);
|
|
29
29
|
throwIfAborted(options.signal);
|
|
30
|
-
setSyncStatus(ctx, `checking ${config.setupName}
|
|
30
|
+
setSyncStatus(ctx, `checking ${config.setupName}`, { activity: "checking" });
|
|
31
31
|
const { head, selectionState, localChanged, remoteChanged, localFiles, destination, capability } = await inspectSync(
|
|
32
32
|
config,
|
|
33
33
|
snapshotOptionsForContext(ctx, config),
|
|
@@ -70,7 +70,7 @@ export async function diff(
|
|
|
70
70
|
) {
|
|
71
71
|
const config = await loadConfig(options.setup);
|
|
72
72
|
throwIfAborted(options.signal);
|
|
73
|
-
setSyncStatus(ctx, `checking ${config.setupName}
|
|
73
|
+
setSyncStatus(ctx, `checking ${config.setupName}`, { activity: "checking" });
|
|
74
74
|
const backend = await factory(config);
|
|
75
75
|
const local = portableSnapshot(
|
|
76
76
|
await createSnapshot(config.snapshotIdentity, snapshotOptionsForContext(ctx, config)),
|
package/src/sync-extension.ts
CHANGED
|
@@ -10,12 +10,13 @@ import { errorMessage } from "./sync/sync-errors.js";
|
|
|
10
10
|
import { createSyncLoaders, type SyncDependencies } from "./sync/sync-loaders.js";
|
|
11
11
|
import { registerStartupTimings } from "./ui/startup-timings-entry.js";
|
|
12
12
|
import { createSyncAttentionController } from "./ui/sync-attention.js";
|
|
13
|
-
import { setSyncStatus } from "./ui/sync-status.js";
|
|
13
|
+
import { createSyncStatusPublisher, setSyncStatus } from "./ui/sync-status.js";
|
|
14
14
|
import { safeTerminalText } from "./ui/terminal-text.js";
|
|
15
15
|
|
|
16
16
|
export default function sync(pi: ExtensionAPI, dependencies: Partial<SyncDependencies> = {}) {
|
|
17
17
|
const loaders = createSyncLoaders(dependencies);
|
|
18
18
|
const attention = createSyncAttentionController();
|
|
19
|
+
const status = createSyncStatusPublisher(pi.events);
|
|
19
20
|
const publishTimings = registerStartupTimings(pi);
|
|
20
21
|
const check = createStartupCheck(loaders, attention, 30_000, publishTimings);
|
|
21
22
|
let sessionAbort = new AbortController();
|
|
@@ -60,6 +61,7 @@ export default function sync(pi: ExtensionAPI, dependencies: Partial<SyncDepende
|
|
|
60
61
|
if (!ctx.hasUI) {
|
|
61
62
|
throw new Error("/sync requires TUI or RPC mode so results and safety prompts are observable.");
|
|
62
63
|
}
|
|
64
|
+
status.bind(ctx);
|
|
63
65
|
const signal = sessionAbort.signal;
|
|
64
66
|
const manager = ctx.sessionManager;
|
|
65
67
|
const command = splitArgs(args)[0];
|
|
@@ -104,39 +106,46 @@ export default function sync(pi: ExtensionAPI, dependencies: Partial<SyncDepende
|
|
|
104
106
|
});
|
|
105
107
|
|
|
106
108
|
pi.on("session_start", async (_event, ctx) => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (signal.aborted) return false;
|
|
122
|
-
await check.stop();
|
|
123
|
-
if (signal.aborted) return false;
|
|
124
|
-
try {
|
|
125
|
-
const startup = await withStateDirectoryAccess(() => startSession(ctx, signal, publishTimings));
|
|
109
|
+
const releaseStatus = status.start(ctx);
|
|
110
|
+
try {
|
|
111
|
+
shutdownAbort?.abort(new DOMException("Session replaced", "AbortError"));
|
|
112
|
+
shutdownAbort = undefined;
|
|
113
|
+
sessionAbort.abort(new DOMException("Session replaced", "AbortError"));
|
|
114
|
+
sessionAbort = new AbortController();
|
|
115
|
+
const signal = sessionAbort.signal;
|
|
116
|
+
owner = ctx.sessionManager;
|
|
117
|
+
continuousPossible = false;
|
|
118
|
+
startupDebug = false;
|
|
119
|
+
attention.reset(ctx);
|
|
120
|
+
const previous = initialization;
|
|
121
|
+
initialization = (async () => {
|
|
122
|
+
await previous;
|
|
126
123
|
if (signal.aborted) return false;
|
|
127
|
-
|
|
128
|
-
return
|
|
129
|
-
|
|
130
|
-
if (
|
|
131
|
-
|
|
124
|
+
await continuous?.stop();
|
|
125
|
+
if (signal.aborted) return false;
|
|
126
|
+
await check.stop();
|
|
127
|
+
if (signal.aborted) return false;
|
|
128
|
+
try {
|
|
129
|
+
const startup = await withStateDirectoryAccess(() => startSession(ctx, signal, publishTimings));
|
|
130
|
+
if (signal.aborted) return false;
|
|
131
|
+
startupDebug = startup?.debug ?? false;
|
|
132
|
+
return startup?.config;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
if (!signal.aborted && ctx.hasUI) {
|
|
135
|
+
ctx.ui.notify(`pi-sync recovery required: ${safeTerminalText(errorMessage(error))}`, "error");
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
132
138
|
}
|
|
133
|
-
|
|
139
|
+
})();
|
|
140
|
+
const ready = await initialization;
|
|
141
|
+
if (ready === false) releaseStatus();
|
|
142
|
+
if (ready && !signal.aborted) {
|
|
143
|
+
if (ready.continuous && ready.agentMode) await resumeContinuous(ctx, signal);
|
|
144
|
+
else if (ready.automatic || startupTransferEnabled(ready)) check.start(ctx, signal, ready);
|
|
134
145
|
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (ready.continuous && ready.agentMode) await resumeContinuous(ctx, signal);
|
|
139
|
-
else if (ready.automatic || startupTransferEnabled(ready)) check.start(ctx, signal, ready);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
releaseStatus();
|
|
148
|
+
throw error;
|
|
140
149
|
}
|
|
141
150
|
});
|
|
142
151
|
|
|
@@ -154,36 +163,41 @@ export default function sync(pi: ExtensionAPI, dependencies: Partial<SyncDepende
|
|
|
154
163
|
});
|
|
155
164
|
|
|
156
165
|
pi.on("session_shutdown", async (event, ctx) => {
|
|
157
|
-
|
|
158
|
-
sessionAbort.abort(new DOMException("Session shut down", "AbortError"));
|
|
159
|
-
attention.reset(ctx);
|
|
160
|
-
shutdownAbort?.abort(new DOMException("Session shut down again", "AbortError"));
|
|
161
|
-
const controller = new AbortController();
|
|
162
|
-
shutdownAbort = controller;
|
|
163
|
-
const signal = combineSignals(controller.signal, AbortSignal.timeout(30_000));
|
|
164
|
-
const reason = typeof event === "object" && event ? (event as { reason?: string }).reason : undefined;
|
|
166
|
+
const releaseStatus = status.cleanupFor(ctx);
|
|
165
167
|
try {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
168
|
+
if (continuous && owner && owner !== ctx.sessionManager) return;
|
|
169
|
+
sessionAbort.abort(new DOMException("Session shut down", "AbortError"));
|
|
170
|
+
attention.reset(ctx);
|
|
171
|
+
shutdownAbort?.abort(new DOMException("Session shut down again", "AbortError"));
|
|
172
|
+
const controller = new AbortController();
|
|
173
|
+
shutdownAbort = controller;
|
|
174
|
+
const signal = combineSignals(controller.signal, AbortSignal.timeout(30_000));
|
|
175
|
+
const reason = typeof event === "object" && event ? (event as { reason?: string }).reason : undefined;
|
|
176
|
+
try {
|
|
177
|
+
const ready = await initialization;
|
|
178
|
+
if (signal.aborted) return;
|
|
179
|
+
await continuous?.stop(ctx.sessionManager);
|
|
180
|
+
if (signal.aborted) return;
|
|
181
|
+
await check.stop();
|
|
182
|
+
if (signal.aborted) return;
|
|
183
|
+
if (ready !== false && reason !== "reload") {
|
|
184
|
+
await withStateDirectoryAccess(async () => {
|
|
185
|
+
if (signal.aborted) return;
|
|
186
|
+
await autoPushSessions(ctx, signal, loaders);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
} catch (error) {
|
|
190
|
+
if (!signal.aborted) {
|
|
191
|
+
ctx.ui.notify(`pi-sync session push skipped: ${errorMessage(error)}`, "warning");
|
|
192
|
+
}
|
|
193
|
+
} finally {
|
|
194
|
+
if (shutdownAbort === controller) shutdownAbort = undefined;
|
|
181
195
|
}
|
|
196
|
+
if (signal.aborted) return;
|
|
197
|
+
setSyncStatus(ctx, undefined);
|
|
182
198
|
} finally {
|
|
183
|
-
|
|
199
|
+
releaseStatus();
|
|
184
200
|
}
|
|
185
|
-
if (signal.aborted) return;
|
|
186
|
-
setSyncStatus(ctx, undefined);
|
|
187
201
|
});
|
|
188
202
|
}
|
|
189
203
|
|
|
@@ -6,10 +6,10 @@ import { confirmMergeReview } from "./merge-review.js";
|
|
|
6
6
|
import { safeTerminalText } from "./terminal-text.js";
|
|
7
7
|
|
|
8
8
|
export const AGENT_MODE_DESCRIPTION =
|
|
9
|
-
"This setup
|
|
9
|
+
"This setup: sync eligible changes at session startup and on Sync now without transfer confirmation. Use the selected Pi model to resolve supported text and settings conflicts. Eligible conflict content may be sent to its provider and incur costs. Selected local and remote content can be replaced or deleted. Unresolved conflicts pause unless partial sync permits independent progress. Continuous sync is a separate opt-in. No automatic reload.";
|
|
10
10
|
|
|
11
11
|
export const CONTINUOUS_DESCRIPTION =
|
|
12
|
-
"This setup
|
|
12
|
+
"This setup: recurring sync while Pi is open. Requires Autonomous sync. Watches selected files, checks the remote every 60–66 seconds, and retries temporary failures. Local changes and retries can cause extra requests between polls. May disclose conflict text and incur model/storage costs. No automatic reload.";
|
|
13
13
|
|
|
14
14
|
export function saveContinuousMode(
|
|
15
15
|
ctx: ExtensionCommandContext,
|
|
@@ -33,8 +33,8 @@ export async function saveAgentMode(
|
|
|
33
33
|
const setup = settings.syncSetups[setupName];
|
|
34
34
|
if (!setup || settings.activeSyncSetup !== setupName) throw new Error("Current sync setup changed; reopen Settings.");
|
|
35
35
|
const continuous = preference === "continuous";
|
|
36
|
-
const label = continuous ? "Continuous sync" : "
|
|
37
|
-
if (enabled && continuous && !setup.sync.agentMode) throw new Error("Enable
|
|
36
|
+
const label = continuous ? "Continuous sync" : "Autonomous sync";
|
|
37
|
+
if (enabled && continuous && !setup.sync.agentMode) throw new Error("Enable Autonomous sync before Continuous sync.");
|
|
38
38
|
if (Boolean(setup.sync[preference]) === enabled) return true;
|
|
39
39
|
if (enabled) {
|
|
40
40
|
const summary = [
|
|
@@ -59,7 +59,7 @@ export async function saveAgentMode(
|
|
|
59
59
|
if (
|
|
60
60
|
!(await confirmMergeReview(
|
|
61
61
|
ctx,
|
|
62
|
-
`Enable
|
|
62
|
+
`Enable ${label}?`,
|
|
63
63
|
summary,
|
|
64
64
|
signal,
|
|
65
65
|
() => !signal?.aborted,
|
|
@@ -80,7 +80,7 @@ export async function saveAgentMode(
|
|
|
80
80
|
settings.storageConnections[setup.storage.connection],
|
|
81
81
|
)
|
|
82
82
|
)
|
|
83
|
-
throw new Error("Sync setup or connection changed during
|
|
83
|
+
throw new Error("Sync setup or connection changed during Autonomous sync review; reopen Settings.");
|
|
84
84
|
return {
|
|
85
85
|
...current,
|
|
86
86
|
syncSetups: { ...current.syncSetups, [setupName]: { ...setup, sync: { ...setup.sync, [preference]: enabled } } },
|
package/src/ui/manager-state.ts
CHANGED
|
@@ -165,7 +165,7 @@ export async function describeManagerState(
|
|
|
165
165
|
`Included: ${selection.builtIns.length} built-in group${selection.builtIns.length === 1 ? "" : "s"} · ${selection.custom.length} extra path${selection.custom.length === 1 ? "" : "s"} · Sessions ${selection.sessions ? "on" : "off"}`,
|
|
166
166
|
`Automatic sync: ${automaticSyncSummary(config.automatic)}`,
|
|
167
167
|
`Startup transfer: ${startupTransferSummary(config)}`,
|
|
168
|
-
`
|
|
168
|
+
`Autonomous sync: ${config.agentMode ? "On (autonomous merged sync)" : "Off"}`,
|
|
169
169
|
...(config.continuous ? await (await import("../sync/continuous-record.js")).continuousStatusLines(config) : []),
|
|
170
170
|
`Last applied: ${lastAppliedSnapshot}`,
|
|
171
171
|
...(noSyncedContent
|
|
@@ -489,7 +489,7 @@ async function inspectConfiguredRemoteSelection(
|
|
|
489
489
|
const config = await loadConfig(setupName);
|
|
490
490
|
if (signal?.aborted) return undefined;
|
|
491
491
|
validateOwner();
|
|
492
|
-
setSyncStatus(ctx, `checking synced content for ${safeTerminalText(config.setupName)}
|
|
492
|
+
setSyncStatus(ctx, `checking synced content for ${safeTerminalText(config.setupName)}`, { activity: "checking" });
|
|
493
493
|
const backend = await factory(config);
|
|
494
494
|
if (signal?.aborted) return undefined;
|
|
495
495
|
validateOwner();
|
package/src/ui/settings-ui.ts
CHANGED
|
@@ -92,32 +92,20 @@ export async function showSyncSettings(
|
|
|
92
92
|
| "storage-location"
|
|
93
93
|
| "setups"
|
|
94
94
|
| "connections";
|
|
95
|
-
type Group = "content" | "behavior" | "storage" | "advanced" | "all";
|
|
96
|
-
type Screen = "categories" | Group;
|
|
97
|
-
const groups: Record<Group, { label: string; ids: readonly string[] }> = {
|
|
98
|
-
content: { label: "Sync content", ids: ["include", "remoteInclude", "localFields"] },
|
|
99
|
-
behavior: {
|
|
100
|
-
label: "Sync behavior",
|
|
101
|
-
ids: ["automatic", "automaticTransfer", "agentMode", "continuous", "onSwitch"],
|
|
102
|
-
},
|
|
103
|
-
storage: { label: "Storage & setups", ids: ["storageLocation", "setups", "connections"] },
|
|
104
|
-
advanced: { label: "Advanced", ids: ["mergeSettings", "contentPolicy", "skipSecretScan", "showStatus", "debug"] },
|
|
105
|
-
all: { label: "All settings (search)", ids: [] },
|
|
106
|
-
};
|
|
107
95
|
let saveStatus = "Simple choices save immediately. Back does not undo saved changes.";
|
|
108
96
|
// Keep Kit's existing settings adapter: native SettingsList has no async save/rollback
|
|
109
97
|
// contract or injected keybindings. Kit preserves these safety and accessibility behaviors.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
[
|
|
98
|
+
const menu = defineMenu<Awaited<ReturnType<typeof loadConfig>>, "settings", Action, ExtensionCommandContext>({
|
|
99
|
+
start: "settings",
|
|
100
|
+
screens: {
|
|
101
|
+
settings: ({ state }) => ({
|
|
102
|
+
kind: "settings",
|
|
103
|
+
title: "Pi Sync Settings",
|
|
104
|
+
lines: [
|
|
105
|
+
`Setup: ${safeTerminalText(state.setupName)} · Connection: ${safeTerminalText(state.connectionName)}`,
|
|
106
|
+
saveStatus,
|
|
107
|
+
],
|
|
108
|
+
items: [
|
|
121
109
|
{
|
|
122
110
|
id: "include",
|
|
123
111
|
label: "Included paths",
|
|
@@ -188,7 +176,7 @@ export async function showSyncSettings(
|
|
|
188
176
|
},
|
|
189
177
|
{
|
|
190
178
|
id: "agentMode",
|
|
191
|
-
label: "
|
|
179
|
+
label: "Autonomous sync",
|
|
192
180
|
description: AGENT_MODE_DESCRIPTION,
|
|
193
181
|
currentValue: state.agentMode ? "On" : "Off",
|
|
194
182
|
values: ["On", "Off"],
|
|
@@ -196,8 +184,8 @@ export async function showSyncSettings(
|
|
|
196
184
|
},
|
|
197
185
|
{
|
|
198
186
|
id: "continuous",
|
|
199
|
-
label: "Continuous sync
|
|
200
|
-
description: `${CONTINUOUS_DESCRIPTION}${state.continuous && !state.agentMode ? " Saved On, but paused until
|
|
187
|
+
label: "Continuous sync",
|
|
188
|
+
description: `${CONTINUOUS_DESCRIPTION}${state.continuous && !state.agentMode ? " Saved On, but paused until Autonomous sync is enabled." : ""}`,
|
|
201
189
|
currentValue: state.continuous ? "On" : "Off",
|
|
202
190
|
values: ["On", "Off"],
|
|
203
191
|
action: "continuous",
|
|
@@ -215,7 +203,7 @@ export async function showSyncSettings(
|
|
|
215
203
|
id: "mergeSettings",
|
|
216
204
|
label: "Merge settings fields",
|
|
217
205
|
description:
|
|
218
|
-
"This setup,
|
|
206
|
+
"This setup, opt-in: merge independent global settings.json root fields with a verified private ancestor. Arrays and nested objects remain atomic. Applies to subsequent sync; no reload.",
|
|
219
207
|
currentValue: state.mergeSettings ? "On" : "Off",
|
|
220
208
|
values: ["On", "Off"],
|
|
221
209
|
action: "merge-settings",
|
|
@@ -224,7 +212,7 @@ export async function showSyncSettings(
|
|
|
224
212
|
id: "contentPolicy",
|
|
225
213
|
label: "Content merge / partial progress",
|
|
226
214
|
description:
|
|
227
|
-
"This setup,
|
|
215
|
+
"This setup, opt-in: review the version-5 upgrade before saving. Merge bounded text and prefix-only sessions; partial progress keeps withheld versions and baselines. Applies to subsequent sync; disabling does not clear recovery evidence.",
|
|
228
216
|
currentValue: state.mergeContent
|
|
229
217
|
? state.partialSync
|
|
230
218
|
? "Merge + partial"
|
|
@@ -262,32 +250,8 @@ export async function showSyncSettings(
|
|
|
262
250
|
values: ["On", "Off"],
|
|
263
251
|
action: "debug",
|
|
264
252
|
},
|
|
265
|
-
] satisfies MenuSettingItem<Action>[]
|
|
266
|
-
).filter((item) => group === "all" || groups[group].ids.includes(item.id)),
|
|
267
|
-
}) as const;
|
|
268
|
-
const menu = defineMenu<Awaited<ReturnType<typeof loadConfig>>, Screen, Action, ExtensionCommandContext>({
|
|
269
|
-
start: "categories",
|
|
270
|
-
screens: {
|
|
271
|
-
categories: ({ state }) => ({
|
|
272
|
-
kind: "actions",
|
|
273
|
-
title: "Pi Sync Settings",
|
|
274
|
-
lines: [
|
|
275
|
-
`Setup: ${safeTerminalText(state.setupName)} · Connection: ${safeTerminalText(state.connectionName)}`,
|
|
276
|
-
"Choose a task, or search all settings. Scope is shown in each setting's description.",
|
|
277
|
-
saveStatus,
|
|
278
|
-
],
|
|
279
|
-
items: (Object.entries(groups) as [Group, (typeof groups)[Group]][]).map(([id, group]) => ({
|
|
280
|
-
id,
|
|
281
|
-
label: group.label,
|
|
282
|
-
to: id,
|
|
283
|
-
})),
|
|
284
|
-
hint: "back",
|
|
253
|
+
] satisfies MenuSettingItem<Action>[],
|
|
285
254
|
}),
|
|
286
|
-
content: ({ state }) => settingsScreen(state, "content"),
|
|
287
|
-
behavior: ({ state }) => settingsScreen(state, "behavior"),
|
|
288
|
-
storage: ({ state }) => settingsScreen(state, "storage"),
|
|
289
|
-
advanced: ({ state }) => settingsScreen(state, "advanced"),
|
|
290
|
-
all: ({ state }) => settingsScreen(state, "all"),
|
|
291
255
|
},
|
|
292
256
|
actions: {
|
|
293
257
|
"storage-location": async ({ signal: actionSignal }) => {
|
|
@@ -315,7 +279,7 @@ export async function showSyncSettings(
|
|
|
315
279
|
const partialSync = value === "Partial only" || value === "Merge + partial";
|
|
316
280
|
if (
|
|
317
281
|
!(await ctx.ui.confirm(
|
|
318
|
-
"Save
|
|
282
|
+
"Save content policy?",
|
|
319
283
|
"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.",
|
|
320
284
|
{ signal: mutationSignal },
|
|
321
285
|
))
|
|
@@ -450,7 +414,7 @@ export async function showSyncSettings(
|
|
|
450
414
|
try {
|
|
451
415
|
validate();
|
|
452
416
|
ctx.ui.notify(
|
|
453
|
-
"
|
|
417
|
+
"Autonomous sync saved. Applies to subsequent sync and the next session start; no transfer started.",
|
|
454
418
|
"info",
|
|
455
419
|
);
|
|
456
420
|
} catch {
|
|
@@ -129,8 +129,8 @@ export async function showSetupSwitcher(
|
|
|
129
129
|
`Included content: ${config.include.length} paths`,
|
|
130
130
|
`Automatic sync: ${automaticSyncSummary(config.automatic)} · Sessions: ${config.include.includes("sessions") ? "On" : "Off"}`,
|
|
131
131
|
`Automatic transfer at startup: ${config.automaticTransfer ? "On — may upload, replace, or delete selected files; no reload" : "Off"}`,
|
|
132
|
-
`
|
|
133
|
-
`Continuous sync: ${config.continuous ? (config.agentMode ? "On (
|
|
132
|
+
`Autonomous sync: ${config.agentMode ? "On (autonomous merged sync)" : "Off"}`,
|
|
133
|
+
`Continuous sync: ${config.continuous ? (config.agentMode ? "On (recurring requests and transfers)" : "Saved On; paused until Autonomous sync is enabled") : "Off"}`,
|
|
134
134
|
"",
|
|
135
135
|
switchEffect,
|
|
136
136
|
].join("\n"),
|
package/src/ui/sync-attention.ts
CHANGED
|
@@ -93,7 +93,9 @@ export function createSyncAttentionController(): SyncAttentionController {
|
|
|
93
93
|
}
|
|
94
94
|
if (!state && classification === "status") {
|
|
95
95
|
ctx.ui.setWidget(WIDGET_KEY, undefined);
|
|
96
|
-
setSyncStatus(ctx, observation?.inspection.remoteChanged ? "sync ⇣" : "sync ⇡"
|
|
96
|
+
setSyncStatus(ctx, observation?.inspection.remoteChanged ? "sync ⇣" : "sync ⇡", {
|
|
97
|
+
activity: observation?.inspection.remoteChanged ? "remote-change" : "local-change",
|
|
98
|
+
});
|
|
97
99
|
return;
|
|
98
100
|
}
|
|
99
101
|
const presentation = state
|
|
@@ -104,7 +106,7 @@ export function createSyncAttentionController(): SyncAttentionController {
|
|
|
104
106
|
};
|
|
105
107
|
if (ctx.mode !== "tui") {
|
|
106
108
|
ctx.ui.setWidget(WIDGET_KEY, undefined);
|
|
107
|
-
setSyncStatus(ctx, presentation.status);
|
|
109
|
+
setSyncStatus(ctx, presentation.status, { activity: "review" });
|
|
108
110
|
return;
|
|
109
111
|
}
|
|
110
112
|
// Keep Kit outside the eager startup graph; module loading owns no cancellable resources.
|
|
@@ -113,7 +115,7 @@ export function createSyncAttentionController(): SyncAttentionController {
|
|
|
113
115
|
throw error;
|
|
114
116
|
});
|
|
115
117
|
if (generation !== currentGeneration || signal?.aborted) return;
|
|
116
|
-
setSyncStatus(ctx, presentation.status);
|
|
118
|
+
setSyncStatus(ctx, presentation.status, { activity: "review" });
|
|
117
119
|
ctx.ui.setWidget(
|
|
118
120
|
WIDGET_KEY,
|
|
119
121
|
(_tui, theme) =>
|
package/src/ui/sync-setups-ui.ts
CHANGED
|
@@ -193,8 +193,8 @@ async function loadSetupMenuState(selectedName: string | undefined, signal?: Abo
|
|
|
193
193
|
`Sessions: ${selection.sessions ? "On — privacy-sensitive" : "Off"}`,
|
|
194
194
|
`Automatic sync: ${config.automatic ? "On" : "Off"}`,
|
|
195
195
|
`Automatic transfer at startup: ${config.automaticTransfer ? "On — may upload, replace, or delete selected files; no reload" : "Off"}`,
|
|
196
|
-
`
|
|
197
|
-
`Continuous sync: ${config.continuous ? (config.agentMode ? "On (
|
|
196
|
+
`Autonomous sync: ${config.agentMode ? "On (autonomous merged sync)" : "Off"}`,
|
|
197
|
+
`Continuous sync: ${config.continuous ? (config.agentMode ? "On (recurring requests and transfers)" : "Saved On; paused until Autonomous sync is enabled") : "Off"}`,
|
|
198
198
|
isCurrent
|
|
199
199
|
? "Edit included content and automatic sync in /sync → Settings."
|
|
200
200
|
: "To edit included content or automatic sync, make this setup current, then open /sync → Settings.",
|