@mulmoclaude/core 1.12.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/assets/helps/error-recovery.md +110 -1
  2. package/dist/{calendarGrid-CaS9er8i.cjs → calendarGrid-BQYqOCnF.cjs} +8 -9
  3. package/dist/{calendarGrid-CaS9er8i.cjs.map → calendarGrid-BQYqOCnF.cjs.map} +1 -1
  4. package/dist/{calendarGrid-CrGxeCOG.js → calendarGrid-DivcNKkX.js} +8 -9
  5. package/dist/{calendarGrid-CrGxeCOG.js.map → calendarGrid-DivcNKkX.js.map} +1 -1
  6. package/dist/collection/core/project.d.ts +11 -0
  7. package/dist/collection/core/schemaZ.d.ts +4 -4
  8. package/dist/collection/index.cjs +5 -4
  9. package/dist/collection/index.js +5 -5
  10. package/dist/collection/registry/server/index.cjs +3 -3
  11. package/dist/collection/registry/server/index.cjs.map +1 -1
  12. package/dist/collection/registry/server/index.js +3 -3
  13. package/dist/collection/registry/server/index.js.map +1 -1
  14. package/dist/collection/server/csvQuery.d.ts +7 -2
  15. package/dist/collection/server/csvStore.d.ts +2 -1
  16. package/dist/collection/server/index.cjs +3 -3
  17. package/dist/collection/server/index.js +3 -3
  18. package/dist/collection-watchers/index.cjs +10 -25
  19. package/dist/collection-watchers/index.cjs.map +1 -1
  20. package/dist/collection-watchers/index.js +10 -25
  21. package/dist/collection-watchers/index.js.map +1 -1
  22. package/dist/{discovery-NRy3tyUA.cjs → discovery-CRDx6TS4.cjs} +88 -47
  23. package/dist/discovery-CRDx6TS4.cjs.map +1 -0
  24. package/dist/{discovery-B4CZvrXR.js → discovery-D6p4ITNR.js} +88 -47
  25. package/dist/discovery-D6p4ITNR.js.map +1 -0
  26. package/dist/{dist-pWpC-b04.cjs → dist-CsgSfWwR.cjs} +66 -1
  27. package/dist/{dist-pWpC-b04.cjs.map → dist-CsgSfWwR.cjs.map} +1 -1
  28. package/dist/{dist-Cwk0e12G.js → dist-D8zokgGo.js} +25 -2
  29. package/dist/{dist-Cwk0e12G.js.map → dist-D8zokgGo.js.map} +1 -1
  30. package/dist/feeds/index.cjs +2 -2
  31. package/dist/feeds/index.js +2 -2
  32. package/dist/feeds/server/engine.d.ts +27 -0
  33. package/dist/feeds/server/index.cjs +97 -26
  34. package/dist/feeds/server/index.cjs.map +1 -1
  35. package/dist/feeds/server/index.d.ts +1 -1
  36. package/dist/feeds/server/index.js +96 -27
  37. package/dist/feeds/server/index.js.map +1 -1
  38. package/dist/google/calendar.d.ts +23 -9
  39. package/dist/google/calendarStateLock.d.ts +23 -0
  40. package/dist/google/collectionProjection.d.ts +0 -8
  41. package/dist/google/collectionPush.d.ts +13 -8
  42. package/dist/google/collectionSync.d.ts +80 -10
  43. package/dist/google/index.cjs +366 -98
  44. package/dist/google/index.cjs.map +1 -1
  45. package/dist/google/index.d.ts +6 -4
  46. package/dist/google/index.js +357 -99
  47. package/dist/google/index.js.map +1 -1
  48. package/dist/{ingestTypes-D1GQdG8e.cjs → ingestTypes-C7IJSqv4.cjs} +2 -2
  49. package/dist/{ingestTypes-D1GQdG8e.cjs.map → ingestTypes-C7IJSqv4.cjs.map} +1 -1
  50. package/dist/{ingestTypes-XReG-li7.js → ingestTypes-DmtDLRmt.js} +2 -2
  51. package/dist/{ingestTypes-XReG-li7.js.map → ingestTypes-DmtDLRmt.js.map} +1 -1
  52. package/dist/notifier/index.cjs +1 -1
  53. package/dist/notifier/index.js +1 -1
  54. package/dist/notifier/store.d.ts +4 -4
  55. package/dist/{notifier-BdA5qzhe.js → notifier-Dxh8mEFh.js} +39 -25
  56. package/dist/notifier-Dxh8mEFh.js.map +1 -0
  57. package/dist/{notifier-tMsAXyXp.cjs → notifier-Wc4M24Bi.cjs} +39 -25
  58. package/dist/notifier-Wc4M24Bi.cjs.map +1 -0
  59. package/dist/plugin-vue/index.cjs +4 -2
  60. package/dist/plugin-vue/index.cjs.map +1 -1
  61. package/dist/plugin-vue/index.js +4 -2
  62. package/dist/plugin-vue/index.js.map +1 -1
  63. package/dist/project-B0j_Vf_Q.js +33 -0
  64. package/dist/project-B0j_Vf_Q.js.map +1 -0
  65. package/dist/project-C1ep9pvo.cjs +44 -0
  66. package/dist/project-C1ep9pvo.cjs.map +1 -0
  67. package/dist/{promptSafety-FD8cQn3e.cjs → promptSafety-B52GvaJc.cjs} +99 -85
  68. package/dist/promptSafety-B52GvaJc.cjs.map +1 -0
  69. package/dist/{promptSafety-C--op_6a.js → promptSafety-DJgaDjM4.js} +99 -85
  70. package/dist/promptSafety-DJgaDjM4.js.map +1 -0
  71. package/dist/remote-host/index.cjs +2 -1
  72. package/dist/remote-host/index.d.ts +10 -0
  73. package/dist/remote-host/index.js +2 -2
  74. package/dist/remote-host/server/index.cjs +2 -2
  75. package/dist/remote-host/server/index.cjs.map +1 -1
  76. package/dist/remote-host/server/index.js +2 -2
  77. package/dist/remote-host/server/index.js.map +1 -1
  78. package/dist/{remote-host-DkDVxNim.cjs → remote-host-CtQjagPt.cjs} +53 -1
  79. package/dist/remote-host-CtQjagPt.cjs.map +1 -0
  80. package/dist/remote-host-Dba4lF3Z.js +82 -0
  81. package/dist/remote-host-Dba4lF3Z.js.map +1 -0
  82. package/dist/remote-view/index.cjs +6 -4
  83. package/dist/remote-view/index.cjs.map +1 -1
  84. package/dist/remote-view/index.js +6 -4
  85. package/dist/remote-view/index.js.map +1 -1
  86. package/dist/scheduler/index.cjs +1 -1
  87. package/dist/scheduler/index.js +1 -1
  88. package/dist/{server-DdyIvFhz.js → server-D6EUf3BF.js} +46 -27
  89. package/dist/server-D6EUf3BF.js.map +1 -0
  90. package/dist/{server-7U-3DE2e.cjs → server-D7_oRbTJ.cjs} +46 -27
  91. package/dist/server-D7_oRbTJ.cjs.map +1 -0
  92. package/dist/utils/index.cjs +1 -1
  93. package/dist/utils/index.js +1 -1
  94. package/dist/whisper/index.cjs +2 -6
  95. package/dist/whisper/index.cjs.map +1 -1
  96. package/dist/whisper/index.js +2 -6
  97. package/dist/whisper/index.js.map +1 -1
  98. package/dist/wiki/index.cjs +2 -2
  99. package/dist/wiki/index.cjs.map +1 -1
  100. package/dist/wiki/index.js +2 -2
  101. package/dist/wiki/index.js.map +1 -1
  102. package/package.json +2 -2
  103. package/dist/discovery-B4CZvrXR.js.map +0 -1
  104. package/dist/discovery-NRy3tyUA.cjs.map +0 -1
  105. package/dist/notifier-BdA5qzhe.js.map +0 -1
  106. package/dist/notifier-tMsAXyXp.cjs.map +0 -1
  107. package/dist/project-BWI5w_BT.cjs +0 -17
  108. package/dist/project-BWI5w_BT.cjs.map +0 -1
  109. package/dist/project-bU98ycsy.js +0 -12
  110. package/dist/project-bU98ycsy.js.map +0 -1
  111. package/dist/promptSafety-C--op_6a.js.map +0 -1
  112. package/dist/promptSafety-FD8cQn3e.cjs.map +0 -1
  113. package/dist/remote-host-D_BRFHcI.js +0 -36
  114. package/dist/remote-host-D_BRFHcI.js.map +0 -1
  115. package/dist/remote-host-DkDVxNim.cjs.map +0 -1
  116. package/dist/server-7U-3DE2e.cjs.map +0 -1
  117. package/dist/server-DdyIvFhz.js.map +0 -1
