@narumitw/pi-sync 0.57.5 → 0.57.7
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/README.md +1 -1
- package/dist/chunks/{chunk-UZPMDLTL.ts → chunk-M6VQJIOU.ts} +5 -5
- package/dist/chunks/chunk-M6VQJIOU.ts.map +7 -0
- package/dist/chunks/{chunk-R75IHAOJ.ts → chunk-N5TFMPB5.ts} +2 -2
- package/dist/chunks/{chunk-DT6TOCIG.ts → chunk-PQW6GS4J.ts} +2 -2
- package/dist/chunks/{conflict-review-T7GSOKNG.ts → conflict-review-UC5AGVER.ts} +3 -3
- package/dist/chunks/{continuous-sync-U4HIKPQK.ts → continuous-sync-POEQXR25.ts} +73 -46
- package/dist/chunks/continuous-sync-POEQXR25.ts.map +7 -0
- package/dist/chunks/{manager-ui-FXXO6TW4.ts → manager-ui-26LEINEX.ts} +2 -2
- package/dist/chunks/{manager-ui-FXXO6TW4.ts.map → manager-ui-26LEINEX.ts.map} +1 -1
- package/dist/chunks/{sync-inspection-5MY2HI4P.ts → sync-inspection-J25EEKZK.ts} +3 -3
- package/dist/chunks/{sync-operations-GNPS6BLY.ts → sync-operations-ZHA6HQDB.ts} +4 -4
- package/dist/index.ts +52 -18
- package/dist/index.ts.map +3 -3
- package/docs/continuous-sync-verification.md +10 -0
- package/package.json +2 -2
- package/skills/configure-pi-sync/references/configuration.md +8 -4
- package/src/sync/automatic-sync.ts +11 -6
- package/src/sync/continuous-sync.ts +78 -41
- package/src/sync/startup-check.ts +8 -3
- package/src/sync/startup-timings.ts +8 -4
- package/src/sync-extension.ts +18 -6
- package/src/ui/settings-ui.ts +1 -1
- package/src/ui/startup-timings-entry.ts +36 -0
- package/dist/chunks/chunk-UZPMDLTL.ts.map +0 -7
- package/dist/chunks/continuous-sync-U4HIKPQK.ts.map +0 -7
- /package/dist/chunks/{chunk-R75IHAOJ.ts.map → chunk-N5TFMPB5.ts.map} +0 -0
- /package/dist/chunks/{chunk-DT6TOCIG.ts.map → chunk-PQW6GS4J.ts.map} +0 -0
- /package/dist/chunks/{conflict-review-T7GSOKNG.ts.map → conflict-review-UC5AGVER.ts.map} +0 -0
- /package/dist/chunks/{sync-inspection-5MY2HI4P.ts.map → sync-inspection-J25EEKZK.ts.map} +0 -0
- /package/dist/chunks/{sync-operations-GNPS6BLY.ts.map → sync-operations-ZHA6HQDB.ts.map} +0 -0
|
@@ -58,6 +58,16 @@ Final verification:
|
|
|
58
58
|
| README heading audit | Required headings/order checked across all 36 active package READMEs. |
|
|
59
59
|
| Package build, RPC and archive | Passed as described below. |
|
|
60
60
|
|
|
61
|
+
## Startup timing report verification
|
|
62
|
+
|
|
63
|
+
Startup timing reports use native custom session entries in TUI and retain RPC notifications. Entry renderers and live append events were checked at the declared Pi peer floor. Tests verify transcript retention across renderer replacement and ordinary notifications, unchanged model context, control sanitization, and zero/narrow/resized rendering.
|
|
64
|
+
|
|
65
|
+
Continuous timing tests cover the initial check, nested transfer stages, failure, timeout, later polls, foreground pauses, idle resume, replacement, and shutdown. Debug is captured during awaited session initialization, including when Continuous sync is initially Off or no setup exists; both directions of a later debug edit retain the session-start value. Automatic sync and startup transfer remain Off when not authorized. No new watcher, timer, setting, command, provider request, or model-visible prefix transition is added.
|
|
66
|
+
|
|
67
|
+
The package-directory RPC smoke now waits for both local and initial Continuous timing notifications before making a synthetic edit. It verifies exactly two timing reports, conditional loopback publication, zero provider requests, and released ownership. The startup timing fix passed both root gates, the package pack workflow, archive inspection, generated-runtime tests, and the fenced-code-aware README heading audit.
|
|
68
|
+
|
|
69
|
+
Physical-terminal visual acceptance and production storage/provider behavior remain unverified by this fix. Automated native rendering and RPC tests do not replace that acceptance. Existing installed npm copies are not modified by a source checkout; the user must explicitly load the local build or install a later release. No package was published, tagged, or installed into the user's managed npm root.
|
|
70
|
+
|
|
61
71
|
## CLI, package and load checks
|
|
62
72
|
|
|
63
73
|
The required package build and dry-run pack use:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@narumitw/pi-sync",
|
|
3
|
-
"version": "0.57.
|
|
3
|
+
"version": "0.57.7",
|
|
4
4
|
"description": "Pi extension that syncs Pi configuration through Git, WebDAV, Cloudflare R2, or S3-compatible storage.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"typecheck": "tsc --noEmit"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@narumitw/pi-tui-kit": "^0.
|
|
48
|
+
"@narumitw/pi-tui-kit": "^0.68.0",
|
|
49
49
|
"fast-xml-parser": "^5.11.2",
|
|
50
50
|
"proper-lockfile": "^4.1.2",
|
|
51
51
|
"semver": "^7.8.5"
|
|
@@ -225,7 +225,10 @@ Edit `<getAgentDir()>/pi-sync.json` (normally `~/.pi/agent/pi-sync.json`) or use
|
|
|
225
225
|
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
226
|
There is no project or environment override.
|
|
227
227
|
|
|
228
|
-
With `debug: true`,
|
|
228
|
+
With `debug: true`, reports show local initialization after recovery and settings resolution, then background work when it ends.
|
|
229
|
+
TUI reports are stored as custom session entries, so they remain in the transcript after `/reload` and later notifications without entering model context.
|
|
230
|
+
RPC reports remain notifications.
|
|
231
|
+
When Continuous sync is enabled, the background report covers its first cycle rather than the separate one-shot startup check; later polls and same-session idle resumes do not publish more timing reports.
|
|
229
232
|
Stages include transaction recovery, settings reads, inspection import, backend initialization, local snapshot, sync state read, remote head, operations import, and automatic transfer when those stages run.
|
|
230
233
|
Automatic-transfer details separate authorization, journal recovery, state and local reads, remote-head reads, snapshot downloads, merge planning, backup, publication, local apply, baseline writes, and cleanup.
|
|
231
234
|
Repeated stages aggregate elapsed time and show a call count.
|
|
@@ -237,10 +240,11 @@ Recovery failures before valid settings can be read cannot enable a timing repor
|
|
|
237
240
|
|
|
238
241
|
Interrupted stages are marked `interrupted`; cancelled or failed background work is marked `stopped`.
|
|
239
242
|
Session replacement and shutdown suppress late reports from the old session.
|
|
243
|
+
Pausing Continuous sync cancels its active cycle and suppresses that cycle’s late report; it does not schedule another startup report on idle resume.
|
|
240
244
|
Labels and durations contain no credentials, paths, file contents, remote addresses, or error text.
|
|
241
|
-
No log file or model message is created.
|
|
242
|
-
Print/JSON produce no timing
|
|
243
|
-
The setting does not enable automatic sync or transfer, change safety checks, or
|
|
245
|
+
No separate log file or model message is created; TUI reports remain in session history even after debugging is disabled.
|
|
246
|
+
Print/JSON produce no timing reports.
|
|
247
|
+
The setting does not enable automatic sync or transfer, change safety checks, or report manual/shutdown operations.
|
|
244
248
|
Set `debug` to `false` or remove it, then start a new session to disable reports.
|
|
245
249
|
|
|
246
250
|
### Secret scanning
|
|
@@ -5,6 +5,7 @@ import { loadConfig, loadPartialConfig } from "../settings/config.js";
|
|
|
5
5
|
import { isMissingConfigError } from "../settings/config-errors.js";
|
|
6
6
|
import { consumeLocalConfigMigrationNotice } from "../settings/config-file.js";
|
|
7
7
|
import { readLocalConfigObject } from "../settings/settings-store.js";
|
|
8
|
+
import type { AnySyncConfig } from "../settings/settings-types.js";
|
|
8
9
|
import { sessionDirFromContext, snapshotOptionsForContext } from "../snapshot/session-paths.js";
|
|
9
10
|
import { recoverSnapshotTransactionsOnStartup } from "../snapshot/snapshot-transaction.js";
|
|
10
11
|
import { withLock } from "../state/lock.js";
|
|
@@ -12,9 +13,8 @@ import { stateDirectoryMigrationNotice } from "../state/state-directory.js";
|
|
|
12
13
|
import { ensureStateDir, readStateForConfig } from "../state/sync-state-store.js";
|
|
13
14
|
import { configureSyncStatus, setSyncStatus } from "../ui/sync-status.js";
|
|
14
15
|
import { safeTerminalText } from "../ui/terminal-text.js";
|
|
15
|
-
import { startupTransferEnabled } from "./agent-policy.js";
|
|
16
16
|
import { throwIfAborted } from "./signals.js";
|
|
17
|
-
import { createStartupTimings } from "./startup-timings.js";
|
|
17
|
+
import { createStartupTimings, type StartupTimingPublisher } from "./startup-timings.js";
|
|
18
18
|
import { errorMessage } from "./sync-errors.js";
|
|
19
19
|
import type { SyncLoaders } from "./sync-loaders.js";
|
|
20
20
|
import { captureMutationOwner } from "./sync-local.js";
|
|
@@ -29,8 +29,12 @@ const AUTO_SYNC_OPTIONS: CommandOptions = {
|
|
|
29
29
|
args: [],
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
export async function startSession(
|
|
33
|
-
|
|
32
|
+
export async function startSession(
|
|
33
|
+
ctx: ExtensionContext,
|
|
34
|
+
signal: AbortSignal,
|
|
35
|
+
publish?: StartupTimingPublisher,
|
|
36
|
+
): Promise<{ debug: boolean; config?: AnySyncConfig } | undefined> {
|
|
37
|
+
const timings = createStartupTimings(publish);
|
|
34
38
|
let debug = false;
|
|
35
39
|
let outcome: "completed" | "stopped" = "stopped";
|
|
36
40
|
try {
|
|
@@ -64,19 +68,20 @@ export async function startSession(ctx: ExtensionContext, signal: AbortSignal) {
|
|
|
64
68
|
const migrationNotice = consumeLocalConfigMigrationNotice();
|
|
65
69
|
if (migrationNotice && ctx.hasUI) ctx.ui.notify(migrationNotice, "warning");
|
|
66
70
|
throwIfAborted(signal);
|
|
67
|
-
if (!ctx.hasUI) return;
|
|
71
|
+
if (!ctx.hasUI) return { debug };
|
|
68
72
|
try {
|
|
69
73
|
const config = await timings.measure("config resolution", () => loadConfig());
|
|
70
74
|
throwIfAborted(signal);
|
|
71
75
|
configureSyncStatus(ctx, config.showStatus);
|
|
72
76
|
outcome = "completed";
|
|
73
|
-
return
|
|
77
|
+
return { debug, config };
|
|
74
78
|
} catch (error) {
|
|
75
79
|
throwIfAborted(signal);
|
|
76
80
|
if (!isMissingConfigError(error)) {
|
|
77
81
|
ctx.ui.notify(`pi-sync startup check skipped: ${safeTerminalText(errorMessage(error))}`, "warning");
|
|
78
82
|
}
|
|
79
83
|
}
|
|
84
|
+
return { debug };
|
|
80
85
|
} finally {
|
|
81
86
|
timings.publish(ctx, debug, signal, "local initialization", outcome);
|
|
82
87
|
}
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
import { continuousBinding, hashContinuous } from "./continuous-storage.js";
|
|
19
19
|
import { watchContinuous } from "./continuous-watch.js";
|
|
20
20
|
import { readMergeJournal } from "./merge-journal.js";
|
|
21
|
+
import { createStartupTimings, type StartupTimingPublisher, type StartupTimings } from "./startup-timings.js";
|
|
21
22
|
import type { SyncLoaders } from "./sync-loaders.js";
|
|
22
23
|
import { captureMutationOwner } from "./sync-local.js";
|
|
23
24
|
import { fileHashMap } from "./sync-state.js";
|
|
@@ -43,18 +44,34 @@ const LOCAL_CHECK_MS = 5_000;
|
|
|
43
44
|
const POLL_MS = 60_000;
|
|
44
45
|
const DEBOUNCE_MS = 1_000;
|
|
45
46
|
|
|
47
|
+
interface ContinuousStartup {
|
|
48
|
+
signal: AbortSignal;
|
|
49
|
+
debug: boolean;
|
|
50
|
+
reported: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
46
53
|
/** Created on first enabled session. No module/factory-owned timers or watchers. */
|
|
47
|
-
export function createContinuousSync(
|
|
54
|
+
export function createContinuousSync(
|
|
55
|
+
loaders: SyncLoaders,
|
|
56
|
+
clock: ContinuousClock = systemClock,
|
|
57
|
+
publish?: StartupTimingPublisher,
|
|
58
|
+
) {
|
|
59
|
+
const startups = new WeakMap<ExtensionContext["sessionManager"], ContinuousStartup>();
|
|
48
60
|
const repairNotices = new WeakSet<ExtensionContext["sessionManager"]>();
|
|
49
61
|
let task:
|
|
50
62
|
| { owner: ExtensionContext["sessionManager"]; controller: AbortController; promise: Promise<void>; clear(): void }
|
|
51
63
|
| undefined;
|
|
52
64
|
return {
|
|
53
|
-
start(ctx: ExtensionContext, sessionSignal: AbortSignal, explicitRetry = false) {
|
|
65
|
+
start(ctx: ExtensionContext, sessionSignal: AbortSignal, explicitRetry = false, debug = false) {
|
|
54
66
|
if (!ctx.hasUI || (ctx.mode !== "tui" && ctx.mode !== "rpc") || sessionSignal.aborted || task) return;
|
|
55
67
|
const controller = new AbortController();
|
|
56
68
|
const signal = AbortSignal.any([sessionSignal, controller.signal]);
|
|
57
69
|
const validateContext = captureMutationOwner(ctx);
|
|
70
|
+
// The caller supplies the session-start debug value, including first activation after settings edits.
|
|
71
|
+
const previousStartup = startups.get(ctx.sessionManager);
|
|
72
|
+
const startup: ContinuousStartup =
|
|
73
|
+
previousStartup?.signal === sessionSignal ? previousStartup : { signal: sessionSignal, debug, reported: false };
|
|
74
|
+
startups.set(ctx.sessionManager, startup);
|
|
58
75
|
const current = {
|
|
59
76
|
owner: ctx.sessionManager,
|
|
60
77
|
controller,
|
|
@@ -73,7 +90,7 @@ export function createContinuousSync(loaders: SyncLoaders, clock: ContinuousCloc
|
|
|
73
90
|
let explicit = explicitRetry;
|
|
74
91
|
while (!signal.aborted) {
|
|
75
92
|
try {
|
|
76
|
-
await run(ctx, signal, explicit);
|
|
93
|
+
await run(ctx, signal, explicit, startup);
|
|
77
94
|
return;
|
|
78
95
|
} catch {
|
|
79
96
|
if (signal.aborted) return;
|
|
@@ -113,7 +130,7 @@ export function createContinuousSync(loaders: SyncLoaders, clock: ContinuousCloc
|
|
|
113
130
|
},
|
|
114
131
|
};
|
|
115
132
|
|
|
116
|
-
async function run(ctx: ExtensionContext, signal: AbortSignal, explicitRetry: boolean) {
|
|
133
|
+
async function run(ctx: ExtensionContext, signal: AbortSignal, explicitRetry: boolean, startup: ContinuousStartup) {
|
|
117
134
|
const validateOwner = captureMutationOwner(ctx, signal);
|
|
118
135
|
try {
|
|
119
136
|
while (!signal.aborted) {
|
|
@@ -134,7 +151,7 @@ export function createContinuousSync(loaders: SyncLoaders, clock: ContinuousCloc
|
|
|
134
151
|
validateOwner();
|
|
135
152
|
});
|
|
136
153
|
if (lease) {
|
|
137
|
-
await monitor(ctx, config, binding, lease, signal, explicitRetry);
|
|
154
|
+
await monitor(ctx, config, binding, lease, signal, explicitRetry, startup);
|
|
138
155
|
explicitRetry = false;
|
|
139
156
|
} else setSyncStatus(ctx, "sync waiting: another local owner");
|
|
140
157
|
} finally {
|
|
@@ -172,6 +189,7 @@ export function createContinuousSync(loaders: SyncLoaders, clock: ContinuousCloc
|
|
|
172
189
|
lease: ContinuousLease,
|
|
173
190
|
parent: AbortSignal,
|
|
174
191
|
explicitRetry: boolean,
|
|
192
|
+
startup: ContinuousStartup,
|
|
175
193
|
) {
|
|
176
194
|
const signal = AbortSignal.any([parent, lease.signal]);
|
|
177
195
|
const validateOwner = captureMutationOwner(ctx, signal);
|
|
@@ -230,6 +248,10 @@ export function createContinuousSync(loaders: SyncLoaders, clock: ContinuousCloc
|
|
|
230
248
|
const hintDue = dirtyAt === undefined ? Number.POSITIVE_INFINITY : dirtyAt + DEBOUNCE_MS;
|
|
231
249
|
const due = Math.max(record.error === "budget" ? 0 : record.next, Math.min(pollDue, hintDue));
|
|
232
250
|
if (now >= due) {
|
|
251
|
+
const timings = createStartupTimings(publish);
|
|
252
|
+
const report = !startup.reported && startup.debug;
|
|
253
|
+
startup.reported = true;
|
|
254
|
+
let outcome: "completed" | "stopped" = "stopped";
|
|
233
255
|
const force = explicitRetry || controlChanged;
|
|
234
256
|
explicitRetry = false;
|
|
235
257
|
const expectedModel = hashContinuous(ctx.model);
|
|
@@ -277,7 +299,9 @@ export function createContinuousSync(loaders: SyncLoaders, clock: ContinuousCloc
|
|
|
277
299
|
validateCycle,
|
|
278
300
|
configuration,
|
|
279
301
|
force,
|
|
302
|
+
timings,
|
|
280
303
|
);
|
|
304
|
+
outcome = "completed";
|
|
281
305
|
} catch (error) {
|
|
282
306
|
if (signal.aborted || !ctx.isIdle()) return;
|
|
283
307
|
// A settings hint interrupts the old authorization; load it afresh, not as a failed transfer.
|
|
@@ -313,6 +337,7 @@ export function createContinuousSync(loaders: SyncLoaders, clock: ContinuousCloc
|
|
|
313
337
|
guard.abort();
|
|
314
338
|
await guardTask;
|
|
315
339
|
cycle = undefined;
|
|
340
|
+
timings.publish(ctx, report, signal, "continuous startup", outcome);
|
|
316
341
|
}
|
|
317
342
|
pollDue = clock.now() + POLL_MS + Math.floor(clock.random() * 6_000);
|
|
318
343
|
closeWatch();
|
|
@@ -356,6 +381,7 @@ export function createContinuousSync(loaders: SyncLoaders, clock: ContinuousCloc
|
|
|
356
381
|
validateOwner: (config?: AnySyncConfig) => void,
|
|
357
382
|
configuration: string,
|
|
358
383
|
force: boolean,
|
|
384
|
+
timings: StartupTimings,
|
|
359
385
|
) {
|
|
360
386
|
const validate = (latest?: AnySyncConfig) => {
|
|
361
387
|
signal.throwIfAborted();
|
|
@@ -375,27 +401,35 @@ export function createContinuousSync(loaders: SyncLoaders, clock: ContinuousCloc
|
|
|
375
401
|
)
|
|
376
402
|
return;
|
|
377
403
|
setSyncStatus(ctx, "sync checking");
|
|
378
|
-
const inspectionModule = await loaders.inspection();
|
|
404
|
+
const inspectionModule = await timings.measure("inspection import", () => loaders.inspection());
|
|
379
405
|
validate();
|
|
380
|
-
const observed = await
|
|
381
|
-
|
|
382
|
-
const inspection = await inspectionModule.inspectSync(config, snapshotOptionsForContext(ctx, config), signal);
|
|
383
|
-
validate();
|
|
384
|
-
let localIdentity = inspection.localIdentity;
|
|
385
|
-
if (!localIdentity) {
|
|
386
|
-
const snapshotModule = await loaders.snapshot();
|
|
406
|
+
const observed = await timings.measure("inspection and guards", () =>
|
|
407
|
+
guardedLock("continuous-check", async () => {
|
|
387
408
|
validate();
|
|
388
|
-
const
|
|
389
|
-
|
|
409
|
+
const inspection = await inspectionModule.inspectSync(
|
|
410
|
+
config,
|
|
411
|
+
snapshotOptionsForContext(ctx, config),
|
|
390
412
|
signal,
|
|
391
|
-
|
|
413
|
+
undefined,
|
|
414
|
+
timings,
|
|
415
|
+
);
|
|
392
416
|
validate();
|
|
393
|
-
localIdentity =
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
417
|
+
let localIdentity = inspection.localIdentity;
|
|
418
|
+
if (!localIdentity) {
|
|
419
|
+
const snapshotModule = await loaders.snapshot();
|
|
420
|
+
validate();
|
|
421
|
+
const local = await snapshotModule.createSnapshot(config.snapshotIdentity, {
|
|
422
|
+
...snapshotOptionsForContext(ctx, config),
|
|
423
|
+
signal,
|
|
424
|
+
});
|
|
425
|
+
validate();
|
|
426
|
+
localIdentity = hashContinuous(fileHashMap(local));
|
|
427
|
+
}
|
|
428
|
+
const journal = await readMergeJournal(config);
|
|
429
|
+
validate();
|
|
430
|
+
return { inspection, journal, localIdentity };
|
|
431
|
+
}),
|
|
432
|
+
);
|
|
399
433
|
validate();
|
|
400
434
|
const evidence = hashContinuous([
|
|
401
435
|
configuration,
|
|
@@ -434,28 +468,31 @@ export function createContinuousSync(loaders: SyncLoaders, clock: ContinuousCloc
|
|
|
434
468
|
throw new ContinuousError("unsafe", "Continuous sync needs an accepted baseline and matching remote selection.");
|
|
435
469
|
let transferred = false;
|
|
436
470
|
if (observed.journal || observed.inspection.localChanged || observed.inspection.remoteChanged) {
|
|
437
|
-
const operations = await loaders.operations();
|
|
471
|
+
const operations = await timings.measure("operations import", () => loaders.operations());
|
|
438
472
|
validate();
|
|
439
473
|
setSyncStatus(ctx, "sync syncing");
|
|
440
|
-
const result = await
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
474
|
+
const result = await timings.measure("automatic transfer", () =>
|
|
475
|
+
withStateDirectoryAccess(() =>
|
|
476
|
+
withAgentSyncLock(
|
|
477
|
+
ctx,
|
|
478
|
+
{
|
|
479
|
+
args: [],
|
|
480
|
+
yes: true,
|
|
481
|
+
force: false,
|
|
482
|
+
stale: false,
|
|
483
|
+
silent: true,
|
|
484
|
+
reload: false,
|
|
485
|
+
auto: true,
|
|
486
|
+
signal,
|
|
487
|
+
startupTimings: timings,
|
|
488
|
+
background: {
|
|
489
|
+
validate,
|
|
490
|
+
reserve: (key, requestSignal) =>
|
|
491
|
+
reserveContinuousRequest(binding, lease, key, () => clock.now(), requestSignal),
|
|
492
|
+
},
|
|
456
493
|
},
|
|
457
|
-
|
|
458
|
-
|
|
494
|
+
(options) => operations.syncBoth(ctx, options),
|
|
495
|
+
),
|
|
459
496
|
),
|
|
460
497
|
);
|
|
461
498
|
validate();
|
|
@@ -12,12 +12,17 @@ import { safeTerminalText } from "../ui/terminal-text.js";
|
|
|
12
12
|
import { startupTransferEnabled } from "./agent-policy.js";
|
|
13
13
|
import { withAgentSyncLock } from "./agent-sync.js";
|
|
14
14
|
import { combineSignals, throwIfAborted } from "./signals.js";
|
|
15
|
-
import { createStartupTimings } from "./startup-timings.js";
|
|
15
|
+
import { createStartupTimings, type StartupTimingPublisher } from "./startup-timings.js";
|
|
16
16
|
import { errorMessage } from "./sync-errors.js";
|
|
17
17
|
import type { SyncLoaders } from "./sync-loaders.js";
|
|
18
18
|
|
|
19
19
|
/** One owned task per extension session, never keyed by the shared headless UI. */
|
|
20
|
-
export function createStartupCheck(
|
|
20
|
+
export function createStartupCheck(
|
|
21
|
+
loaders: SyncLoaders,
|
|
22
|
+
attention: SyncAttentionController,
|
|
23
|
+
timeoutMs = 30_000,
|
|
24
|
+
publish?: StartupTimingPublisher,
|
|
25
|
+
) {
|
|
21
26
|
let active:
|
|
22
27
|
| {
|
|
23
28
|
controller: AbortController;
|
|
@@ -49,7 +54,7 @@ export function createStartupCheck(loaders: SyncLoaders, attention: SyncAttentio
|
|
|
49
54
|
return;
|
|
50
55
|
}
|
|
51
56
|
if (active) throw new Error("Previous startup check has not been drained.");
|
|
52
|
-
const timings = createStartupTimings();
|
|
57
|
+
const timings = createStartupTimings(publish);
|
|
53
58
|
const controller = new AbortController();
|
|
54
59
|
const signal = combineSignals(sessionSignal, controller.signal);
|
|
55
60
|
const task = {
|
|
@@ -33,8 +33,12 @@ type StartupStage =
|
|
|
33
33
|
| "transfer baseline write"
|
|
34
34
|
| "transfer cleanup";
|
|
35
35
|
|
|
36
|
+
export type StartupTimingPublisher = (ctx: ExtensionContext, report: string) => void;
|
|
37
|
+
|
|
36
38
|
/** Fixed labels and durations only: never retain errors, paths, settings, or payloads. */
|
|
37
|
-
export function createStartupTimings(
|
|
39
|
+
export function createStartupTimings(
|
|
40
|
+
publishReport: StartupTimingPublisher = (ctx, report) => ctx.ui.notify(report, "info"),
|
|
41
|
+
) {
|
|
38
42
|
const started = performance.now();
|
|
39
43
|
const stages = new Map<StartupStage, { ms: number; calls: number; interrupted: boolean }>();
|
|
40
44
|
let unchanged = false;
|
|
@@ -62,12 +66,13 @@ export function createStartupTimings() {
|
|
|
62
66
|
ctx: ExtensionContext,
|
|
63
67
|
enabled: boolean,
|
|
64
68
|
sessionSignal: AbortSignal,
|
|
65
|
-
scope: "local initialization" | "background startup",
|
|
69
|
+
scope: "local initialization" | "background startup" | "continuous startup",
|
|
66
70
|
outcome: "completed" | "stopped" = "completed",
|
|
67
71
|
) {
|
|
68
72
|
if (!enabled || !ctx.hasUI || sessionSignal.aborted) return;
|
|
69
73
|
try {
|
|
70
|
-
|
|
74
|
+
publishReport(
|
|
75
|
+
ctx,
|
|
71
76
|
[
|
|
72
77
|
`pi-sync debug: ${scope} (${outcome})`,
|
|
73
78
|
...Array.from(
|
|
@@ -78,7 +83,6 @@ export function createStartupTimings() {
|
|
|
78
83
|
...(unchanged ? ["transfer result: unchanged (fast path)"] : []),
|
|
79
84
|
`total: ${(performance.now() - started).toFixed(1)} ms`,
|
|
80
85
|
].join("\n"),
|
|
81
|
-
"info",
|
|
82
86
|
);
|
|
83
87
|
} catch {
|
|
84
88
|
// Diagnostics must not change sync behavior if the UI is unavailable during teardown.
|
package/src/sync-extension.ts
CHANGED
|
@@ -3,11 +3,13 @@ import { completeSyncArguments, splitArgs } from "./commands/command.js";
|
|
|
3
3
|
import { handleCommand } from "./commands/command-handler.js";
|
|
4
4
|
import type { AnySyncConfig } from "./settings/settings-types.js";
|
|
5
5
|
import { withStateDirectoryAccess } from "./state/state-directory.js";
|
|
6
|
+
import { startupTransferEnabled } from "./sync/agent-policy.js";
|
|
6
7
|
import { autoPushSessions, startSession } from "./sync/automatic-sync.js";
|
|
7
8
|
import { combineSignals } from "./sync/signals.js";
|
|
8
9
|
import { createStartupCheck } from "./sync/startup-check.js";
|
|
9
10
|
import { errorMessage } from "./sync/sync-errors.js";
|
|
10
11
|
import { createSyncLoaders, type SyncDependencies } from "./sync/sync-loaders.js";
|
|
12
|
+
import { registerStartupTimings } from "./ui/startup-timings-entry.js";
|
|
11
13
|
import { createSyncAttentionController } from "./ui/sync-attention.js";
|
|
12
14
|
import { setSyncStatus } from "./ui/sync-status.js";
|
|
13
15
|
import { safeTerminalText } from "./ui/terminal-text.js";
|
|
@@ -15,13 +17,15 @@ import { safeTerminalText } from "./ui/terminal-text.js";
|
|
|
15
17
|
export default function sync(pi: ExtensionAPI, dependencies: Partial<SyncDependencies> = {}) {
|
|
16
18
|
const loaders = createSyncLoaders(dependencies);
|
|
17
19
|
const attention = createSyncAttentionController();
|
|
18
|
-
const
|
|
20
|
+
const publishTimings = registerStartupTimings(pi);
|
|
21
|
+
const check = createStartupCheck(loaders, attention, 30_000, publishTimings);
|
|
19
22
|
let sessionAbort = new AbortController();
|
|
20
23
|
let shutdownAbort: AbortController | undefined;
|
|
21
24
|
let initialization = Promise.resolve<AnySyncConfig | false | undefined>(undefined);
|
|
22
25
|
let continuous: ReturnType<typeof import("./sync/continuous-sync.js")["createContinuousSync"]> | undefined;
|
|
23
26
|
let owner: ExtensionContext["sessionManager"] | undefined;
|
|
24
27
|
let continuousPossible = false;
|
|
28
|
+
let startupDebug = false;
|
|
25
29
|
const foreground = new Map<ExtensionContext["sessionManager"], number>();
|
|
26
30
|
async function resumeContinuous(ctx: ExtensionContext, signal: AbortSignal, retry?: { setup?: string }) {
|
|
27
31
|
const isCurrent = () => !signal.aborted && !foreground.has(ctx.sessionManager) && owner === ctx.sessionManager;
|
|
@@ -38,8 +42,13 @@ export default function sync(pi: ExtensionAPI, dependencies: Partial<SyncDepende
|
|
|
38
42
|
}
|
|
39
43
|
const { createContinuousSync } = await import("./sync/continuous-sync.js");
|
|
40
44
|
if (!isCurrent()) return;
|
|
41
|
-
continuous ??= createContinuousSync(loaders);
|
|
42
|
-
continuous.start(
|
|
45
|
+
continuous ??= createContinuousSync(loaders, undefined, publishTimings);
|
|
46
|
+
continuous.start(
|
|
47
|
+
ctx,
|
|
48
|
+
signal,
|
|
49
|
+
Boolean(retry && (retry.setup === undefined || retry.setup === config.setupName)),
|
|
50
|
+
startupDebug,
|
|
51
|
+
);
|
|
43
52
|
} catch {
|
|
44
53
|
/* Existing initialization/command paths report settings errors. */
|
|
45
54
|
}
|
|
@@ -97,6 +106,7 @@ export default function sync(pi: ExtensionAPI, dependencies: Partial<SyncDepende
|
|
|
97
106
|
const signal = sessionAbort.signal;
|
|
98
107
|
owner = ctx.sessionManager;
|
|
99
108
|
continuousPossible = false;
|
|
109
|
+
startupDebug = false;
|
|
100
110
|
attention.reset(ctx);
|
|
101
111
|
const previous = initialization;
|
|
102
112
|
initialization = (async () => {
|
|
@@ -107,8 +117,10 @@ export default function sync(pi: ExtensionAPI, dependencies: Partial<SyncDepende
|
|
|
107
117
|
await check.stop();
|
|
108
118
|
if (signal.aborted) return false;
|
|
109
119
|
try {
|
|
110
|
-
const
|
|
111
|
-
|
|
120
|
+
const startup = await withStateDirectoryAccess(() => startSession(ctx, signal, publishTimings));
|
|
121
|
+
if (signal.aborted) return false;
|
|
122
|
+
startupDebug = startup?.debug ?? false;
|
|
123
|
+
return startup?.config;
|
|
112
124
|
} catch (error) {
|
|
113
125
|
if (!signal.aborted && ctx.hasUI) {
|
|
114
126
|
ctx.ui.notify(`pi-sync recovery required: ${safeTerminalText(errorMessage(error))}`, "error");
|
|
@@ -119,7 +131,7 @@ export default function sync(pi: ExtensionAPI, dependencies: Partial<SyncDepende
|
|
|
119
131
|
const ready = await initialization;
|
|
120
132
|
if (ready && !signal.aborted) {
|
|
121
133
|
if (ready.continuous && ready.agentMode) await resumeContinuous(ctx, signal);
|
|
122
|
-
else check.start(ctx, signal, ready);
|
|
134
|
+
else if (ready.automatic || startupTransferEnabled(ready)) check.start(ctx, signal, ready);
|
|
123
135
|
}
|
|
124
136
|
});
|
|
125
137
|
|
package/src/ui/settings-ui.ts
CHANGED
|
@@ -256,7 +256,7 @@ export async function showSyncSettings(
|
|
|
256
256
|
id: "debug",
|
|
257
257
|
label: "Debug startup timings",
|
|
258
258
|
description:
|
|
259
|
-
"All setups on this device: show startup durations in TUI
|
|
259
|
+
"All setups on this device: show startup durations in the TUI transcript or RPC notifications, including initial Continuous sync. Applies next session start; no credentials or file contents.",
|
|
260
260
|
currentValue: state.debug ? "On" : "Off",
|
|
261
261
|
values: ["On", "Off"],
|
|
262
262
|
action: "debug",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Text, truncateToWidth } from "@earendil-works/pi-tui";
|
|
3
|
+
import type { StartupTimingPublisher } from "../sync/startup-timings.js";
|
|
4
|
+
import { safeTerminalText } from "./terminal-text.js";
|
|
5
|
+
|
|
6
|
+
export const STARTUP_TIMING_ENTRY = "pi-sync-startup-timings";
|
|
7
|
+
|
|
8
|
+
export interface StartupTimingEntry {
|
|
9
|
+
version: 1;
|
|
10
|
+
report: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function registerStartupTimings(pi: ExtensionAPI): StartupTimingPublisher {
|
|
14
|
+
pi.registerEntryRenderer<StartupTimingEntry>(STARTUP_TIMING_ENTRY, (entry, _options, theme) => {
|
|
15
|
+
const data = entry.data;
|
|
16
|
+
if (data?.version !== 1 || typeof data.report !== "string") return undefined;
|
|
17
|
+
const text = new Text(
|
|
18
|
+
data.report
|
|
19
|
+
.split("\n")
|
|
20
|
+
.map((line, index) => theme.fg(index === 0 ? "accent" : "muted", safeTerminalText(line)))
|
|
21
|
+
.join("\n"),
|
|
22
|
+
0,
|
|
23
|
+
0,
|
|
24
|
+
);
|
|
25
|
+
return {
|
|
26
|
+
render: (width) => (width > 0 ? text.render(width).map((line) => truncateToWidth(line, width)) : []),
|
|
27
|
+
invalidate: () => text.invalidate(),
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
return (ctx, report) => {
|
|
31
|
+
// Pi coalesces consecutive info notifications, including the final /reload notice.
|
|
32
|
+
// Custom entries remain visible without adding messages to model context.
|
|
33
|
+
if (ctx.mode === "tui") pi.appendEntry<StartupTimingEntry>(STARTUP_TIMING_ENTRY, { version: 1, report });
|
|
34
|
+
else ctx.ui.notify(report, "info");
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/sync/startup-timings.ts"],
|
|
4
|
-
"sourcesContent": ["import { performance } from \"node:perf_hooks\";\nimport type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\n\ntype StartupStage =\n | \"transaction recovery\"\n | \"settings read\"\n | \"config resolution\"\n | \"inspection import\"\n | \"inspection and guards\"\n | \"backend initialization\"\n | \"local snapshot\"\n | \"sync state read\"\n | \"remote head\"\n | \"operations import\"\n | \"automatic transfer\"\n | \"transfer authorization\"\n | \"transfer backend initialization\"\n | \"transfer journal read\"\n | \"transfer journal recovery\"\n | \"transfer state read\"\n | \"transfer local snapshot\"\n | \"transfer remote head\"\n | \"transfer snapshot download\"\n | \"transfer unchanged verification\"\n | \"transfer merge planning\"\n | \"transfer settings merge\"\n | \"transfer content merge\"\n | \"transfer preflight\"\n | \"transfer backup\"\n | \"transfer journal write\"\n | \"transfer remote publication\"\n | \"transfer local apply\"\n | \"transfer baseline write\"\n | \"transfer cleanup\";\n\n/** Fixed labels and durations only: never retain errors, paths, settings, or payloads. */\nexport function createStartupTimings() {\n const started = performance.now();\n const stages = new Map<StartupStage, { ms: number; calls: number; interrupted: boolean }>();\n let unchanged = false;\n return {\n markUnchanged() {\n unchanged = true;\n },\n async measure<T>(stage: StartupStage, run: () => T | Promise<T>): Promise<T> {\n const start = performance.now();\n let completed = false;\n try {\n const result = await run();\n completed = true;\n return result;\n } finally {\n const previous = stages.get(stage);\n stages.set(stage, {\n ms: (previous?.ms ?? 0) + performance.now() - start,\n calls: (previous?.calls ?? 0) + 1,\n interrupted: Boolean(previous?.interrupted || !completed),\n });\n }\n },\n publish(\n ctx: ExtensionContext,\n enabled: boolean,\n sessionSignal: AbortSignal,\n scope: \"local initialization\" | \"background startup\",\n outcome: \"completed\" | \"stopped\" = \"completed\",\n ) {\n if (!enabled || !ctx.hasUI || sessionSignal.aborted) return;\n try {\n ctx.ui.notify(\n [\n `pi-sync debug: ${scope} (${outcome})`,\n ...Array.from(\n stages,\n ([stage, value]) =>\n `${stage}: ${value.ms.toFixed(1)} ms${value.calls > 1 ? ` (${value.calls} calls)` : \"\"}${value.interrupted ? \" (interrupted)\" : \"\"}`,\n ),\n ...(unchanged ? [\"transfer result: unchanged (fast path)\"] : []),\n `total: ${(performance.now() - started).toFixed(1)} ms`,\n ].join(\"\\n\"),\n \"info\",\n );\n } catch {\n // Diagnostics must not change sync behavior if the UI is unavailable during teardown.\n }\n },\n };\n}\n\nexport type StartupTimings = ReturnType<typeof createStartupTimings>;\n"],
|
|
5
|
-
"mappings": ";;;;AAAA,SAAS,mBAAmB;AAoCrB,SAAS,uBAAuB;AACrC,QAAM,UAAU,YAAY,IAAI;AAChC,QAAM,SAAS,oBAAI,IAAuE;AAC1F,MAAI,YAAY;AAChB,SAAO;AAAA,IACL,gBAAgB;AACd,kBAAY;AAAA,IACd;AAAA,IACA,MAAM,QAAW,OAAqB,KAAuC;AAC3E,YAAM,QAAQ,YAAY,IAAI;AAC9B,UAAI,YAAY;AAChB,UAAI;AACF,cAAM,SAAS,MAAM,IAAI;AACzB,oBAAY;AACZ,eAAO;AAAA,MACT,UAAE;AACA,cAAM,WAAW,OAAO,IAAI,KAAK;AACjC,eAAO,IAAI,OAAO;AAAA,UAChB,KAAK,UAAU,MAAM,KAAK,YAAY,IAAI,IAAI;AAAA,UAC9C,QAAQ,UAAU,SAAS,KAAK;AAAA,UAChC,aAAa,QAAQ,UAAU,eAAe,CAAC,SAAS;AAAA,QAC1D,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,QACE,KACA,SACA,eACA,OACA,UAAmC,aACnC;AACA,UAAI,CAAC,WAAW,CAAC,IAAI,SAAS,cAAc,QAAS;AACrD,UAAI;AACF,YAAI,GAAG;AAAA,UACL;AAAA,YACE,kBAAkB,KAAK,KAAK,OAAO;AAAA,YACnC,GAAG,MAAM;AAAA,cACP;AAAA,cACA,CAAC,CAAC,OAAO,KAAK,MACZ,GAAG,KAAK,KAAK,MAAM,GAAG,QAAQ,CAAC,CAAC,MAAM,MAAM,QAAQ,IAAI,KAAK,MAAM,KAAK,YAAY,EAAE,GAAG,MAAM,cAAc,mBAAmB,EAAE;AAAA,YACtI;AAAA,YACA,GAAI,YAAY,CAAC,wCAAwC,IAAI,CAAC;AAAA,YAC9D,WAAW,YAAY,IAAI,IAAI,SAAS,QAAQ,CAAC,CAAC;AAAA,UACpD,EAAE,KAAK,IAAI;AAAA,UACX;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/sync/continuous-watch.ts", "../../src/sync/continuous-sync.ts"],
|
|
4
|
-
"sourcesContent": ["import { type FSWatcher, watch } from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { getAgentDir } from \"@earendil-works/pi-coding-agent\";\nimport { isDeniedPath } from \"../paths.js\";\nimport type { AnySyncConfig } from \"../settings/settings-types.js\";\n\n/** Hints only. The scheduler's periodic snapshot scan remains required. */\nexport async function watchContinuous(\n config: AnySyncConfig,\n changed: (settings: boolean) => void,\n): Promise<() => void> {\n const root = getAgentDir();\n const watchers: FSWatcher[] = [];\n const selected = config.include.map((item) => path.resolve(root, item));\n const relevant = (target: string) =>\n selected.some(\n (item) => target === item || target.startsWith(`${item}${path.sep}`) || item.startsWith(`${target}${path.sep}`),\n );\n const add = (directory: string, recursive: boolean) => {\n try {\n const watcher = watch(directory, { recursive, persistent: false }, (_event, file) => {\n // Unknown names can be our own private writes. Periodic scans cover them.\n if (!file) return;\n const target = path.resolve(directory, String(file));\n if (target === path.join(root, \"pi-sync.json\")) changed(true);\n else if (isDeniedPath(path.relative(root, target))) return;\n else if (relevant(target)) changed(false);\n });\n watcher.on(\"error\", () => watcher.close());\n watchers.push(watcher);\n } catch {\n /* Periodic scans cover unavailable or unsupported watchers. */\n }\n };\n add(root, false);\n const directories = new Set<string>();\n try {\n for (const target of selected) {\n try {\n const stat = await fs.lstat(target);\n if (stat.isSymbolicLink()) continue;\n const directory = stat.isDirectory() ? target : path.dirname(target);\n if (directory === root || directories.has(directory)) continue;\n directories.add(directory);\n add(directory, stat.isDirectory());\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") continue;\n // Missing targets are found by the parent hint or the next periodic scan.\n }\n }\n return () => {\n for (const watcher of watchers) watcher.close();\n };\n } catch (error) {\n for (const watcher of watchers) watcher.close();\n throw error;\n }\n}\n", "import type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { loadConfigForCheck, syncCheckConfigFingerprint } from \"../settings/config.js\";\nimport type { AnySyncConfig } from \"../settings/settings-types.js\";\nimport { snapshotOptionsForContext } from \"../snapshot/session-paths.js\";\nimport { withLock } from \"../state/lock.js\";\nimport { withStateDirectoryAccess } from \"../state/state-directory.js\";\nimport { readStateForConfig } from \"../state/sync-state-store.js\";\nimport { setSyncStatus } from \"../ui/sync-status.js\";\nimport { withAgentSyncLock } from \"./agent-sync.js\";\nimport { ContinuousError, continuousFailure } from \"./continuous-errors.js\";\nimport { acquireContinuousLease, type ContinuousLease } from \"./continuous-lease.js\";\nimport {\n normalizeContinuousClock,\n readContinuousRecord,\n reserveContinuousRequest,\n updateContinuousRecord,\n} from \"./continuous-record.js\";\nimport { continuousBinding, hashContinuous } from \"./continuous-storage.js\";\nimport { watchContinuous } from \"./continuous-watch.js\";\nimport { readMergeJournal } from \"./merge-journal.js\";\nimport type { SyncLoaders } from \"./sync-loaders.js\";\nimport { captureMutationOwner } from \"./sync-local.js\";\nimport { fileHashMap } from \"./sync-state.js\";\n\nexport function continuousEnabled(config: Pick<AnySyncConfig, \"continuous\" | \"agentMode\">) {\n return Boolean(config.continuous && config.agentMode);\n}\nexport interface ContinuousClock {\n now(): number;\n random(): number;\n schedule(ms: number, run: () => void, kind?: \"wait\" | \"authorization\"): () => void;\n}\nconst systemClock: ContinuousClock = {\n now: () => Date.now(),\n random: () => Math.random(),\n schedule: (ms, run) => {\n const timer = setTimeout(run, ms);\n timer.unref?.();\n return () => clearTimeout(timer);\n },\n};\nconst LOCAL_CHECK_MS = 5_000;\nconst POLL_MS = 60_000;\nconst DEBOUNCE_MS = 1_000;\n\n/** Created on first enabled session. No module/factory-owned timers or watchers. */\nexport function createContinuousSync(loaders: SyncLoaders, clock: ContinuousClock = systemClock) {\n const repairNotices = new WeakSet<ExtensionContext[\"sessionManager\"]>();\n let task:\n | { owner: ExtensionContext[\"sessionManager\"]; controller: AbortController; promise: Promise<void>; clear(): void }\n | undefined;\n return {\n start(ctx: ExtensionContext, sessionSignal: AbortSignal, explicitRetry = false) {\n if (!ctx.hasUI || (ctx.mode !== \"tui\" && ctx.mode !== \"rpc\") || sessionSignal.aborted || task) return;\n const controller = new AbortController();\n const signal = AbortSignal.any([sessionSignal, controller.signal]);\n const validateContext = captureMutationOwner(ctx);\n const current = {\n owner: ctx.sessionManager,\n controller,\n promise: Promise.resolve(),\n clear: () => {\n try {\n validateContext();\n setSyncStatus(ctx, undefined);\n } catch {\n /* replaced session */\n }\n },\n };\n task = current;\n current.promise = (async () => {\n let explicit = explicitRetry;\n while (!signal.aborted) {\n try {\n await run(ctx, signal, explicit);\n return;\n } catch {\n if (signal.aborted) return;\n try {\n validateContext();\n setSyncStatus(ctx, \"sync blocked: private state or settings need repair\");\n if (!repairNotices.has(ctx.sessionManager)) {\n repairNotices.add(ctx.sessionManager);\n ctx.ui.notify(\n \"Continuous sync blocked: private state or settings need repair. Use /sync. Evidence was retained.\",\n \"warning\",\n );\n }\n } catch {\n return;\n }\n }\n explicit = false;\n // Only local prerequisites are retried here. No remote/model request can\n // pass a malformed settings/state read. A repair can resume monitoring.\n await wait(clock, LOCAL_CHECK_MS, signal);\n }\n })()\n .catch(() => {\n /* Cancellation/UI disposal must not leak a detached rejection. */\n })\n .finally(() => {\n if (task === current) task = undefined;\n });\n },\n async stop(owner?: ExtensionContext[\"sessionManager\"]) {\n const current = task;\n if (!current || (owner && current.owner !== owner)) return;\n current.controller.abort(new DOMException(\"Continuous sync interrupted\", \"AbortError\"));\n current.clear();\n await current.promise;\n },\n };\n\n async function run(ctx: ExtensionContext, signal: AbortSignal, explicitRetry: boolean) {\n const validateOwner = captureMutationOwner(ctx, signal);\n try {\n while (!signal.aborted) {\n validateOwner();\n const config = await loadConfigForCheck();\n validateOwner();\n if (!continuousEnabled(config)) return;\n try {\n if (ctx.isIdle()) {\n let lease: ContinuousLease | undefined;\n let binding = \"\";\n try {\n await withStateDirectoryAccess(async () => {\n validateOwner();\n binding = await continuousBinding(config);\n validateOwner();\n lease = await acquireContinuousLease(binding);\n validateOwner();\n });\n if (lease) {\n await monitor(ctx, config, binding, lease, signal, explicitRetry);\n explicitRetry = false;\n } else setSyncStatus(ctx, \"sync waiting: another local owner\");\n } finally {\n await lease?.release();\n }\n }\n } catch (error) {\n if (signal.aborted) return;\n const failure = continuousFailure(error);\n if (!failure.retry && !(error instanceof Error && error.name === \"AbortError\")) throw error;\n setSyncStatus(ctx, `sync retry scheduled: ${failure.kind}`);\n }\n validateOwner();\n if (!continuousEnabled(await loadConfigForCheck())) return;\n validateOwner();\n await wait(clock, LOCAL_CHECK_MS, signal);\n }\n } finally {\n // Shutdown/replacement owns clearing the old UI; do not call it from a stale continuation.\n if (!signal.aborted) {\n try {\n validateOwner();\n setSyncStatus(ctx, undefined);\n } catch {\n /* stale context */\n }\n }\n }\n }\n\n async function monitor(\n ctx: ExtensionContext,\n initial: AnySyncConfig,\n binding: string,\n lease: ContinuousLease,\n parent: AbortSignal,\n explicitRetry: boolean,\n ) {\n const signal = AbortSignal.any([parent, lease.signal]);\n const validateOwner = captureMutationOwner(ctx, signal);\n let config = initial;\n let closeWatch = () => {};\n let wake = () => {};\n let dirtyAt: number | undefined;\n let cycle: AbortController | undefined;\n const changed = (settings: boolean) => {\n dirtyAt = clock.now();\n if (settings) cycle?.abort(new DOMException(\"Continuous settings changed\", \"AbortError\"));\n wake();\n };\n const validate = () => {\n validateOwner();\n lease.assert();\n if (!ctx.isIdle()) throw new DOMException(\"Owning session became busy\", \"AbortError\");\n };\n const stopWatch = () => {\n closeWatch();\n wake();\n };\n signal.addEventListener(\"abort\", stopWatch, { once: true });\n let pollDue = 0;\n try {\n closeWatch = await watchContinuous(config, changed);\n validate();\n while (!signal.aborted && ctx.isIdle()) {\n validate();\n const latest = await loadConfigForCheck();\n validate();\n if (!continuousEnabled(latest) || (await continuousBinding(latest)) !== binding) return;\n validate();\n if (syncCheckConfigFingerprint(latest) !== syncCheckConfigFingerprint(config)) {\n closeWatch();\n config = latest;\n closeWatch = await watchContinuous(config, changed);\n validate();\n pollDue = 0;\n }\n const record = await readContinuousRecord(binding);\n validate();\n const now = clock.now();\n const oldClock = JSON.stringify([record.next, record.calls]);\n normalizeContinuousClock(record, now);\n if (oldClock !== JSON.stringify([record.next, record.calls])) {\n await updateContinuousRecord(binding, lease, (current) => normalizeContinuousClock(current, now));\n validate();\n }\n const configuration = hashContinuous([{ ...config, showStatus: undefined, debug: undefined }, ctx.model]);\n const controlChanged = record.configuration !== configuration;\n if (controlChanged || explicitRetry) {\n record.next = 0;\n pollDue = 0;\n }\n const hintDue = dirtyAt === undefined ? Number.POSITIVE_INFINITY : dirtyAt + DEBOUNCE_MS;\n const due = Math.max(record.error === \"budget\" ? 0 : record.next, Math.min(pollDue, hintDue));\n if (now >= due) {\n const force = explicitRetry || controlChanged;\n explicitRetry = false;\n const expectedModel = hashContinuous(ctx.model);\n const expectedConfig = syncCheckConfigFingerprint(config);\n dirtyAt = undefined;\n cycle = new AbortController();\n const cancelDeadline = clock.schedule(30_000, () =>\n cycle?.abort(new DOMException(\"Continuous cycle timed out\", \"TimeoutError\")),\n );\n const guard = new AbortController();\n const guardSignal = AbortSignal.any([signal, cycle.signal, guard.signal]);\n const activeCycle = cycle;\n // Transfer entrypoints supply each fresh settings read. Never let a new\n // transfer adopt authorization that changed after this cycle's inspection.\n const validateCycle = (latest?: AnySyncConfig) => {\n validate();\n if (\n (latest && syncCheckConfigFingerprint(latest) !== expectedConfig) ||\n hashContinuous(ctx.model) !== expectedModel\n )\n activeCycle.abort(new DOMException(\"Continuous authorization changed\", \"AbortError\"));\n activeCycle.signal.throwIfAborted();\n };\n const guardTask = (async () => {\n while (!guardSignal.aborted) {\n await wait(clock, LOCAL_CHECK_MS, guardSignal, undefined, \"authorization\");\n if (guardSignal.aborted) return;\n try {\n validate();\n const latest = await loadConfigForCheck();\n if (guardSignal.aborted) return;\n validateCycle(latest);\n } catch {\n activeCycle.abort(new DOMException(\"Continuous authorization unavailable\", \"AbortError\"));\n }\n }\n })().catch(() => {});\n try {\n await performCycle(\n ctx,\n config,\n binding,\n lease,\n AbortSignal.any([signal, cycle.signal]),\n validateCycle,\n configuration,\n force,\n );\n } catch (error) {\n if (signal.aborted || !ctx.isIdle()) return;\n // A settings hint interrupts the old authorization; load it afresh, not as a failed transfer.\n if (cycle.signal.aborted && cycle.signal.reason?.name !== \"TimeoutError\") {\n pollDue = 0;\n continue;\n }\n const failure = continuousFailure(error);\n let announce = false;\n await updateContinuousRecord(binding, lease, (current) => {\n const now = clock.now();\n normalizeContinuousClock(current, now);\n const notice = hashContinuous([failure.kind, current.evidence, configuration]);\n announce = current.notice !== notice;\n if (!failure.retry) current.notice = notice;\n current.configuration = configuration;\n current.pending = true;\n current.error = failure.kind;\n current.phase = failure.retry ? \"retry\" : \"blocked\";\n current.failures = Math.min(30, current.failures + 1);\n const base = Math.min(300_000, 5_000 * 2 ** Math.min(6, current.failures - 1));\n const delay = Math.min(300_000, base + Math.floor(clock.random() * base * 0.2));\n current.next = now + Math.max(delay, failure.delay ?? 0);\n });\n validate();\n if (announce && !failure.retry)\n ctx.ui.notify(\n `Continuous sync blocked (${failure.kind}). Review /sync or repair the setup. Evidence was retained.`,\n \"warning\",\n );\n } finally {\n cancelDeadline();\n guard.abort();\n await guardTask;\n cycle = undefined;\n }\n pollDue = clock.now() + POLL_MS + Math.floor(clock.random() * 6_000);\n closeWatch();\n closeWatch = () => {};\n closeWatch = await watchContinuous(config, changed);\n validate();\n }\n validate();\n const state = await readContinuousRecord(binding);\n validate();\n setSyncStatus(\n ctx,\n state.phase === \"blocked\"\n ? `sync blocked: ${state.error}`\n : state.phase === \"retry\"\n ? `sync retry scheduled: ${state.error}`\n : \"sync waiting for changes\",\n );\n if (state.phase === \"retry\") pollDue = Math.min(pollDue, state.next);\n const next = Math.max(\n state.error === \"budget\" ? 0 : state.next,\n Math.min(pollDue, dirtyAt === undefined ? Number.POSITIVE_INFINITY : dirtyAt + DEBOUNCE_MS),\n );\n await wait(clock, Math.max(1, Math.min(LOCAL_CHECK_MS, next - clock.now())), signal, (callback) => {\n wake = callback;\n });\n }\n } finally {\n signal.removeEventListener(\"abort\", stopWatch);\n closeWatch();\n cycle?.abort();\n }\n }\n\n async function performCycle(\n ctx: ExtensionContext,\n config: AnySyncConfig,\n binding: string,\n lease: ContinuousLease,\n signal: AbortSignal,\n validateOwner: (config?: AnySyncConfig) => void,\n configuration: string,\n force: boolean,\n ) {\n const validate = (latest?: AnySyncConfig) => {\n signal.throwIfAborted();\n validateOwner(latest);\n };\n validate();\n const current = await readContinuousRecord(binding);\n validate();\n // A newly pending journal may prove an uncertain commit. It is never authority to republish.\n const pending = await guardedLock(\"continuous-journal\", () => readMergeJournal(config));\n validate();\n if (\n !force &&\n !pending &&\n current.phase === \"blocked\" &&\n [\"authentication\", \"storage\", \"unknown\"].includes(current.error ?? \"\")\n )\n return;\n setSyncStatus(ctx, \"sync checking\");\n const inspectionModule = await loaders.inspection();\n validate();\n const observed = await guardedLock(\"continuous-check\", async () => {\n validate();\n const inspection = await inspectionModule.inspectSync(config, snapshotOptionsForContext(ctx, config), signal);\n validate();\n let localIdentity = inspection.localIdentity;\n if (!localIdentity) {\n const snapshotModule = await loaders.snapshot();\n validate();\n const local = await snapshotModule.createSnapshot(config.snapshotIdentity, {\n ...snapshotOptionsForContext(ctx, config),\n signal,\n });\n validate();\n localIdentity = hashContinuous(fileHashMap(local));\n }\n const journal = await readMergeJournal(config);\n validate();\n return { inspection, journal, localIdentity };\n });\n validate();\n const evidence = hashContinuous([\n configuration,\n observed.localIdentity,\n observed.inspection.head?.revision,\n observed.inspection.stateIdentity,\n ]);\n const recovery = observed.journal ? hashContinuous([evidence, observed.journal]) : undefined;\n const blocked =\n !force &&\n current.evidence === evidence &&\n ((current.phase === \"blocked\" && (!recovery || recovery === current.recovery)) ||\n (current.error === \"budget\" && current.next > clock.now() && !observed.journal));\n await updateContinuousRecord(binding, lease, (record) => {\n if (record.configuration !== configuration) {\n record.failures = 0;\n record.next = 0;\n }\n record.configuration = configuration;\n record.checked = clock.now();\n record.evidence = evidence;\n if (!blocked) {\n if (record.phase !== \"blocked\") record.phase = \"syncing\";\n record.pending = true;\n if (recovery) record.recovery = recovery;\n }\n });\n validate();\n if (blocked) return;\n if (\n !observed.journal &&\n (observed.inspection.firstSync ||\n !observed.inspection.head ||\n observed.inspection.selectionState?.kind !== \"same\")\n )\n throw new ContinuousError(\"unsafe\", \"Continuous sync needs an accepted baseline and matching remote selection.\");\n let transferred = false;\n if (observed.journal || observed.inspection.localChanged || observed.inspection.remoteChanged) {\n const operations = await loaders.operations();\n validate();\n setSyncStatus(ctx, \"sync syncing\");\n const result = await withStateDirectoryAccess(() =>\n withAgentSyncLock(\n ctx,\n {\n args: [],\n yes: true,\n force: false,\n stale: false,\n silent: true,\n reload: false,\n auto: true,\n signal,\n background: {\n validate,\n reserve: (key, requestSignal) =>\n reserveContinuousRequest(binding, lease, key, () => clock.now(), requestSignal),\n },\n },\n (options) => operations.syncBoth(ctx, options),\n ),\n );\n validate();\n transferred = result === \"applied\";\n }\n const state = await guardedLock(\"continuous-result\", () => readStateForConfig(config));\n validate();\n const unresolved = state.unresolved;\n let announcePartial = false;\n await updateContinuousRecord(binding, lease, (record) => {\n record.phase = unresolved?.length ? \"blocked\" : \"waiting\";\n record.pending = Boolean(unresolved?.length);\n record.failures = 0;\n record.next = 0;\n if (unresolved?.length) {\n record.error = \"unsafe\";\n const notice = hashContinuous([\"partial\", unresolved, configuration]);\n announcePartial = record.notice !== notice;\n record.notice = notice;\n } else {\n delete record.error;\n delete record.notice;\n }\n if (transferred) record.transferred = clock.now();\n });\n validate();\n if (announcePartial)\n ctx.ui.notify(\n \"Continuous sync retained unresolved groups. Review /sync conflicts; independent partial progress was preserved.\",\n \"warning\",\n );\n }\n}\n\nfunction guardedLock<T>(command: string, run: () => Promise<T>) {\n return withStateDirectoryAccess(() => withLock(command, run));\n}\n\nfunction wait(\n clock: ContinuousClock,\n ms: number,\n signal: AbortSignal,\n expose?: (wake: () => void) => void,\n kind: \"wait\" | \"authorization\" = \"wait\",\n) {\n signal.throwIfAborted();\n return new Promise<void>((resolve) => {\n const done = () => {\n cancel();\n signal.removeEventListener(\"abort\", done);\n expose?.(() => {});\n resolve();\n };\n const cancel = clock.schedule(ms, done, kind);\n signal.addEventListener(\"abort\", done, { once: true });\n expose?.(done);\n if (signal.aborted) done();\n });\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["latest", "now"]
|
|
7
|
-
}
|
|
File without changes
|