@@ -1,13 +1,14 @@
1
- import { t as errorMessage } from "../dist-Cwk0e12G.js";
1
+ import { i as hasStringProp, t as errorMessage } from "../dist-D8zokgGo.js";
2
2
  import { t as truncate } from "../text-CjDbP2O1.js";
3
3
  import { t as writeFileAtomic } from "../atomic-DPpdrJzO.js";
4
4
  import { fetchWithTimeout } from "../utils/fetch.js";
5
- import { l as parseIsoDate, u as parseIsoDateTime } from "../calendarGrid-CrGxeCOG.js";
6
- import { J as getWorkspaceRoot, d as storeFor, et as createForwardingLogger, n as discoverCollections, tt as createHostSlot } from "../discovery-B4CZvrXR.js";
5
+ import { l as parseIsoDate, u as parseIsoDateTime } from "../calendarGrid-DivcNKkX.js";
6
+ import { t as mergeIntoExisting } from "../project-B0j_Vf_Q.js";
7
+ import { J as getWorkspaceRoot, d as storeFor, et as createForwardingLogger, n as discoverCollections, tt as createHostSlot } from "../discovery-D6p4ITNR.js";
7
8
  import { promises } from "node:fs";
8
9
  import path, { join } from "node:path";
9
- import { randomBytes } from "node:crypto";
10
- import { constants, copyFile, mkdir, readFile, readdir, rm, stat } from "node:fs/promises";
10
+ import { randomBytes, randomUUID } from "node:crypto";
11
+ import { constants, copyFile, mkdir, open, readFile, readdir, rm, stat, unlink } from "node:fs/promises";
11
12
  import { homedir } from "node:os";
12
13
  import { MISSED_RUN_POLICIES, SCHEDULE_TYPES } from "@receptron/task-scheduler";
13
14
  import http from "node:http";
@@ -588,6 +589,7 @@ var CALENDAR_LIST_PAGE_SIZE = 250;
588
589
  var MAX_CALENDAR_LIST_PAGES = 40;
589
590
  var EVENT_SYNC_PAGE_SIZE = 2500;
590
591
  var MAX_EVENT_SYNC_PAGES = 200;
592
+ var CALENDAR_META_PAGE_SIZE = 1;
591
593
  var HTTP_GONE = 410;
592
594
  var EVENT_ABSENT_STATUSES = [404, HTTP_GONE];
593
595
  /** An `If-Match` write whose etag no longer matches — someone else changed the
@@ -799,20 +801,34 @@ async function collectCalendarPages(fetchPage, maxPages = MAX_CALENDAR_LIST_PAGE
799
801
  }
800
802
  return calendars;
801
803
  }
802
- /** The calendars the user has added/subscribed to (primary + secondary +
803
- * shared), each with its id, name and colour, following pagination. Needs the
804
- * calendar-list read scope (GOOGLE_CALENDARLIST_SCOPE). */
805
- /** One calendar's own resource, addressed by id rather than looked up in the
806
- * user's list.
804
+ var toCalendarMeta = (value) => {
805
+ const record = asRecord(value);
806
+ return {
807
+ timeZone: stringField(record, "timeZone"),
808
+ accessRole: stringField(record, "accessRole")
809
+ };
810
+ };
811
+ /** One calendar's zone and the caller's role on it, read by id rather than
812
+ * looked up in the user's list.
807
813
  *
808
814
  * `calendarList` only holds calendars the user has ADDED; a calendar shared
809
- * with them can be readable and writable by id without appearing there. This is
810
- * how a push learns such a calendar's `timeZone`. Note the resource carries no
811
- * `accessRole` — that is a calendarList property — so reachability here says
812
- * nothing about writability. */
813
- async function getCalendar(accessToken, calendarId) {
814
- return toCalendarSummary(await googleRequest(CALENDAR_API_LABEL, accessToken, `${CALENDAR_BASE_URL}/calendars/${encodeURIComponent(canonicalCalendarId(calendarId))}`));
815
+ * with them can be readable and writable by id without appearing there, and a
816
+ * push still needs its zone to send an offset-less `dateTime`.
817
+ *
818
+ * Deliberately `events.list` and NOT `calendars.get`: the latter takes only
819
+ * full-calendar scopes (`calendar`, `calendar.readonly`, `calendar.calendars*`)
820
+ * and this app asks for none of them, so it answers 403 for every account it
821
+ * links (#2735). The events list carries the same `timeZone` — plus the
822
+ * `accessRole` the calendar resource does not have — under `calendar.events`,
823
+ * which the push already holds because it writes with it. Only the envelope is
824
+ * read; the one event asked for is the smallest page Google will serve. */
825
+ async function getCalendarMeta(accessToken, calendarId) {
826
+ const params = new URLSearchParams({ maxResults: String(CALENDAR_META_PAGE_SIZE) });
827
+ return toCalendarMeta(await googleRequest(CALENDAR_API_LABEL, accessToken, `${eventsUrl(calendarId)}?${params.toString()}`));
815
828
  }
829
+ /** The calendars the user has added/subscribed to (primary + secondary +
830
+ * shared), each with its id, name and colour, following pagination. Needs the
831
+ * calendar-list read scope (GOOGLE_CALENDARLIST_SCOPE). */
816
832
  async function listCalendars(accessToken) {
817
833
  return collectCalendarPages(async (pageToken) => {
818
834
  const params = new URLSearchParams({ maxResults: String(CALENDAR_LIST_PAGE_SIZE) });
@@ -834,6 +850,159 @@ async function getCalendarColors(accessToken) {
834
850
  };
835
851
  }
836
852
  //#endregion
853
+ //#region src/google/calendarStateLock.ts
854
+ var LOCK_MODE = 384;
855
+ /** How long a lock may sit untouched before another process may reclaim it.
856
+ *
857
+ * Sized against the HOLD time, which is one read plus one atomic write — a few
858
+ * milliseconds. Generous by three orders of magnitude so a slow disk never
859
+ * looks like a crash, while a host that died mid-write blocks the others for
860
+ * at most this long. */
861
+ var LOCK_STALE_MS = 1e4;
862
+ /** How long a caller waits before giving up and proceeding unlocked. */
863
+ var LOCK_WAIT_MS = 5e3;
864
+ var LOCK_RETRY_MS = 20;
865
+ var liveLockClock = {
866
+ now: () => Date.now(),
867
+ sleep: (delay_ms) => new Promise((resolve) => setTimeout(resolve, delay_ms))
868
+ };
869
+ var isEexist = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
870
+ /** The file's bytes, or null when it is not there. */
871
+ async function readLockRaw(lockPath) {
872
+ try {
873
+ return await readFile(lockPath, "utf-8");
874
+ } catch {
875
+ return null;
876
+ }
877
+ }
878
+ /** Absent means unheld; UNREADABLE does not — see `stealIfStale`. */
879
+ function parseLock(raw) {
880
+ try {
881
+ const parsed = JSON.parse(raw);
882
+ return hasStringProp(parsed, "holder") ? { holder: parsed.holder } : null;
883
+ } catch {
884
+ return null;
885
+ }
886
+ }
887
+ async function readLock(lockPath) {
888
+ const raw = await readLockRaw(lockPath);
889
+ return raw === null ? null : parseLock(raw);
890
+ }
891
+ /** Take the lock, or answer false because someone else holds a live one. */
892
+ async function tryAcquire(lockPath, holder) {
893
+ try {
894
+ const handle = await open(lockPath, "wx", LOCK_MODE);
895
+ try {
896
+ await handle.writeFile(JSON.stringify({ holder }));
897
+ } finally {
898
+ await handle.close();
899
+ }
900
+ return true;
901
+ } catch (error) {
902
+ if (!isEexist(error)) throw error;
903
+ return false;
904
+ }
905
+ }
906
+ /** How long this lock has existed, per the filesystem that holds it.
907
+ *
908
+ * From mtime rather than anything the holder wrote down, and for every lock
909
+ * rather than only the unreadable ones. A timestamp written by one host and
910
+ * compared against another host's clock is two wall clocks pretending to be
911
+ * one: skew either way reclaims a live lock or immortalises a dead one (Codex
912
+ * review #2690). Every host reading this workspace sees the SAME mtime.
913
+ *
914
+ * This still assumes the reader's clock and the filesystem's are within a TTL
915
+ * of each other — true by construction on a local disk, and a sub-second NTP
916
+ * question on a network mount, against a window of ten seconds.
917
+ *
918
+ * Reading mtime also covers the moment `open("wx")` has created the file but
919
+ * its payload has not landed yet: "unreadable" is a state every healthy lock
920
+ * passes through, and one being born reads as newborn rather than dead.
921
+ *
922
+ * Null when the file is gone, which is not an age but an absence. */
923
+ async function lockAgeMs(lockPath, clock) {
924
+ const stamped = await stat(lockPath).catch(() => null);
925
+ return stamped === null ? null : clock.now() - stamped.mtimeMs;
926
+ }
927
+ /** Drop a lock that has sat untouched past the TTL, whatever it says.
928
+ *
929
+ * Readable or not, nothing is reclaimed until it is demonstrably stale. For a
930
+ * readable one the holder is re-read and compared before the unlink, so a
931
+ * holder that renewed in between is not evicted. That narrows the window
932
+ * rather than closing it — two processes can still both decide to clear the
933
+ * same corpse. The cost of losing that race is one lost update, which is what
934
+ * this file reduces rather than a new failure, and reaching it takes a host
935
+ * dying mid-write plus a full TTL of waiting. */
936
+ async function stealIfStale(lockPath, clock) {
937
+ const raw = await readLockRaw(lockPath);
938
+ if (raw === null) return;
939
+ const held = parseLock(raw);
940
+ const age_ms = await lockAgeMs(lockPath, clock);
941
+ if (age_ms === null || age_ms < LOCK_STALE_MS) return;
942
+ if (held === null) {
943
+ log.warn("google", "reclaiming an unreadable calendar state lock", {
944
+ lockPath,
945
+ age_ms
946
+ });
947
+ await unlink(lockPath).catch(() => void 0);
948
+ return;
949
+ }
950
+ if ((await readLock(lockPath))?.holder !== held.holder) return;
951
+ log.warn("google", "reclaiming a calendar state lock whose holder went quiet", {
952
+ lockPath,
953
+ age_ms
954
+ });
955
+ await unlink(lockPath).catch(() => void 0);
956
+ }
957
+ async function release(lockPath, holder) {
958
+ if ((await readLock(lockPath))?.holder !== holder) return;
959
+ await unlink(lockPath).catch(() => void 0);
960
+ }
961
+ /** Run `mutate` with no other process mutating the same file.
962
+ *
963
+ * Fails OPEN. A workspace that cannot create the lock file — a read-only
964
+ * mount, a missing directory, an exhausted disk — still syncs, the way it did
965
+ * before #2679; the lock removes a race, it is not a precondition for
966
+ * correctness. The same applies to waiting the timeout out: proceeding
967
+ * unlocked risks the lost update this exists to prevent, whereas refusing
968
+ * would stop the sync outright.
969
+ *
970
+ * Note what this cannot promise. `O_EXCL` is atomic on a local filesystem and
971
+ * on NFSv3+, but a workspace living in a consumer sync folder (Dropbox,
972
+ * iCloud, Drive) has no such guarantee — there the file is replicated after
973
+ * the fact and both hosts believe they hold it. */
974
+ async function withCalendarStateLock(lockPath, mutate, clock = liveLockClock) {
975
+ const holder = randomUUID();
976
+ const deadline = clock.now() + LOCK_WAIT_MS;
977
+ let holding = false;
978
+ try {
979
+ holding = await acquireBefore(lockPath, holder, deadline, clock);
980
+ if (!holding) log.warn("google", "proceeding without the calendar state lock — another host is holding it", { lockPath });
981
+ return await mutate();
982
+ } finally {
983
+ if (holding) await release(lockPath, holder);
984
+ }
985
+ }
986
+ /** Poll until the lock is ours or the deadline passes. */
987
+ async function acquireBefore(lockPath, holder, deadline, clock) {
988
+ for (;;) {
989
+ try {
990
+ if (await tryAcquire(lockPath, holder)) return true;
991
+ await stealIfStale(lockPath, clock);
992
+ } catch (error) {
993
+ log.warn("google", "could not take the calendar state lock — mutating without it", {
994
+ lockPath,
995
+ error: String(error)
996
+ });
997
+ return false;
998
+ }
999
+ if (clock.now() >= deadline) return false;
1000
+ await clock.sleep(LOCK_RETRY_MS);
1001
+ }
1002
+ }
1003
+ /** `<state file>.lock`, so each state file is serialised on its own. */
1004
+ var stateLockPath = (statePath) => `${statePath}.lock`;
1005
+ //#endregion
837
1006
  //#region src/google/calendarSyncStore.ts
838
1007
  var SYNC_STATE_MODE = 384;
839
1008
  /** `<workspace>/data/calendar/.sync-state.json` */
@@ -861,11 +1030,12 @@ var writeQueue$1 = { tail: Promise.resolve() };
861
1030
  * nothing in this process can read between the two. A `null` state skips the
862
1031
  * write; `result` is what the caller learns about the decision. */
863
1032
  async function updateStateWith(decide, workspaceRoot) {
864
- const run = writeQueue$1.tail.then(async () => {
1033
+ const statePath = calendarSyncStatePath(workspaceRoot);
1034
+ const run = writeQueue$1.tail.then(async () => await withCalendarStateLock(stateLockPath(statePath), async () => {
865
1035
  const { state, result } = decide(await readState$1(workspaceRoot));
866
- if (state) await writeJsonAtomicWithMode(calendarSyncStatePath(workspaceRoot), state, SYNC_STATE_MODE);
1036
+ if (state) await writeJsonAtomicWithMode(statePath, state, SYNC_STATE_MODE);
867
1037
  return result;
868
- });
1038
+ }));
869
1039
  writeQueue$1.tail = run.catch(() => void 0);
870
1040
  return await run;
871
1041
  }
@@ -985,10 +1155,11 @@ async function readState(workspaceRoot) {
985
1155
  }
986
1156
  var writeQueue = { tail: Promise.resolve() };
987
1157
  async function updateState(mutate, workspaceRoot) {
988
- const run = writeQueue.tail.then(async () => {
1158
+ const statePath = calendarPushStatePath(workspaceRoot);
1159
+ const run = writeQueue.tail.then(async () => await withCalendarStateLock(stateLockPath(statePath), async () => {
989
1160
  const state = await readState(workspaceRoot);
990
- await writeJsonAtomicWithMode(calendarPushStatePath(workspaceRoot), { events: mutate(state.events) }, PUSH_STATE_MODE);
991
- });
1161
+ await writeJsonAtomicWithMode(statePath, { events: mutate(state.events) }, PUSH_STATE_MODE);
1162
+ }));
992
1163
  writeQueue.tail = run.catch(() => void 0);
993
1164
  return await run;
994
1165
  }
@@ -1193,19 +1364,6 @@ function toCollectionRecord(event, map, primaryKey, fields) {
1193
1364
  [primaryKey]: event.id
1194
1365
  };
1195
1366
  }
1196
- /** Google's mapped values laid over whatever the record already holds.
1197
- *
1198
- * A record file is written whole (`writeItem`), so projecting alone would drop
1199
- * every column the map does not name — a collection cannot then carry a local
1200
- * note next to a mirrored event, because the next pull that touches that event
1201
- * silently deletes it. The projection still wins on the fields it covers: those
1202
- * are Google's to own. */
1203
- function mergeIntoExisting(existing, projected) {
1204
- return {
1205
- ...existing ?? {},
1206
- ...projected
1207
- };
1208
- }
1209
1367
  //#endregion
1210
1368
  //#region src/google/pushPlan.ts
1211
1369
  /** The event fields Google lets a caller write. `htmlLink` and `status` are
@@ -1220,7 +1378,7 @@ var PUSHABLE_SOURCE_FIELDS = [
1220
1378
  "description",
1221
1379
  "location"
1222
1380
  ];
1223
- var isPushableSource = (source) => PUSHABLE_SOURCE_FIELDS.includes(source);
1381
+ var isPushableSource = (source) => PUSHABLE_SOURCE_FIELDS.some((field) => field === source);
1224
1382
  /** The subset of a schema's `map` a push can act on: collection field → event field. */
1225
1383
  function pushableMap(map) {
1226
1384
  return Object.fromEntries(Object.entries(map).filter((entry) => isPushableSource(entry[1])));
@@ -1342,21 +1500,28 @@ function locallyDeletedIds(shadow, presentIds) {
1342
1500
  var WRITABLE_ACCESS_ROLES = ["owner", "writer"];
1343
1501
  /** Whether to refuse the whole push before touching a single event.
1344
1502
  *
1345
- * Only on POSITIVE evidence of a non-writable role. An unlisted calendar is
1346
- * unknown and must fall through: hard-denying it would block the feature
1347
- * outright for a calendar the user can in fact write to. Those attempts surface
1348
- * Google's own 403 per record if the write really is not allowed. */
1503
+ * Only on POSITIVE evidence of a non-writable role. A calendar whose role
1504
+ * neither source reported is unknown and must fall through: hard-denying it
1505
+ * would block the feature outright for a calendar the user can in fact write
1506
+ * to. Those attempts surface Google's own 403 per record if the write really is
1507
+ * not allowed. */
1349
1508
  var isDeniedAccessRole = (accessRole) => accessRole !== null && !WRITABLE_ACCESS_ROLES.includes(accessRole);
1509
+ /** A role Google left blank is UNKNOWN, not a denial. `isDeniedAccessRole`
1510
+ * refuses every role it does not recognise — `""` included — so passing an
1511
+ * unreported one straight through would refuse a calendar Google simply said
1512
+ * nothing about. */
1513
+ var reportedAccessRole = (accessRole) => accessRole || null;
1350
1514
  /** Resolve the calendar a schema names, for its writability and its timezone.
1351
1515
  *
1352
- * The list is consulted first because only it reports `accessRole`, which is
1353
- * what lets a read-only calendar be refused with the real reason instead of an
1354
- * opaque per-event 403. `"primary"` is matched on the `primary` flag, not the
1355
- * id — the primary calendar's own id is the account's email address, which never
1356
- * equals the literal the schema declares.
1516
+ * The list is consulted first because it answers for every added calendar in
1517
+ * one call the push already makes. `"primary"` is matched on the `primary`
1518
+ * flag, not the id — the primary calendar's own id is the account's email
1519
+ * address, which never equals the literal the schema declares.
1357
1520
  *
1358
- * A calendar absent from the list is NOT denied: it is fetched by id for its
1359
- * timezone and its role left unknown. A 404 there means the calendar really is
1521
+ * A calendar absent from the list is NOT denied: `calendarList` holds only the
1522
+ * calendars the user has added, so one shared with write access can be missing
1523
+ * from it (Codex review). It is asked about itself instead, which reports the
1524
+ * same zone AND the real role. A 404 there means the calendar is genuinely
1360
1525
  * unreachable, and propagates as a `failed` outcome. */
1361
1526
  async function liveCalendarMeta(accessToken, calendarId) {
1362
1527
  const key = canonicalCalendarId(calendarId);
@@ -1366,10 +1531,13 @@ async function liveCalendarMeta(accessToken, calendarId) {
1366
1531
  accessRole: listed.accessRole,
1367
1532
  timeZone: listed.timeZone
1368
1533
  };
1369
- const direct = await getCalendar(accessToken, calendarId);
1370
- log.info("google", "calendar is not in the user's list — pushing without an up-front role check", { calendarId: key });
1534
+ const direct = await getCalendarMeta(accessToken, calendarId);
1535
+ log.info("google", "calendar is not in the user's list — role read from the calendar itself", {
1536
+ calendarId: key,
1537
+ accessRole: direct.accessRole
1538
+ });
1371
1539
  return {
1372
- accessRole: null,
1540
+ accessRole: reportedAccessRole(direct.accessRole),
1373
1541
  timeZone: direct.timeZone
1374
1542
  };
1375
1543
  }
@@ -1483,15 +1651,23 @@ function buildPatch(ctx, record, changed, shadow) {
1483
1651
  }
1484
1652
  };
1485
1653
  }
1654
+ /** Google has dropped the event this record mirrors.
1655
+ *
1656
+ * The advice used to be "sync first, then re-create it", which sent people
1657
+ * nowhere: Google never resends a cancellation, so a sync sees nothing to do —
1658
+ * and before #2688 the sync that DID see it deleted the record along with the
1659
+ * edit. Deliberately silent on whether Google will hand the id back; that is
1660
+ * unverified. */
1661
+ var goneFromGoogle = (eventId) => ({
1662
+ kind: "skipped",
1663
+ message: `${eventId}: Google no longer has this event, and your local edit was kept. Delete this record, or copy it into a new one.`
1664
+ });
1486
1665
  async function updateFromRecord(ctx, eventId, record, changed, shadow) {
1487
1666
  const fetched = await getCalendarEvent(ctx.accessToken, {
1488
1667
  calendarId: ctx.calendarId,
1489
1668
  eventId
1490
1669
  });
1491
- if (fetched === null || fetched.event.status === "cancelled") return {
1492
- kind: "skipped",
1493
- message: `${eventId}: the event no longer exists in Google — sync first, then re-create it`
1494
- };
1670
+ if (fetched === null || fetched.event.status === "cancelled") return goneFromGoogle(eventId);
1495
1671
  if (conflictingFields(shadow, toShadowEvent(fetched.event), changed).length > 0) return { kind: "conflict" };
1496
1672
  const built = buildPatch(ctx, record, changed, shadow);
1497
1673
  if (!built.ok) return {
@@ -1546,9 +1722,9 @@ async function createOrAdopt(ctx, eventId, record) {
1546
1722
  }
1547
1723
  /** A 403 on a write is a permissions answer, but `googleApiError` appends the
1548
1724
  * "is the API enabled for the Cloud project?" hint to every 403 — true for a
1549
- * disabled API, misleading here. Reached when the up-front role check could not
1550
- * run because the calendar is not in the user's list, which is exactly when the
1551
- * user needs the real reason. */
1725
+ * disabled API, misleading here. Reached when the up-front gate let the push
1726
+ * through on an unknown role, which is exactly when the user needs the real
1727
+ * reason. */
1552
1728
  function writeFailure(eventId, error) {
1553
1729
  if (isGoogleApiError(error) && error.status === 403) return {
1554
1730
  kind: "skipped",
@@ -1719,7 +1895,42 @@ function classifyDelete(eventId, kind) {
1719
1895
  message: `delete ${eventId}: ${kind}`
1720
1896
  };
1721
1897
  }
1722
- async function applyEvent(collection, event, workspaceRoot) {
1898
+ /** Whether this record still says what the workspace last saw Google say.
1899
+ *
1900
+ * Compared against the baseline as it stood when this run STARTED READING, not
1901
+ * as it stands now: a full re-walk clears the baseline before writing a new one
1902
+ * (`restartFullSync`), so reading it live would answer "no baseline" for every
1903
+ * event and protect nothing exactly when the window is widest (#2684).
1904
+ *
1905
+ * A record with no baseline at all is NOT withheld — that is an event this
1906
+ * workspace has never held, so there is no local edit to lose. */
1907
+ function unsentEditGuard(schema, baseline) {
1908
+ const map = pushableMap(schema.googleCalendar?.map ?? {});
1909
+ return (existing, eventId) => {
1910
+ const shadow = baseline[eventId];
1911
+ if (shadow === void 0) return false;
1912
+ return locallyChangedFields(existing, baselineRecord(eventId, shadow, map, schema.primaryKey, schema.fields), map).length > 0;
1913
+ };
1914
+ }
1915
+ /** What the pull may do to the record behind one event.
1916
+ *
1917
+ * The guard runs BEFORE the status is consulted, and that order is the whole
1918
+ * fix: "is there something local to lose here?" outranks "what did Google do
1919
+ * to it?". Asking about the status first is how a cancellation kept deleting
1920
+ * records that held an edit Google had never seen (#2688), long after the
1921
+ * same guard had been put in front of the overwrite (#2684). */
1922
+ function applyPlanFor(existing, event, hasUnsentEdit) {
1923
+ if (existing !== null && hasUnsentEdit(existing, event.id)) return "withhold";
1924
+ return event.status === "cancelled" ? "delete" : "write";
1925
+ }
1926
+ /** Lay Google's mapped values over whatever the record already holds. Split out
1927
+ * so `applyEvent` reads as read → decide → act rather than carrying the write
1928
+ * itself (CodeRabbit review #2689). */
1929
+ async function writeProjected(write, event, schema, existing) {
1930
+ const record = toCollectionRecord(event, schema.googleCalendar?.map ?? {}, schema.primaryKey, schema.fields);
1931
+ return classifyWrite(event.id, (await write(event.id, mergeIntoExisting(existing, record))).kind);
1932
+ }
1933
+ async function applyEvent(collection, event, workspaceRoot, hasUnsentEdit) {
1723
1934
  const { schema } = collection;
1724
1935
  try {
1725
1936
  const store = storeFor(collection, { workspaceRoot });
@@ -1727,13 +1938,11 @@ async function applyEvent(collection, event, workspaceRoot) {
1727
1938
  kind: "unwritable",
1728
1939
  message: `collection '${collection.slug}' is read-only`
1729
1940
  };
1730
- if (event.status === "cancelled") {
1731
- const deleted = await store.delete(event.id);
1732
- return classifyDelete(event.id, deleted.kind);
1733
- }
1734
- const record = toCollectionRecord(event, schema.googleCalendar?.map ?? {}, schema.primaryKey, schema.fields);
1735
- const written = await store.write(event.id, mergeIntoExisting(await store.read(event.id), record));
1736
- return classifyWrite(event.id, written.kind);
1941
+ const existing = await store.read(event.id);
1942
+ const plan = applyPlanFor(existing, event, hasUnsentEdit);
1943
+ if (plan === "withhold") return { kind: "withheld" };
1944
+ if (plan === "delete") return classifyDelete(event.id, (await store.delete(event.id)).kind);
1945
+ return await writeProjected(store.write, event, schema, existing);
1737
1946
  } catch (error) {
1738
1947
  return {
1739
1948
  kind: "error",
@@ -1784,15 +1993,15 @@ var NOTHING_UNPUSHED = /* @__PURE__ */ new Set();
1784
1993
  * protection — would overwrite the very edits this exists to protect; the read
1785
1994
  * that failed is no evidence that the pull's own writes would fail too, so they
1786
1995
  * would land (CodeRabbit review #2666). */
1787
- var PROTECTION_UNKNOWN = "could not work out which records to protect after a failed push";
1788
- /** What ONE collection's pull must leave alone: only what ITS OWN push failed to
1996
+ var PROTECTION_UNKNOWN = "could not work out which records to protect from the pull";
1997
+ /** What ONE collection's pull must leave alone: only what ITS OWN push did not
1789
1998
  * send.
1790
1999
  *
1791
- * Scoped per collection because a calendar can back several of them, and a
1792
- * conflict in one says nothing about the others. Sharing one set across the
1793
- * group starved a collection that never even declares `autoPush`: it cannot
1794
- * conflict, yet a neighbour's conflict froze its records — and the sync token
1795
- * still advanced, so Google never resent them (Codex review #2666). */
2000
+ * Scoped per collection because a calendar can back several of them, and one
2001
+ * collection's unsent edit says nothing about the others. Sharing one set across
2002
+ * the group starved a collection that never even declares `autoPush`: a
2003
+ * neighbour's conflict froze its records — and the sync token still advanced, so
2004
+ * Google never resent them (Codex review #2666). */
1796
2005
  var unpushedFor = (unpushed, slug) => {
1797
2006
  const protection = unpushed.get(slug);
1798
2007
  return protection === void 0 ? NOTHING_UNPUSHED : protection;
@@ -1817,16 +2026,19 @@ var allUnpushed = (unpushed) => new Set([...unpushed.values()].flatMap((ids) =>
1817
2026
  * feature exists to prevent: a calendar whose role degrades to reader refuses
1818
2027
  * the whole push, yet the pull still runs — reading needs no write access — and
1819
2028
  * overwrote every unsent local edit while advancing its baseline past it, so the
1820
- * next push could not even report the conflict (CodeRabbit review #2666).
2029
+ * next push could not even report the conflict (CodeRabbit review #2666). A
2030
+ * collection that never declares `autoPush` is in that same state on every
2031
+ * single run, which is how the loss reappeared without a failure in sight
2032
+ * (#2683).
1821
2033
  *
1822
2034
  * Protects the edited records rather than all of them, so an unchanged record
1823
2035
  * keeps syncing normally. `null` when even that could not be worked out — the
1824
2036
  * caller then refuses to pull the collection at all, because failing open here
1825
2037
  * destroys exactly what this protects. */
1826
- async function protectUnsentEdits(collection, workspaceRoot) {
2038
+ async function protectUnsentEdits(collection, workspaceRoot, deps) {
1827
2039
  try {
1828
- const edited = await unsentLocalEdits(collection, workspaceRoot);
1829
- if (edited.length > 0) log.warn("google", "protecting local edits a failed push could not send", {
2040
+ const edited = await deps.unsentEdits(collection, workspaceRoot);
2041
+ if (edited.length > 0) log.warn("google", "protecting local edits that have not reached Google", {
1830
2042
  slug: collection.slug,
1831
2043
  edited
1832
2044
  });
@@ -1839,8 +2051,18 @@ async function protectUnsentEdits(collection, workspaceRoot) {
1839
2051
  return null;
1840
2052
  }
1841
2053
  }
1842
- /** Push every `autoPush` collection in this group, and answer with the records
1843
- * whose local edit did not reach Google, keyed by collection.
2054
+ var livePullProtectionDeps = {
2055
+ pushNow: pushCollectionNow,
2056
+ unsentEdits: unsentLocalEdits
2057
+ };
2058
+ /** What ONE collection's pull must leave alone, pushing it first if it asked to
2059
+ * be pushed.
2060
+ *
2061
+ * A collection WITHOUT `autoPush` never pushes, so its local edits are unsent by
2062
+ * definition — the same state a failed push leaves behind, and it needs the same
2063
+ * protection. Pulling over them destroys the edit AND advances the baseline past
2064
+ * it, after which no conflict can be detected any more (#2683). "The push did not
2065
+ * run" is the condition that matters here; why it did not run is not.
1844
2066
  *
1845
2067
  * MUST run inside the calendar lock the caller already holds — hence
1846
2068
  * `pushCollectionNow` rather than `pushCalendarForCollection`, which would take
@@ -1848,27 +2070,31 @@ async function protectUnsentEdits(collection, workspaceRoot) {
1848
2070
  *
1849
2071
  * A failed push must not stop the pull: the pull is what keeps the collection
1850
2072
  * fresh, and a revoked write grant is no reason to freeze reading. */
1851
- async function pushAutoCollections(collections, workspaceRoot) {
1852
- const unpushed = /* @__PURE__ */ new Map();
1853
- for (const collection of collections.filter((entry) => entry.schema.googleCalendar?.autoPush)) try {
1854
- const outcome = await pushCollectionNow(collection, workspaceRoot);
2073
+ async function pullProtectionFor(collection, workspaceRoot, deps = livePullProtectionDeps) {
2074
+ if (!collection.schema.googleCalendar?.autoPush) return await protectUnsentEdits(collection, workspaceRoot, deps);
2075
+ try {
2076
+ const outcome = await deps.pushNow(collection, workspaceRoot);
1855
2077
  if (outcome.kind === "pushed") {
1856
2078
  reportAutoPush(collection.slug, outcome.result);
1857
- unpushed.set(collection.slug, new Set(outcome.result.unpushedIds));
1858
- continue;
2079
+ return new Set(outcome.result.unpushedIds);
1859
2080
  }
1860
2081
  log.warn("google", "auto push did not run", {
1861
2082
  slug: collection.slug,
1862
2083
  reason: outcome.kind
1863
2084
  });
1864
- unpushed.set(collection.slug, await protectUnsentEdits(collection, workspaceRoot));
1865
2085
  } catch (error) {
1866
2086
  log.warn("google", "auto push failed — pulling anyway", {
1867
2087
  slug: collection.slug,
1868
2088
  error: String(error)
1869
2089
  });
1870
- unpushed.set(collection.slug, await protectUnsentEdits(collection, workspaceRoot));
1871
2090
  }
2091
+ return await protectUnsentEdits(collection, workspaceRoot, deps);
2092
+ }
2093
+ /** Push the `autoPush` collections in this group, and answer with what every
2094
+ * collection's pull must leave alone, keyed by collection. */
2095
+ async function pushAndProtect(collections, workspaceRoot, deps = livePullProtectionDeps) {
2096
+ const unpushed = /* @__PURE__ */ new Map();
2097
+ for (const collection of collections) unpushed.set(collection.slug, await pullProtectionFor(collection, workspaceRoot, deps));
1872
2098
  return unpushed;
1873
2099
  }
1874
2100
  async function restartFullSync(accessToken, calendarId, workspaceRoot) {
@@ -1945,17 +2171,26 @@ async function releaseCalendarSyncClaim(calendarId, workspaceRoot) {
1945
2171
  });
1946
2172
  }
1947
2173
  }
2174
+ /** Every event whose baseline must NOT advance: what the push could not send,
2175
+ * plus what the apply refused to overwrite.
2176
+ *
2177
+ * The two must agree exactly. The apply's refusals only became visible after it
2178
+ * ran, so they join the union here rather than at push time — leaving them out
2179
+ * would advance the baseline past a record the pull deliberately left holding a
2180
+ * local edit, which is the silent overwrite this whole path exists to stop. */
2181
+ var heldBack = (unpushed, results) => /* @__PURE__ */ new Set([...allUnpushed(unpushed), ...results.flatMap((result) => result.withheld)]);
1948
2182
  async function syncCalendarGroupNow(calendarId, collections, workspaceRoot) {
1949
- const unpushed = await pushAutoCollections(collections, workspaceRoot);
2183
+ const unpushed = await pushAndProtect(collections, workspaceRoot);
2184
+ const baseline = await loadCalendarShadow(calendarId, workspaceRoot);
1950
2185
  const accessToken = await getGoogleAccessToken();
1951
2186
  const first = await syncCalendarEvents(accessToken, {
1952
2187
  calendarId,
1953
2188
  syncToken: await loadCalendarSyncToken(calendarId, workspaceRoot) ?? void 0
1954
2189
  });
1955
2190
  const result = first.fullResyncRequired ? await restartFullSync(accessToken, calendarId, workspaceRoot) : first;
1956
- const results = await applyWindowToGroup(collections, result.events, workspaceRoot, unpushed);
2191
+ const results = await applyWindowToGroup(collections, result.events, workspaceRoot, unpushed, baseline);
1957
2192
  if (windowFullyLanded(calendarId, results)) {
1958
- await saveCalendarShadow(calendarId, shadowUpdates(result.events, allUnpushed(unpushed)), workspaceRoot);
2193
+ await saveCalendarShadow(calendarId, shadowUpdates(result.events, heldBack(unpushed, results), baseline), workspaceRoot);
1959
2194
  if (result.nextSyncToken) await advanceToken(calendarId, result.nextSyncToken, collections, workspaceRoot);
1960
2195
  }
1961
2196
  return results;
@@ -1964,7 +2199,7 @@ async function syncCalendarGroupNow(calendarId, collections, workspaceRoot) {
1964
2199
  * one's own push protected. A collection whose protection could not be worked
1965
2200
  * out is not pulled at all — it reports a retryable error instead, which holds
1966
2201
  * the token and the baseline back for the whole group. */
1967
- async function applyWindowToGroup(collections, events, workspaceRoot, unpushed) {
2202
+ async function applyWindowToGroup(collections, events, workspaceRoot, unpushed, baseline) {
1968
2203
  const results = [];
1969
2204
  for (const collection of collections) {
1970
2205
  const protection = unpushedFor(unpushed, collection.slug);
@@ -1973,8 +2208,9 @@ async function applyWindowToGroup(collections, events, workspaceRoot, unpushed)
1973
2208
  written: 0,
1974
2209
  removed: 0,
1975
2210
  unwritable: [],
2211
+ withheld: [],
1976
2212
  errors: [PROTECTION_UNKNOWN]
1977
- } : await applyEventsToCollection(collection, events, workspaceRoot, protection));
2213
+ } : await applyEventsToCollection(collection, events, workspaceRoot, protection, baseline));
1978
2214
  }
1979
2215
  return results;
1980
2216
  }
@@ -1991,6 +2227,11 @@ function windowFullyLanded(calendarId, results) {
1991
2227
  slug: entry.slug,
1992
2228
  unwritable: entry.unwritable
1993
2229
  }));
2230
+ results.filter((entry) => entry.withheld.length > 0).forEach((entry) => log.warn("google", "leaving calendar records alone — they hold edits Google has not seen", {
2231
+ calendarId,
2232
+ slug: entry.slug,
2233
+ withheld: entry.withheld
2234
+ }));
1994
2235
  const failed = results.filter((entry) => entry.errors.length > 0);
1995
2236
  failed.forEach((entry) => log.warn("google", "holding back calendar sync token after failed writes", {
1996
2237
  calendarId,
@@ -2007,10 +2248,20 @@ function windowFullyLanded(calendarId, results) {
2007
2248
  * keeps the local one would make the next push read a plain one-sided edit —
2008
2249
  * no conflict to detect any more — and quietly overwrite Google. Held back, the
2009
2250
  * baseline stays older than both sides, so the conflict keeps being reported
2010
- * until someone resolves it (#2620). */
2011
- function shadowUpdates(events, unpushed = /* @__PURE__ */ new Set()) {
2012
- const carried = pullableEvents(events, unpushed);
2013
- return Object.fromEntries(carried.map((event) => [event.id, event.status === "cancelled" ? null : toShadowEvent(event)]));
2251
+ * until someone resolves it (#2620).
2252
+ *
2253
+ * `held` carries what those events must KEEP. Omitting them is enough on an
2254
+ * incremental run, where the file is merged rather than replaced — but a full
2255
+ * re-walk CLEARS the baseline first (`restartFullSync`), and there omission
2256
+ * drops the entry for good. The next push would then read a conflicted record
2257
+ * as a brand-new create, hit Google's duplicate-id 409 and refuse it, instead
2258
+ * of reporting the conflict it actually is. Re-stating the pre-run value makes
2259
+ * a held-back event behave the same either way (observed during Claude review;
2260
+ * no bot flagged it). */
2261
+ function shadowUpdates(events, unpushed = /* @__PURE__ */ new Set(), held = {}) {
2262
+ const advanced = pullableEvents(events, unpushed).map((event) => [event.id, event.status === "cancelled" ? null : toShadowEvent(event)]);
2263
+ const kept = [...unpushed].flatMap((eventId) => held[eventId] === void 0 ? [] : [[eventId, held[eventId]]]);
2264
+ return Object.fromEntries([...kept, ...advanced]);
2014
2265
  }
2015
2266
  /** Save the window's token unless every collection that consumed it was deleted
2016
2267
  * while the sync was in flight.
@@ -2043,13 +2294,19 @@ async function pathExists(absPath) {
2043
2294
  async function anySyncedCollectionSurvives(collections, exists = pathExists) {
2044
2295
  return (await Promise.all(collections.map((collection) => exists(collection.skillDir)))).some(Boolean);
2045
2296
  }
2046
- async function applyEventsToCollection(collection, events, workspaceRoot, unpushed) {
2047
- const outcomes = [];
2048
- for (const event of pullableEvents(events, unpushed)) outcomes.push(await applyEvent(collection, event, workspaceRoot));
2297
+ async function applyEventsToCollection(collection, events, workspaceRoot, unpushed, baseline) {
2298
+ const hasUnsentEdit = unsentEditGuard(collection.schema, baseline);
2299
+ const attempts = [];
2300
+ for (const event of pullableEvents(events, unpushed)) attempts.push({
2301
+ eventId: event.id,
2302
+ outcome: await applyEvent(collection, event, workspaceRoot, hasUnsentEdit)
2303
+ });
2304
+ const outcomes = attempts.map((attempt) => attempt.outcome);
2049
2305
  return {
2050
2306
  slug: collection.slug,
2051
2307
  written: outcomes.filter((outcome) => outcome.kind === "written").length,
2052
2308
  removed: outcomes.filter((outcome) => outcome.kind === "removed").length,
2309
+ withheld: attempts.flatMap((attempt) => attempt.outcome.kind === "withheld" ? [attempt.eventId] : []),
2053
2310
  unwritable: outcomes.flatMap((outcome) => outcome.kind === "unwritable" ? [outcome.message] : []),
2054
2311
  errors: outcomes.flatMap((outcome) => outcome.kind === "error" ? [outcome.message] : [])
2055
2312
  };
@@ -2152,6 +2409,7 @@ async function runCalendarGroups(groups, workspaceRoot, mayClaim = ALWAYS_CLAIM)
2152
2409
  written: 0,
2153
2410
  removed: 0,
2154
2411
  unwritable: [],
2412
+ withheld: [],
2155
2413
  errors: [String(error)]
2156
2414
  })));
2157
2415
  }
@@ -2437,6 +2695,6 @@ async function deleteDriveFile(accessToken, input) {
2437
2695
  await googleRequest(DRIVE_API_LABEL, accessToken, `${DRIVE_FILES_URL}/${encodeURIComponent(input.fileId)}`, { method: "DELETE" });
2438
2696
  }
2439
2697
  //#endregion
2440
- export { CANCELLED_EVENT_STATUS, DEFAULT_LIST_MAX_RESULTS, GOOGLE_AUTH_CANCELLED, GOOGLE_CALENDARLIST_SCOPE, GOOGLE_CALENDAR_SCOPE, GOOGLE_CALENDAR_SYNC_TASK_ID, GOOGLE_DRIVE_FILE_SCOPE, GOOGLE_SCOPES, GOOGLE_TASKS_SCOPE, GoogleApiError, HTTP_CONFLICT, HTTP_FORBIDDEN, HTTP_PRECONDITION_FAILED, MAX_LIST_RESULTS, PROTECTION_UNKNOWN, PUSHABLE_SOURCE_FIELDS, allUnpushed, anySyncedCollectionSurvives, assertSafeMimeType, authorizeGoogle, baselineRecord, brokerBaseUrl, brokerExchange, brokerRefresh, brokerStart, buildEventPatch, buildMultipartBody, buildTaskPatch, bySourceField, calendarApiError, calendarPushStatePath, calendarSyncDueWindowMs, calendarSyncStatePath, canonicalCalendarId, canonicalTaskListId, claimCalendarSyncIfDue, classifyDelete, classifyWrite, clearCalendarLastSyncedAt, clearCalendarShadow, clearCalendarSyncToken, clientSecretPresence, collectCalendarPages, commitLinkedTokens, completeTask, configureGoogleHost, conflictingFields, createCalendarEvent, createDriveFile, createGoogleAuthFlow, createTask, deleteCalendarEvent, deleteDriveFile, deleteGoogleTokens, deleteTask, fieldText, findClientSecretPath, getCalendar, getCalendarColors, getCalendarEvent, getGoogleAccessToken, googleApiError, googleAuthFlow, googleCalendarSyncTaskDef, googleConfigDir, googleSecretsDir, googleTokenPath, groupByCalendar, isCalendarSyncDue, isClientSettableEventId, isDeniedAccessRole, isGoogleApiError, isIsoDateTimeWithOffset, isTextMimeType, isUnpushed, legacyGoogleTokenPath, listCalendarEvents, listCalendars, listDriveFiles, listTaskLists, listTasks, loadCalendarLastSyncedAt, loadCalendarShadow, loadCalendarSyncToken, loadClientSecret, loadGoogleTokens, locallyChangedFields, locallyDeletedIds, locallyEditedIds, mayAdoptExisting, mergeGoogleTokens, mergeIntoExisting, mergeShadow, orphanedCalendarId, pickBoundary, planRecord, pullableEvents, pushCalendarForCollection, pushCollectionNow, pushableMap, readDriveFile, releaseOrphanedCalendarToken, resolveEventSpan, saveCalendarShadow, saveCalendarSyncToken, saveGoogleTokens, shadowUpdates, syncCalendarEvents, syncCalendarForCollection, syncCalendarGroup, syncDueCalendarCollections, syncNewCalendarCollections, toCalendarSummary, toCollectionDateTime, toCollectionRecord, toDriveFileSummary, toEventSummary, toGoogleEventTime, toShadowEvent, toTaskListSummary, toTaskSummary, uncompleteTask, unlinkGoogle, unpushedFor, unsentLocalEdits, unsyncedGroups, updateCalendarEvent, updateTask, waitForAuthCode, withCalendarLock, withKeyedLock, zoneSuffixOf };
2698
+ export { CANCELLED_EVENT_STATUS, DEFAULT_LIST_MAX_RESULTS, GOOGLE_AUTH_CANCELLED, GOOGLE_CALENDARLIST_SCOPE, GOOGLE_CALENDAR_SCOPE, GOOGLE_CALENDAR_SYNC_TASK_ID, GOOGLE_DRIVE_FILE_SCOPE, GOOGLE_SCOPES, GOOGLE_TASKS_SCOPE, GoogleApiError, HTTP_CONFLICT, HTTP_FORBIDDEN, HTTP_PRECONDITION_FAILED, MAX_LIST_RESULTS, PROTECTION_UNKNOWN, PUSHABLE_SOURCE_FIELDS, allUnpushed, anySyncedCollectionSurvives, applyPlanFor, assertSafeMimeType, authorizeGoogle, baselineRecord, brokerBaseUrl, brokerExchange, brokerRefresh, brokerStart, buildEventPatch, buildMultipartBody, buildTaskPatch, bySourceField, calendarApiError, calendarPushStatePath, calendarSyncDueWindowMs, calendarSyncStatePath, canonicalCalendarId, canonicalTaskListId, claimCalendarSyncIfDue, classifyDelete, classifyWrite, clearCalendarLastSyncedAt, clearCalendarShadow, clearCalendarSyncToken, clientSecretPresence, collectCalendarPages, commitLinkedTokens, completeTask, configureGoogleHost, conflictingFields, createCalendarEvent, createDriveFile, createGoogleAuthFlow, createTask, deleteCalendarEvent, deleteDriveFile, deleteGoogleTokens, deleteTask, fieldText, findClientSecretPath, getCalendarColors, getCalendarEvent, getCalendarMeta, getGoogleAccessToken, googleApiError, googleAuthFlow, googleCalendarSyncTaskDef, googleConfigDir, googleSecretsDir, googleTokenPath, groupByCalendar, heldBack, isCalendarSyncDue, isClientSettableEventId, isDeniedAccessRole, isGoogleApiError, isIsoDateTimeWithOffset, isTextMimeType, isUnpushed, legacyGoogleTokenPath, listCalendarEvents, listCalendars, listDriveFiles, listTaskLists, listTasks, liveLockClock, loadCalendarLastSyncedAt, loadCalendarShadow, loadCalendarSyncToken, loadClientSecret, loadGoogleTokens, locallyChangedFields, locallyDeletedIds, locallyEditedIds, mayAdoptExisting, mergeGoogleTokens, mergeIntoExisting, mergeShadow, orphanedCalendarId, pickBoundary, planRecord, pullProtectionFor, pullableEvents, pushAndProtect, pushCalendarForCollection, pushCollectionNow, pushableMap, readDriveFile, releaseOrphanedCalendarToken, reportedAccessRole, resolveEventSpan, saveCalendarShadow, saveCalendarSyncToken, saveGoogleTokens, shadowUpdates, stateLockPath, syncCalendarEvents, syncCalendarForCollection, syncCalendarGroup, syncDueCalendarCollections, syncNewCalendarCollections, toCalendarMeta, toCalendarSummary, toCollectionDateTime, toCollectionRecord, toDriveFileSummary, toEventSummary, toGoogleEventTime, toShadowEvent, toTaskListSummary, toTaskSummary, uncompleteTask, unlinkGoogle, unpushedFor, unsentEditGuard, unsentLocalEdits, unsyncedGroups, updateCalendarEvent, updateTask, waitForAuthCode, withCalendarLock, withCalendarStateLock, withKeyedLock, zoneSuffixOf };
2441
2699
 
2442
2700
  //# sourceMappingURL=index.js.map