{"version":3,"file":"index.cjs","names":[],"sources":["../../src/google/host.ts","../../src/google/datetime.ts","../../src/google/paths.ts","../../src/google/util.ts","../../src/google/clientSecret.ts","../../src/google/fsJson.ts","../../src/google/tokenStore.ts","../../src/google/broker.ts","../../src/google/auth.ts","../../src/google/authFlow.ts","../../src/google/apiClient.ts","../../src/google/calendar.ts","../../src/google/calendarStateLock.ts","../../src/google/calendarSyncStore.ts","../../src/google/calendarSyncDue.ts","../../src/google/calendarBackfillState.ts","../../src/google/calendarPushState.ts","../../src/google/calendarLock.ts","../../src/google/pushDateTime.ts","../../src/google/collectionDateTime.ts","../../src/google/collectionProjection.ts","../../src/google/pushPlan.ts","../../src/google/collectionPush.ts","../../src/google/collectionSync.ts","../../src/google/tasks.ts","../../src/google/driveFile.ts"],"sourcesContent":["// Host binding for the Google engine. The engine logs through the host's\n// logger, but a package-level import of the host logger would be an uphill\n// dependency — so the host injects it once at startup (same pattern as\n// `collection/server/host.ts`). The default is silent so the engine works\n// unconfigured in unit tests.\n\nimport { createForwardingLogger, createHostSlot, type StructuredLogger } from \"../host/hostSlot.js\";\n\n/** Public alias of the shared `StructuredLogger` — keeps the domain surface name-stable. */\nexport type GoogleLogger = StructuredLogger;\n\nconst hostSlot = createHostSlot<GoogleLogger>(\"@mulmoclaude/core/google: configureGoogleHost()\");\n\nexport function configureGoogleHost(binding: { log: GoogleLogger }): void {\n  hostSlot.set(binding.log);\n}\n\nexport const log: GoogleLogger = createForwardingLogger(() => hostSlot.peek());\n","// Strict RFC3339 date-time validation shared by every surface that feeds\n// Calendar `dateTime`/`timeMin` values (agent tool args, remote-host command\n// params). Calendar rejects date-only / offset-less values with an opaque\n// 400, so callers validate here and return an actionable message instead.\n\n// Fractional seconds are normalized away first — an optional `(\\.\\d+)?`\n// group inside the main pattern trips security/detect-unsafe-regex.\nconst ISO_DATE_TIME_WITH_OFFSET_RE = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:Z|[+-](\\d{2}):(\\d{2}))$/;\nconst FRACTIONAL_SECONDS_RE = /\\.\\d+(?=Z|[+-])/;\n\nconst MAX_HOUR = 23;\nconst MAX_MINUTE = 59;\nconst MAX_SECOND = 59;\n\n// JS Date normalizes overflowed components (2026-02-31 parses as\n// 2026-03-03), so `new Date(value)` alone cannot reject impossible dates —\n// a UTC round-trip of the raw components can.\nconst isRealCalendarDate = (year: number, month: number, day: number): boolean => {\n  const roundTrip = new Date(Date.UTC(year, month - 1, day));\n  return roundTrip.getUTCFullYear() === year && roundTrip.getUTCMonth() === month - 1 && roundTrip.getUTCDate() === day;\n};\n\nexport const isIsoDateTimeWithOffset = (value: string): boolean => {\n  const match = ISO_DATE_TIME_WITH_OFFSET_RE.exec(value.replace(FRACTIONAL_SECONDS_RE, \"\"));\n  if (!match) return false;\n  const [year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0] = match.slice(1, 7).map(Number);\n  const timeInRange = hour <= MAX_HOUR && minute <= MAX_MINUTE && second <= MAX_SECOND;\n  // RFC3339 bounds the offset to 00-23:59; groups 7/8 are undefined for \"Z\".\n  const offsetInRange = match[7] === undefined || (Number(match[7]) <= MAX_HOUR && Number(match[8]) <= MAX_MINUTE);\n  return isRealCalendarDate(year, month, day) && timeInRange && offsetInRange;\n};\n","// Google OAuth material lives OUTSIDE the workspace: the client secret is\n// machine-only (never synced) and the refresh token must survive workspace\n// resets — same reasoning as the gcloud / gh CLI model. The dir is the\n// host-NEUTRAL `~/.config/mulmo` because this engine is shared by both\n// MulmoClaude and MulmoTerminal, which deliberately share one grant per\n// machine. The `home` parameter exists so tests can thread a fake home.\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { mulmoConfigDir } from \"../global-config/paths.js\";\n\n/** Same directory as every other cross-app per-user file — see\n *  `../global-config/paths.ts`, which owns the spelling. */\nexport function googleConfigDir(home?: string): string {\n  return mulmoConfigDir(home);\n}\n\n/** Pre-0.20.1 token dir (mulmoclaude-branded); reads migrate away from it. */\nexport function legacyGoogleTokenPath(home?: string): string {\n  return join(home ?? homedir(), \".config\", \"mulmoclaude\", \"google-token.json\");\n}\n\nexport function googleTokenPath(home?: string): string {\n  return join(googleConfigDir(home), \"google-token.json\");\n}\n\nexport function googleSecretsDir(home?: string): string {\n  return join(home ?? homedir(), \".secrets\");\n}\n","// Time + type helpers kept local so the Google engine carries no host\n// dependency. `errorMessage` / `truncate` now come from the canonical core\n// helpers (#2217): the local copies had drifted — `errorMessage` collapsed\n// gRPC-shaped errors to \"unknown error\" instead of surfacing their `details`,\n// and `truncate` dropped the guard that keeps output within `max`.\nexport { errorMessage } from \"../utils/errors.js\";\nexport { truncate } from \"../utils/text.js\";\n\nexport const ONE_SECOND_MS = 1_000;\nexport const ONE_MINUTE_MS = 60_000;\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","// Loads the Google OAuth **desktop-app** client credentials a user may have\n// downloaded from the Cloud Console into `~/.secrets/client_secret_*.json`.\n// Files are discovered by prefix so nobody has to rename Google's long default\n// filename.\n//\n// Only `{\"installed\": …}` (desktop) files count. A `{\"web\": …}` client cannot\n// drive the loopback consent this engine runs — and one legitimately sits in\n// the same directory for anyone who also deploys the broker — so web clients\n// are skipped rather than treated as a competing choice.\nimport { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { isRecord } from \"./util.js\";\nimport { googleSecretsDir } from \"./paths.js\";\n\nexport interface InstalledClientSecret {\n  client_id: string;\n  client_secret: string;\n}\n\n// Empty strings must not count: they would satisfy a `typeof` check, get\n// picked as the client, and fail at Google with an opaque invalid_client.\nconst isNonEmptyString = (value: unknown): value is string => typeof value === \"string\" && value !== \"\";\n\nconst isInstalledClientSecret = (value: unknown): value is { installed: InstalledClientSecret } => {\n  if (!isRecord(value) || !isRecord(value.installed)) return false;\n  return isNonEmptyString(value.installed.client_id) && isNonEmptyString(value.installed.client_secret);\n};\n\nconst isClientSecretFileName = (name: string): boolean => name.startsWith(\"client_secret_\") && name.endsWith(\".json\");\n\nconst readIfDesktopClient = async (filePath: string): Promise<string | null> => {\n  try {\n    const parsed: unknown = JSON.parse(await readFile(filePath, \"utf-8\"));\n    return isInstalledClientSecret(parsed) ? filePath : null;\n  } catch {\n    // Unreadable or malformed: not a usable desktop client, and refusing the\n    // whole directory over one stray file would be worse than ignoring it.\n    return null;\n  }\n};\n\n/** Absolute paths of every desktop-app client JSON in `~/.secrets/`. */\nasync function listDesktopClientSecretFiles(home?: string): Promise<string[]> {\n  const dir = googleSecretsDir(home);\n  const entries = await readdir(dir).catch((): string[] => []);\n  const candidates = entries.filter(isClientSecretFileName).sort();\n  const checked = await Promise.all(candidates.map((name) => readIfDesktopClient(join(dir, name))));\n  return checked.filter((path): path is string => path !== null);\n}\n\n/** `missing` is the ordinary case — the broker supplies the client, so no user\n *  action is needed. `ambiguous` (2+ desktop clients) still needs a human:\n *  a stored refresh token pairs with exactly one client_id, so picking for\n *  them could silently break the link. */\nexport type ClientSecretPresence = \"found\" | \"missing\" | \"ambiguous\";\n\nexport async function clientSecretPresence(home?: string): Promise<ClientSecretPresence> {\n  const matches = await listDesktopClientSecretFiles(home);\n  if (matches.length === 0) return \"missing\";\n  return matches.length === 1 ? \"found\" : \"ambiguous\";\n}\n\nexport async function findClientSecretPath(home?: string): Promise<string> {\n  const dir = googleSecretsDir(home);\n  const matches = await listDesktopClientSecretFiles(home);\n  const [first, ...rest] = matches;\n  if (!first) {\n    throw new Error(`no desktop-app client_secret_*.json found in ${dir} — this host links through the sign-in service instead`);\n  }\n  if (rest.length > 0) {\n    const names = matches.map((path) => path.slice(dir.length + 1)).join(\", \");\n    throw new Error(`multiple desktop-app client_secret_*.json files found in ${dir} (${names}) — keep exactly one`);\n  }\n  return first;\n}\n\nexport async function loadClientSecret(home?: string): Promise<InstalledClientSecret> {\n  const filePath = await findClientSecretPath(home);\n  const parsed: unknown = JSON.parse(await readFile(filePath, \"utf-8\"));\n  if (!isInstalledClientSecret(parsed)) {\n    throw new Error(`${filePath} is not a desktop-app OAuth client secret (missing \"installed\" with client_id / client_secret)`);\n  }\n  return { client_id: parsed.installed.client_id, client_secret: parsed.installed.client_secret };\n}\n","// Minimal JSON file I/O for the token store. The token file must be 0600, so\n// the write goes through the shared atomic writer with an explicit `mode`.\nimport { promises as fsp } from \"node:fs\";\nimport { writeFileAtomic } from \"../files/atomic.js\";\n\nconst JSON_INDENT = 2;\n\nexport async function readJsonOrNull<T>(filePath: string): Promise<T | null> {\n  try {\n    const raw = await fsp.readFile(filePath, \"utf-8\");\n    const parsed: T = JSON.parse(raw);\n    return parsed;\n  } catch {\n    return null;\n  }\n}\n\n/** tmp-write + rename so readers never see a half-written file; `mode` applies\n *  to the tmp file and survives the rename. A single-writer token file, so the\n *  staging name is stable (`uniqueTmp: false`). */\nexport async function writeJsonAtomicWithMode(filePath: string, data: unknown, mode: number): Promise<void> {\n  await writeFileAtomic(filePath, JSON.stringify(data, null, JSON_INDENT), { mode, uniqueTmp: false });\n}\n","// Persistence for the Google OAuth tokens (refresh + access) at\n// `~/.config/mulmo/google-token.json`, mode 600. Google omits\n// `refresh_token` from refresh responses, so merges must preserve the one we\n// already hold — losing it forces the user through the browser consent again.\nimport { constants as fsConstants, copyFile, mkdir, rm, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { Credentials } from \"google-auth-library\";\nimport { readJsonOrNull, writeJsonAtomicWithMode } from \"./fsJson.js\";\nimport { googleTokenPath, legacyGoogleTokenPath } from \"./paths.js\";\n\nconst TOKEN_FILE_MODE = 0o600;\n\n/** Which client minted the grant: `local` = the user's own client JSON in\n *  `~/.secrets/`, `broker` = the mulmoserver broker. Renewal must use the same\n *  path, since only that client's secret can refresh its tokens. Absent on\n *  tokens written before the broker existed — those are all `local`. */\nexport type IssuedVia = \"local\" | \"broker\";\n\nexport interface StoredGoogleTokens extends Credentials {\n  issuedVia?: IssuedVia;\n}\n\nexport function mergeGoogleTokens(existing: StoredGoogleTokens | null, incoming: StoredGoogleTokens): StoredGoogleTokens {\n  const merged = { ...existing, ...incoming };\n  if (!incoming.refresh_token && existing?.refresh_token) merged.refresh_token = existing.refresh_token;\n  // A broker refresh response carries no marker; keep the one from the link.\n  if (!incoming.issuedVia && existing?.issuedVia) merged.issuedVia = existing.issuedVia;\n  return merged;\n}\n\nconst fileExists = async (filePath: string): Promise<boolean> =>\n  await stat(filePath).then(\n    () => true,\n    () => false,\n  );\n\n// Tokens written before 0.20.1 live under the mulmoclaude-branded dir; move\n// them once. COPYFILE_EXCL makes the create atomic-and-non-clobbering — an\n// exists+rename sequence could overwrite a token a concurrent process wrote\n// to the new path in between (TOCTOU). The legacy file is deleted only after\n// a successful copy; on EEXIST (new path won a race, or both files already\n// exist) it is left for any older install still reading it. copyFile\n// preserves the 600 mode.\nasync function migrateLegacyTokenFile(home?: string): Promise<void> {\n  const current = googleTokenPath(home);\n  const legacy = legacyGoogleTokenPath(home);\n  if (!(await fileExists(legacy))) return;\n  await mkdir(path.dirname(current), { recursive: true });\n  try {\n    await copyFile(legacy, current, fsConstants.COPYFILE_EXCL);\n  } catch {\n    return;\n  }\n  await rm(legacy, { force: true });\n}\n\nexport async function loadGoogleTokens(home?: string): Promise<StoredGoogleTokens | null> {\n  await migrateLegacyTokenFile(home).catch(() => undefined);\n  const current = await readJsonOrNull<StoredGoogleTokens>(googleTokenPath(home));\n  if (current) return current;\n  // Migration is best-effort — a valid legacy token must still count as\n  // linked even when the move failed (permissions, read-only fs, …).\n  return await readJsonOrNull<StoredGoogleTokens>(legacyGoogleTokenPath(home));\n}\n\nexport async function saveGoogleTokens(incoming: StoredGoogleTokens, home?: string): Promise<StoredGoogleTokens> {\n  const merged = mergeGoogleTokens(await loadGoogleTokens(home), incoming);\n  await writeJsonAtomicWithMode(googleTokenPath(home), merged, TOKEN_FILE_MODE);\n  return merged;\n}\n\nexport async function deleteGoogleTokens(home?: string): Promise<void> {\n  await rm(googleTokenPath(home), { force: true });\n}\n","// Client for the mulmoserver OAuth broker (receptron/mulmoserver#54).\n//\n// Why a broker: Google requires a client_secret at the token endpoint even for\n// PKCE flows, so a user without their own Cloud project cannot complete a link\n// on their own. The broker holds the secret and applies it — it never stores or\n// returns it, and tokens still live only on this machine.\n//\n// What proves this host started the flow is the PKCE code_verifier: anyone can\n// mint a `state` at /googleOAuthStart, so the verifier — not the state — is the\n// authorization. See the broker's pkce.ts for the same reasoning server-side.\nimport type { Credentials } from \"google-auth-library\";\nimport { fetchWithTimeout } from \"../utils/fetch.js\";\nimport { errorMessage, isRecord, ONE_SECOND_MS } from \"./util.js\";\n\nconst DEFAULT_BROKER_BASE_URL = \"https://asia-northeast1-mulmoserver.cloudfunctions.net\";\nconst BROKER_TIMEOUT_MS = 20 * ONE_SECOND_MS;\n\n// Trailing slashes are stripped by hand: `/\\/+$/` backtracks super-linearly on\n// a long run of slashes, which lint rejects.\nconst withoutTrailingSlashes = (url: string): string => {\n  let end = url.length;\n  while (end > 0 && url[end - 1] === \"/\") end -= 1;\n  return url.slice(0, end);\n};\n\n/** `MULMO_GOOGLE_BROKER_URL` lets a fork / staging deploy point elsewhere\n *  without a code change; unset means the shipped broker. */\nexport const brokerBaseUrl = (override: string | undefined = process.env.MULMO_GOOGLE_BROKER_URL): string =>\n  withoutTrailingSlashes(override ?? DEFAULT_BROKER_BASE_URL);\n\nexport interface BrokerStartResponse {\n  authUrl: string;\n  state: string;\n}\n\nconst LOOPBACK_HOSTNAMES = new Set([\"127.0.0.1\", \"localhost\", \"::1\", \"[::1]\"]);\n\n/** Codes, PKCE verifiers and refresh tokens travel to the broker, so cleartext\n *  is refused outright — an override pointed at `http://…` would put them on\n *  the wire. Loopback stays allowed: that is where tests stub the broker, and\n *  it never leaves the machine. */\nconst assertSecureBrokerUrl = (url: string): void => {\n  let parsed: URL;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`invalid Google sign-in service URL: ${url}`);\n  }\n  if (parsed.protocol === \"https:\") return;\n  if (parsed.protocol === \"http:\" && LOOPBACK_HOSTNAMES.has(parsed.hostname)) return;\n  throw new Error(`the Google sign-in service must be reached over HTTPS (got ${parsed.protocol}//${parsed.host})`);\n};\n\nconst brokerFetch = async (url: string, init: { method?: string; body?: string } = {}): Promise<unknown> => {\n  assertSecureBrokerUrl(url);\n  let response: Response;\n  try {\n    response = await fetchWithTimeout(url, {\n      ...init,\n      timeoutMs: BROKER_TIMEOUT_MS,\n      headers: { \"Content-Type\": \"application/json\" },\n    });\n  } catch (err) {\n    throw new Error(`Google sign-in service unreachable — check the network connection and retry (${errorMessage(err)})`);\n  }\n  if (!response.ok) {\n    // The broker answers deliberately opaque errors (it must not help probe\n    // codes / refresh tokens), so there is nothing more specific to surface.\n    throw new Error(`Google sign-in service returned HTTP ${response.status}`);\n  }\n  try {\n    return await response.json();\n  } catch {\n    // A proxy / captive portal answering 200 with HTML would otherwise surface\n    // as a raw SyntaxError the agent can't act on.\n    throw new Error(\"Google sign-in service returned a malformed response\");\n  }\n};\n\nconst GOOGLE_CONSENT_HOST = \"accounts.google.com\";\n\n/** The returned URL is opened in the user's browser, so it must be Google's\n *  own consent page and nothing else — a mis-pointed or hostile broker (the\n *  base URL is overridable) could otherwise hand back a phishing page wearing\n *  a sign-in flow. Cheap to assert, and Google's endpoint host is fixed. */\nconst assertGoogleConsentUrl = (authUrl: string): void => {\n  let parsed: URL;\n  try {\n    parsed = new URL(authUrl);\n  } catch {\n    throw new Error(\"Google sign-in service returned an unusable authorization URL\");\n  }\n  if (parsed.protocol !== \"https:\" || parsed.hostname !== GOOGLE_CONSENT_HOST) {\n    throw new Error(`Google sign-in service returned an authorization URL that is not Google's consent page (${parsed.protocol}//${parsed.host})`);\n  }\n};\n\nexport async function brokerStart(port: number, codeChallenge: string, baseUrl = brokerBaseUrl()): Promise<BrokerStartResponse> {\n  const params = new URLSearchParams({ port: String(port), code_challenge: codeChallenge });\n  const payload = await brokerFetch(`${baseUrl}/googleOAuthStart?${params.toString()}`);\n  const record = isRecord(payload) ? payload : {};\n  if (typeof record.auth_url !== \"string\" || typeof record.state !== \"string\") {\n    throw new Error(\"Google sign-in service returned an unexpected response (missing auth_url / state)\");\n  }\n  assertGoogleConsentUrl(record.auth_url);\n  return { authUrl: record.auth_url, state: record.state };\n}\n\nconst toCredentials = (payload: unknown, existingRefreshToken?: string): Credentials => {\n  const record = isRecord(payload) ? payload : {};\n  if (typeof record.access_token !== \"string\") {\n    throw new Error(\"Google sign-in service returned no access token\");\n  }\n  // The refresh endpoint does not echo the refresh_token back — keep the one we\n  // already hold so a renewal can't silently unlink the account.\n  const refreshToken = typeof record.refresh_token === \"string\" ? record.refresh_token : existingRefreshToken;\n  return {\n    access_token: record.access_token,\n    ...(refreshToken ? { refresh_token: refreshToken } : {}),\n    ...(typeof record.expiry_date === \"number\" ? { expiry_date: record.expiry_date } : {}),\n  };\n};\n\nexport async function brokerExchange(input: { code: string; state: string; codeVerifier: string }, baseUrl = brokerBaseUrl()): Promise<Credentials> {\n  const payload = await brokerFetch(`${baseUrl}/googleOAuthExchange`, {\n    method: \"POST\",\n    body: JSON.stringify({ code: input.code, state: input.state, code_verifier: input.codeVerifier }),\n  });\n  const credentials = toCredentials(payload);\n  if (!credentials.refresh_token) {\n    throw new Error(\"Google returned no refresh token — remove this app under Google Account → Security → Third-party access, then retry\");\n  }\n  return credentials;\n}\n\nexport async function brokerRefresh(refreshToken: string, baseUrl = brokerBaseUrl()): Promise<Credentials> {\n  const payload = await brokerFetch(`${baseUrl}/googleOAuthRefresh`, {\n    method: \"POST\",\n    body: JSON.stringify({ refresh_token: refreshToken }),\n  });\n  return toCredentials(payload, refreshToken);\n}\n","// Google OAuth for the host machine, independent of Firebase Auth (which\n// discards refresh tokens). Two ways to reach a linked account:\n//\n//   - LOCAL: the user dropped their own desktop-app client JSON in\n//     `~/.secrets/`. Everything (consent, exchange, refresh) happens on this\n//     machine — nothing but Google is contacted.\n//   - BROKER (the default): no client JSON, so the mulmoserver broker applies\n//     the client secret for the exchange / refresh it cannot be done without.\n//     Tokens still only ever live here. See broker.ts.\n//\n// Either way the loopback listener, the PKCE verifier, and the token file are\n// this machine's. `issuedVia` on the stored token records which path minted it\n// so renewals take the matching one.\nimport { randomBytes } from \"node:crypto\";\nimport http from \"node:http\";\nimport { CodeChallengeMethod, OAuth2Client, type Credentials } from \"google-auth-library\";\nimport { brokerExchange, brokerRefresh, brokerStart } from \"./broker.js\";\nimport { log } from \"./host.js\";\nimport { errorMessage, ONE_MINUTE_MS, ONE_SECOND_MS } from \"./util.js\";\nimport { fetchWithTimeout } from \"../utils/fetch.js\";\nimport { clientSecretPresence, loadClientSecret, type InstalledClientSecret } from \"./clientSecret.js\";\nimport { deleteGoogleTokens, loadGoogleTokens, saveGoogleTokens, type IssuedVia } from \"./tokenStore.js\";\n\nexport const GOOGLE_CALENDAR_SCOPE = \"https://www.googleapis.com/auth/calendar.events\";\n/** Minimal read scope for the calendar list + per-calendar colours.\n *  `calendar.events` already covers reading/writing events on any calendar, but\n *  discovering WHICH calendars the user has (CalendarList.list) needs a\n *  calendar-list read scope — this is the narrowest one that grants it. Kept in\n *  step with the broker's consent scopes so local- and broker-linked accounts\n *  grant the same set. */\nexport const GOOGLE_CALENDARLIST_SCOPE = \"https://www.googleapis.com/auth/calendar.calendarlist.readonly\";\nexport const GOOGLE_TASKS_SCOPE = \"https://www.googleapis.com/auth/tasks\";\nexport const GOOGLE_DRIVE_FILE_SCOPE = \"https://www.googleapis.com/auth/drive.file\";\n/** Requested at consent as one set — matches the scopes registered on the\n *  OAuth consent screen, so a single re-link covers every supported API. */\nexport const GOOGLE_SCOPES = [GOOGLE_CALENDAR_SCOPE, GOOGLE_CALENDARLIST_SCOPE, GOOGLE_TASKS_SCOPE, GOOGLE_DRIVE_FILE_SCOPE];\nconst CALLBACK_PATH = \"/oauth2callback\";\nconst AUTH_TIMEOUT_MS = 5 * ONE_MINUTE_MS;\nconst STATE_BYTES = 16;\n/** Renew a minute early so a call can't start with a token that expires\n *  mid-flight. */\nconst EXPIRY_MARGIN_MS = ONE_MINUTE_MS;\n\nexport interface AuthorizeGoogleOptions {\n  home?: string;\n  /** Called with the consent URL; open it in a browser (and/or print it). */\n  onAuthUrl?: (url: string) => void;\n  timeoutMs?: number;\n  /** Abort a still-pending consent (a settings-UI restart, an explicit cancel).\n   *  Rejects the flow with {@link GOOGLE_AUTH_CANCELLED} and closes the loopback. */\n  signal?: AbortSignal;\n}\n\nconst createClient = (secret: InstalledClientSecret, redirectUri?: string): OAuth2Client =>\n  new OAuth2Client({ clientId: secret.client_id, clientSecret: secret.client_secret, ...(redirectUri !== undefined ? { redirectUri } : {}) });\n\nconst persistRotatedTokens = (client: OAuth2Client, home?: string): void => {\n  client.on(\"tokens\", (tokens) => {\n    saveGoogleTokens(tokens, home).catch((err: unknown) => {\n      log.error(\"google\", \"failed to persist rotated tokens\", { error: String(err) });\n    });\n  });\n};\n\nconst REVOKED_GRANT_MESSAGE = \"could not obtain a Google access token — the grant may have been revoked; re-link the account\";\n\nconst localAccessToken = async (saved: Credentials, home?: string): Promise<string> => {\n  const presence = await clientSecretPresence(home);\n  // Two desktop clients: the token is still renewable — the engine just can't\n  // tell which client minted it. The fix is to remove the duplicate, so raise\n  // the loader's own wording rather than sending the user through a re-link.\n  if (presence === \"ambiguous\") await loadClientSecret(home);\n  // No client at all: the token is bound to the client_id that minted it, so\n  // nothing — not even the broker — can renew it. Re-linking is the only way\n  // out; the loader's \"no desktop client\" wording would read like a setup\n  // problem instead.\n  if (presence === \"missing\") {\n    throw new Error(\n      \"the saved Google link was created with an OAuth client that is no longer configured on this host — ask the user to link their Google account again in this app's settings\",\n    );\n  }\n  const client = createClient(await loadClientSecret(home));\n  client.setCredentials(saved);\n  persistRotatedTokens(client, home);\n  const { token } = await client.getAccessToken();\n  if (!token) throw new Error(REVOKED_GRANT_MESSAGE);\n  return token;\n};\n\n// The broker mints access tokens because only it holds the client secret. The\n// refreshed token is written back so the next call can reuse it until expiry\n// instead of hitting the broker every time.\nconst brokerAccessToken = async (saved: Credentials, home?: string): Promise<string> => {\n  if (typeof saved.expiry_date === \"number\" && saved.access_token && saved.expiry_date - EXPIRY_MARGIN_MS > Date.now()) {\n    return saved.access_token;\n  }\n  const refreshed = await brokerRefresh(saved.refresh_token ?? \"\");\n  if (!refreshed.access_token) throw new Error(REVOKED_GRANT_MESSAGE);\n  await saveGoogleTokens(refreshed, home);\n  return refreshed.access_token;\n};\n\nexport async function getGoogleAccessToken(home?: string): Promise<string> {\n  const saved = await loadGoogleTokens(home);\n  if (!saved?.refresh_token) {\n    // Host-neutral wording — this engine ships to multiple hosts whose link\n    // flows differ (#2128); each host's own help carries the specific steps.\n    throw new Error(\"Google account not linked on this host — ask the user to link their Google account in this app's settings, then retry\");\n  }\n  // Tokens written before the broker existed carry no marker; they were all\n  // minted from a local client, so that stays the default.\n  return saved.issuedVia === \"broker\" ? await brokerAccessToken(saved, home) : await localAccessToken(saved, home);\n}\n\nconst REVOKE_URL = \"https://oauth2.googleapis.com/revoke\";\n\n/** The revoke POST, injectable for tests. */\nexport type RevokeFetch = typeof fetchWithTimeout;\n\n/** Revoke the grant at Google (best-effort) and delete the local token file.\n *  Revoke failures are logged but never block the local delete — Google may\n *  already consider the token invalid, and keeping the file would leave the\n *  user unable to unlink. */\nexport async function unlinkGoogle(home?: string, revokeFetch: RevokeFetch = fetchWithTimeout): Promise<void> {\n  const saved = await loadGoogleTokens(home);\n  const token = saved?.refresh_token ?? saved?.access_token;\n  if (token) {\n    try {\n      const response = await revokeFetch(REVOKE_URL, {\n        method: \"POST\",\n        headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n        body: new URLSearchParams({ token }).toString(),\n      });\n      if (!response.ok) log.warn(\"google\", \"token revoke returned non-ok\", { status: response.status });\n    } catch (err) {\n      log.warn(\"google\", \"token revoke failed, deleting local tokens anyway\", { error: errorMessage(err) });\n    }\n  }\n  await deleteGoogleTokens(home);\n}\n\nconst startLoopbackServer = (): Promise<{ server: http.Server; port: number }> =>\n  new Promise((resolve, reject) => {\n    const server = http.createServer();\n    server.once(\"error\", reject);\n    server.listen(0, \"127.0.0.1\", () => {\n      const address = server.address();\n      if (address === null || typeof address === \"string\") {\n        reject(new Error(\"loopback server has no port\"));\n        return;\n      }\n      resolve({ server, port: address.port });\n    });\n  });\n\n// State is validated before error/code — a callback that can't prove it\n// belongs to this flow must not influence it (its `error` text would\n// otherwise reach the terminal attacker-controlled).\nconst authCodeFromCallback = (url: URL, expectedState: string): string => {\n  if (url.searchParams.get(\"state\") !== expectedState) throw new Error(\"OAuth state mismatch — possible CSRF, aborting\");\n  const error = url.searchParams.get(\"error\");\n  if (error) throw new Error(`Google authorization failed: ${error}`);\n  const code = url.searchParams.get(\"code\");\n  if (!code) throw new Error(\"authorization callback carried no code\");\n  return code;\n};\n\nconst respondHtml = (res: http.ServerResponse, status: number, message: string): void => {\n  res.writeHead(status, { \"Content-Type\": \"text/html; charset=utf-8\" });\n  res.end(`<html><body><h3>${message}</h3></body></html>`);\n};\n\n/** Reject reason when a pending flow is torn down (a settings-UI restart or an\n *  explicit cancel), kept distinct from a real authorization error so callers\n *  can treat it as \"user changed their mind\", not \"linking failed\". */\nexport const GOOGLE_AUTH_CANCELLED = \"authorization cancelled\";\n\ninterface AuthCodeSettle {\n  resolve: (code: string) => void;\n  reject: (err: unknown) => void;\n}\n\n// Answers one loopback request. A terminal callback settles the flow; a\n// wrong-path or wrong-state request is answered but leaves the flow waiting —\n// a drive-by localhost probe or stale tab must not abort a pending consent.\nconst handleCallbackRequest = (req: http.IncomingMessage, res: http.ServerResponse, expectedState: string, settle: AuthCodeSettle): void => {\n  const url = new URL(req.url ?? \"/\", \"http://127.0.0.1\");\n  if (url.pathname !== CALLBACK_PATH) {\n    res.writeHead(404);\n    res.end();\n    return;\n  }\n  if (url.searchParams.get(\"state\") !== expectedState) {\n    respondHtml(res, 400, \"Invalid authorization callback. You can close this tab.\");\n    return;\n  }\n  try {\n    const code = authCodeFromCallback(url, expectedState);\n    respondHtml(res, 200, \"Authorization complete — you can close this tab.\");\n    settle.resolve(code);\n  } catch (err) {\n    // Static text only — the failure detail echoes query-string content, which\n    // must not be reflected into HTML. The CLI prints the detail.\n    respondHtml(res, 400, \"Authorization failed — see the terminal for details. You can close this tab.\");\n    settle.reject(err);\n  }\n};\n\n// Resolves with the OAuth code from the loopback callback, or rejects on the\n// timeout or on `signal` firing (a restart / cancel) — whichever comes first.\nexport const waitForAuthCode = (server: http.Server, expectedState: string, timeoutMs: number, signal?: AbortSignal): Promise<string> =>\n  new Promise<string>((resolve, reject) => {\n    const timer = setTimeout(() => reject(new Error(`authorization timed out after ${timeoutMs / ONE_SECOND_MS}s`)), timeoutMs);\n    const settle: AuthCodeSettle = {\n      resolve: (code) => {\n        clearTimeout(timer);\n        resolve(code);\n      },\n      reject: (err) => {\n        clearTimeout(timer);\n        reject(err instanceof Error ? err : new Error(String(err)));\n      },\n    };\n    if (signal) {\n      if (signal.aborted) {\n        settle.reject(new Error(GOOGLE_AUTH_CANCELLED));\n        return;\n      }\n      signal.addEventListener(\"abort\", () => settle.reject(new Error(GOOGLE_AUTH_CANCELLED)), { once: true });\n    }\n    server.on(\"request\", (req, res) => handleCallbackRequest(req, res, expectedState, settle));\n  });\n\n// `access_type: offline` + `prompt: consent` force Google to return a refresh\n// token on every run (repeat consents otherwise omit it).\nconst buildConsentUrl = (client: OAuth2Client, codeChallenge: string, state: string): string =>\n  client.generateAuthUrl({\n    access_type: \"offline\",\n    prompt: \"consent\",\n    scope: GOOGLE_SCOPES,\n    code_challenge_method: CodeChallengeMethod.S256,\n    code_challenge: codeChallenge,\n    state,\n  });\n\n// PKCE material is generated by an OAuth2Client with no credentials — the\n// helper is pure crypto, and in broker mode there is no client to construct.\nconst generatePkce = async (): Promise<{ codeVerifier: string; codeChallenge: string }> => {\n  const { codeVerifier, codeChallenge } = await new OAuth2Client().generateCodeVerifierAsync();\n  if (!codeChallenge) throw new Error(\"failed to derive a PKCE code challenge\");\n  return { codeVerifier, codeChallenge };\n};\n\nconst authorizeWithLocalClient = async (\n  secret: InstalledClientSecret,\n  server: http.Server,\n  port: number,\n  opts: AuthorizeGoogleOptions,\n): Promise<Credentials> => {\n  const client = createClient(secret, `http://127.0.0.1:${port}${CALLBACK_PATH}`);\n  const { codeVerifier, codeChallenge } = await client.generateCodeVerifierAsync();\n  if (!codeChallenge) throw new Error(\"failed to derive a PKCE code challenge\");\n  const state = randomBytes(STATE_BYTES).toString(\"hex\");\n  opts.onAuthUrl?.(buildConsentUrl(client, codeChallenge, state));\n  const code = await waitForAuthCode(server, state, opts.timeoutMs ?? AUTH_TIMEOUT_MS, opts.signal);\n  const { tokens } = await client.getToken({ code, codeVerifier });\n  if (!tokens.refresh_token) {\n    throw new Error(\"Google returned no refresh token — remove this app under Google Account → Security → Third-party access, then retry\");\n  }\n  return tokens;\n};\n\n// The broker signs `state` with a key it never releases, so it — not this\n// host — builds the authorization URL. `state` then round-trips through the\n// browser and comes back to our loopback, where waitForAuthCode matches it.\nconst authorizeWithBroker = async (server: http.Server, port: number, opts: AuthorizeGoogleOptions): Promise<Credentials> => {\n  const { codeVerifier, codeChallenge } = await generatePkce();\n  const { authUrl, state } = await brokerStart(port, codeChallenge);\n  opts.onAuthUrl?.(authUrl);\n  const code = await waitForAuthCode(server, state, opts.timeoutMs ?? AUTH_TIMEOUT_MS, opts.signal);\n  return await brokerExchange({ code, state, codeVerifier });\n};\n\n// Persists the freshly linked tokens — unless a restart/cancel aborted this run\n// after the code came back. `waitForAuthCode` only guards the wait; the token\n// exchange and this save run past it, so an abandoned flow could otherwise\n// clobber the link the user actually kept. This re-checks the signal right\n// before the one persistent side effect.\nexport const commitLinkedTokens = async (tokens: Credentials, issuedVia: IssuedVia, opts: AuthorizeGoogleOptions): Promise<Credentials> => {\n  if (opts.signal?.aborted) throw new Error(GOOGLE_AUTH_CANCELLED);\n  return await saveGoogleTokens({ ...tokens, issuedVia }, opts.home);\n};\n\nexport async function authorizeGoogle(opts: AuthorizeGoogleOptions = {}): Promise<Credentials> {\n  // A user-supplied client wins: it keeps the whole flow on this machine, and\n  // silently preferring the broker would ignore a deliberate setup. Two of\n  // them is unresolvable rather than a reason to fall back — the user meant to\n  // use one of theirs, and a broker link would quietly not be it.\n  const presence = await clientSecretPresence(opts.home);\n  if (presence === \"ambiguous\") {\n    // Same wording the loader raises, so the CLI and the settings UI (which\n    // disables linking in this state) agree on the fix.\n    await loadClientSecret(opts.home);\n  }\n  const useLocalClient = presence === \"found\";\n  const { server, port } = await startLoopbackServer();\n  try {\n    const issuedVia: IssuedVia = useLocalClient ? \"local\" : \"broker\";\n    const tokens = useLocalClient\n      ? await authorizeWithLocalClient(await loadClientSecret(opts.home), server, port, opts)\n      : await authorizeWithBroker(server, port, opts);\n    return await commitLinkedTokens(tokens, issuedVia, opts);\n  } finally {\n    server.close();\n  }\n}\n","// In-flight manager for the settings-UI OAuth flow. authorizeGoogle() resolves\n// only after the user finishes the browser consent, so the HTTP layer starts it\n// in the background, returns the consent URL immediately, and reports progress\n// via status polling. start() always reflects the latest intent: a new call\n// aborts any pending flow (which closes its loopback listener) before launching\n// a fresh one, so a user who abandoned the browser consent can just link again\n// instead of waiting out the server-side timeout.\nimport { log } from \"./host.js\";\nimport { errorMessage } from \"./util.js\";\nimport { authorizeGoogle } from \"./auth.js\";\n\nexport interface GoogleAuthFlowStatus {\n  pending: boolean;\n  lastError: string | null;\n}\n\nexport interface GoogleAuthFlow {\n  start: () => Promise<{ authUrl: string }>;\n  cancel: () => void;\n  status: () => GoogleAuthFlowStatus;\n}\n\nexport const createGoogleAuthFlow = (authorize: typeof authorizeGoogle): GoogleAuthFlow => {\n  let flowRunning = false;\n  let lastError: string | null = null;\n  let active: AbortController | null = null;\n\n  const launch = (): Promise<{ authUrl: string }> => {\n    const controller = new AbortController();\n    active = controller;\n    flowRunning = true;\n    return new Promise((resolve, reject) => {\n      authorize({ onAuthUrl: (url) => resolve({ authUrl: url }), signal: controller.signal })\n        .then(() => log.info(\"google\", \"authorize flow completed\"))\n        .catch((err: unknown) => {\n          // An aborted flow is a user-initiated restart, not a failure to\n          // surface — only real errors reach lastError.\n          if (!controller.signal.aborted) {\n            lastError = errorMessage(err);\n            log.warn(\"google\", \"authorize flow failed\", { error: lastError });\n          }\n          reject(err instanceof Error ? err : new Error(String(err)));\n        })\n        .finally(() => {\n          // A restart may have already installed a newer controller; only the\n          // current flow clears the shared state so it can't strand the new one.\n          if (active === controller) {\n            flowRunning = false;\n            active = null;\n          }\n        });\n    });\n  };\n\n  const cancel = (): void => {\n    active?.abort();\n  };\n\n  const start = (): Promise<{ authUrl: string }> => {\n    cancel();\n    lastError = null;\n    return launch();\n  };\n\n  const status = (): GoogleAuthFlowStatus => ({ pending: flowRunning, lastError });\n\n  return { start, cancel, status };\n};\n\nexport const googleAuthFlow = createGoogleAuthFlow(authorizeGoogle);\n","// Shared REST plumbing for the Google APIs this engine wraps (Calendar,\n// Tasks, Drive). Plain fetch instead of the `googleapis` SDK — a handful of\n// endpoints don't justify the dependency (see\n// plans/done/feat-google-oauth-calendar.md).\nimport { errorMessage, isRecord, ONE_SECOND_MS, truncate } from \"./util.js\";\nimport { fetchWithTimeout } from \"../utils/fetch.js\";\n\nexport const GOOGLE_API_TIMEOUT_MS = 30 * ONE_SECOND_MS;\nconst ERROR_BODY_MAX_CHARS = 300;\nexport const HTTP_FORBIDDEN = 403;\nexport const DEFAULT_LIST_MAX_RESULTS = 10;\nexport const MAX_LIST_RESULTS = 50;\n\n/** Carries the HTTP status so callers can branch on it (e.g. an expired\n *  calendar syncToken answers 410) without parsing the message. */\nexport class GoogleApiError extends Error {\n  readonly status: number;\n  constructor(status: number, message: string) {\n    super(message);\n    this.name = \"GoogleApiError\";\n    this.status = status;\n  }\n}\n\nexport const isGoogleApiError = (value: unknown): value is GoogleApiError => value instanceof GoogleApiError;\n\n/** 403 usually means the API is not enabled for the user's Cloud project —\n *  name the API so the agent's recovery guidance can be specific. */\nexport const googleApiError = (apiLabel: string, status: number, body: string): GoogleApiError => {\n  const hint = status === HTTP_FORBIDDEN ? ` (is the ${apiLabel} enabled for the Cloud project?)` : \"\";\n  const detail = body ? ` — ${truncate(body, ERROR_BODY_MAX_CHARS)}` : \"\";\n  return new GoogleApiError(status, `${apiLabel}: HTTP ${status}${hint}${detail}`);\n};\n\nexport interface GoogleRequestInit {\n  method?: string;\n  body?: string;\n  /** Overrides the default JSON content type (multipart upload, …). */\n  contentType?: string;\n  /** Response is not JSON (Drive media download) — return the raw text. */\n  expectText?: boolean;\n  /** Extra request headers, merged after the defaults. Used for conditional\n   *  writes (`If-Match: <etag>`), which is how a caller makes a PATCH fail with\n   *  412 instead of silently overwriting a newer version. Cannot replace\n   *  `Authorization`. */\n  extraHeaders?: Record<string, string>;\n}\n\nexport async function googleRequest(apiLabel: string, accessToken: string, url: string, init: GoogleRequestInit = {}): Promise<unknown> {\n  const { contentType = \"application/json\", expectText = false, extraHeaders, ...rest } = init;\n  const response = await fetchWithTimeout(url, {\n    ...rest,\n    timeoutMs: GOOGLE_API_TIMEOUT_MS,\n    headers: { ...extraHeaders, Authorization: `Bearer ${accessToken}`, \"Content-Type\": contentType },\n  });\n  if (!response.ok) {\n    const body = await response.text().catch((err: unknown) => errorMessage(err));\n    throw googleApiError(apiLabel, response.status, body);\n  }\n  if (expectText) return await response.text();\n  // 204 (Drive delete, …) has no body to parse.\n  if (response.status === 204) return {};\n  return await response.json();\n}\n\nexport const stringField = (record: Record<string, unknown>, key: string): string => (typeof record[key] === \"string\" ? record[key] : \"\");\n\nexport const asRecord = (value: unknown): Record<string, unknown> => (isRecord(value) ? value : {});\n\nexport const itemsOf = (value: unknown): unknown[] => {\n  const record = asRecord(value);\n  return Array.isArray(record.items) ? record.items : [];\n};\n","// Google Calendar v3 REST calls. Events read/write against any calendar the\n// user can access (default: their primary); the calendar list and colour\n// palette let callers show non-primary calendars and their colours.\nimport { asRecord, googleApiError, googleRequest, isGoogleApiError, itemsOf, stringField, DEFAULT_LIST_MAX_RESULTS } from \"./apiClient.js\";\nimport { isRecord } from \"./util.js\";\n\nconst CALENDAR_BASE_URL = \"https://www.googleapis.com/calendar/v3\";\nconst CALENDAR_API_LABEL = \"Google Calendar API\";\nconst DEFAULT_CALENDAR_ID = \"primary\";\n// CalendarList.list is paginated; page through nextPageToken so an account\n// with many subscribed calendars isn't silently truncated. The page cap is a\n// runaway guard — 250 * 40 = 10k calendars, far beyond any real account.\nconst CALENDAR_LIST_PAGE_SIZE = 250;\nconst MAX_CALENDAR_LIST_PAGES = 40;\n// Events.list caps maxResults at 2500. The page cap is a runaway guard —\n// 2500 * 200 = 500k events, far beyond any real calendar's history.\nconst EVENT_SYNC_PAGE_SIZE = 2500;\nconst MAX_EVENT_SYNC_PAGES = 200;\n// `getCalendarMeta` reads the list envelope, never the events in it. Google's\n// accepted range for maxResults starts at 1, so this is the smallest page it\n// will serve.\nconst CALENDAR_META_PAGE_SIZE = 1;\n// An expired/invalidated calendar syncToken.\nconst HTTP_GONE = 410;\nconst HTTP_NOT_FOUND = 404;\n// A GET on an event that is not there: 404 when it never existed on this\n// calendar, 410 when it was deleted and the tombstone is no longer served.\nconst EVENT_ABSENT_STATUSES: readonly number[] = [HTTP_NOT_FOUND, HTTP_GONE];\n/** An `If-Match` write whose etag no longer matches — someone else changed the\n *  event since it was read. */\nexport const HTTP_PRECONDITION_FAILED = 412;\n/** An `insert` whose caller-supplied event id is already taken. */\nexport const HTTP_CONFLICT = 409;\n/** Google's status for an event that was deleted; a sync window reports it so\n *  consumers can remove their copy. Single-sourced — the pull and the push both\n *  branch on it. */\nexport const CANCELLED_EVENT_STATUS = \"cancelled\";\n\n/** Resolve a declared calendarId to the one the API and the sync-token store\n *  both address. `||` (not `??`) so an empty string also falls back instead of\n *  building a malformed `/calendars//events` URL. Single-sourced because an\n *  omitted id and an explicit \"primary\" MUST agree everywhere — grouping them\n *  apart while they share a sync token silently loses events (#2184). */\nexport const canonicalCalendarId = (calendarId: string | undefined): string => calendarId || DEFAULT_CALENDAR_ID;\n\nconst eventsUrl = (calendarId: string | undefined): string => `${CALENDAR_BASE_URL}/calendars/${encodeURIComponent(canonicalCalendarId(calendarId))}/events`;\n\n/** One end of an event as the API expresses it: a timed instant, or a whole day.\n *\n *  `timeZone` names the zone a `dateTime` WITHOUT an offset is read in — the\n *  shape a collection push needs, since the stored clock carries no offset\n *  (`pushDateTime.ts`). A `dateTime` that already ends in `Z`/`+09:00` needs no\n *  `timeZone`. `date` is an all-day event, whose `end` Google treats as\n *  EXCLUSIVE. */\nexport type CalendarEventTime = { dateTime: string; timeZone?: string } | { date: string };\n\n/** The two ways to state an event's span. The flat pair is what the `google`\n *  tool passes (RFC3339 with an offset); the structured pair is what a push\n *  needs to express an all-day event or an explicit zone. Expressed as a union\n *  so \"neither given\" cannot typecheck on a create. */\nexport type CalendarEventSpan = { startDateTime: string; endDateTime: string } | { start: CalendarEventTime; end: CalendarEventTime };\n\nexport type CalendarEventInput = {\n  summary: string;\n  description?: string | undefined;\n  location?: string | undefined;\n  /** Calendar to create the event on; defaults to the user's primary. */\n  calendarId?: string | undefined;\n  /** Event colour (Google event palette id \"1\"..\"11\"); omit to inherit the calendar's colour. */\n  colorId?: string | undefined;\n  /** Caller-chosen event id. Google requires base32hex (`0-9a-v`), 5-1024 chars,\n   *  and answers 409 when it is already taken. A collection push sets this so a\n   *  locally-created record keeps its own record id as the event id, instead of\n   *  being re-keyed to Google's after the fact — a re-key that is missed leaves\n   *  a duplicate record on the next pull. */\n  eventId?: string | undefined;\n} & CalendarEventSpan;\n\nexport interface UpdateCalendarEventInput {\n  eventId: string;\n  summary?: string | undefined;\n  startDateTime?: string | undefined;\n  endDateTime?: string | undefined;\n  /** Structured span; wins over the flat pair when both are present. */\n  start?: CalendarEventTime | undefined;\n  end?: CalendarEventTime | undefined;\n  /** `\"\"` clears the description; omit to leave it untouched. */\n  description?: string | undefined;\n  /** `\"\"` clears the location; omit to leave it untouched. */\n  location?: string | undefined;\n  calendarId?: string | undefined;\n  colorId?: string | undefined;\n  /** Etag of the version this edit was computed against. Sent as `If-Match`, so\n   *  Google answers 412 rather than letting the PATCH clobber a change that\n   *  landed after the caller read the event. Omit for an unconditional write. */\n  ifMatch?: string | undefined;\n}\n\nexport interface DeleteCalendarEventInput {\n  eventId: string;\n  calendarId?: string | undefined;\n}\n\nexport interface ListEventsInput {\n  timeMin?: string | undefined;\n  maxResults?: number | undefined;\n  /** Calendar to read; defaults to the user's primary. */\n  calendarId?: string | undefined;\n}\n\nexport interface CalendarEventSummary {\n  id: string;\n  summary: string;\n  start: string;\n  end: string;\n  htmlLink: string;\n  status: string;\n  /** Google event palette id (\"1\"..\"11\"), or \"\" when the event inherits the calendar's colour. */\n  colorId: string;\n  /** The event body, RAW. Google stores limited HTML here, and normalising it on\n   *  the way in would silently strip the user's formatting on the way back out —\n   *  a mirrored calendar has to survive the round trip, so any prettifying\n   *  belongs to whatever displays it. */\n  description: string;\n  location: string;\n}\n\nexport interface CalendarSummary {\n  id: string;\n  summary: string;\n  description: string;\n  /** True only for the user's primary calendar. */\n  primary: boolean;\n  accessRole: string;\n  backgroundColor: string;\n  foregroundColor: string;\n  /** Calendar palette id backing background/foregroundColor. */\n  colorId: string;\n  /** IANA zone (`Asia/Tokyo`). The zone a pushed `dateTime` without an offset is\n   *  read in: the collection stores the clock the user reads off THIS calendar,\n   *  so this is the only correct zone to re-attach. `\"\"` when Google omits it. */\n  timeZone: string;\n}\n\nexport interface CalendarColorEntry {\n  background: string;\n  foreground: string;\n}\n\n/** Palettes from Google's `/colors` endpoint: `event` maps an event's `colorId`\n *  and `calendar` maps a calendar's `colorId` to hex background/foreground. */\nexport interface CalendarColors {\n  event: Record<string, CalendarColorEntry>;\n  calendar: Record<string, CalendarColorEntry>;\n}\n\n// All-day events carry `date`, timed events carry `dateTime`.\nconst eventTime = (value: unknown): string => {\n  if (!isRecord(value)) return \"\";\n  if (typeof value.dateTime === \"string\") return value.dateTime;\n  if (typeof value.date === \"string\") return value.date;\n  return \"\";\n};\n\nexport const toEventSummary = (value: unknown): CalendarEventSummary => {\n  const record = asRecord(value);\n  return {\n    id: stringField(record, \"id\"),\n    summary: stringField(record, \"summary\"),\n    start: eventTime(record.start),\n    end: eventTime(record.end),\n    htmlLink: stringField(record, \"htmlLink\"),\n    status: stringField(record, \"status\"),\n    colorId: stringField(record, \"colorId\"),\n    description: stringField(record, \"description\"),\n    location: stringField(record, \"location\"),\n  };\n};\n\nexport const toCalendarSummary = (value: unknown): CalendarSummary => {\n  const record = asRecord(value);\n  return {\n    id: stringField(record, \"id\"),\n    summary: stringField(record, \"summary\"),\n    description: stringField(record, \"description\"),\n    primary: record.primary === true,\n    accessRole: stringField(record, \"accessRole\"),\n    backgroundColor: stringField(record, \"backgroundColor\"),\n    foregroundColor: stringField(record, \"foregroundColor\"),\n    colorId: stringField(record, \"colorId\"),\n    timeZone: stringField(record, \"timeZone\"),\n  };\n};\n\nconst toColorMap = (value: unknown): Record<string, CalendarColorEntry> => {\n  const entries = Object.entries(asRecord(value)).map(([colorId, entry]): [string, CalendarColorEntry] => {\n    const record = asRecord(entry);\n    return [colorId, { background: stringField(record, \"background\"), foreground: stringField(record, \"foreground\") }];\n  });\n  return Object.fromEntries(entries);\n};\n\n/** Kept as a named export for the existing unit tests / callers; the shared\n *  helper now carries the wording. */\nexport const calendarApiError = (status: number, body: string): Error => googleApiError(CALENDAR_API_LABEL, status, body);\n\n/** Normalise either spelling of a span into the pair the API body carries. */\nexport const resolveEventSpan = (span: CalendarEventSpan): { start: CalendarEventTime; end: CalendarEventTime } =>\n  \"start\" in span ? { start: span.start, end: span.end } : { start: { dateTime: span.startDateTime }, end: { dateTime: span.endDateTime } };\n\nexport async function createCalendarEvent(accessToken: string, input: CalendarEventInput): Promise<CalendarEventSummary> {\n  const body = {\n    summary: input.summary,\n    description: input.description,\n    location: input.location,\n    ...resolveEventSpan(input),\n    ...(input.colorId ? { colorId: input.colorId } : {}),\n    ...(input.eventId ? { id: input.eventId } : {}),\n  };\n  const created = await googleRequest(CALENDAR_API_LABEL, accessToken, eventsUrl(input.calendarId), { method: \"POST\", body: JSON.stringify(body) });\n  return toEventSummary(created);\n}\n\nconst eventUrl = (calendarId: string | undefined, eventId: string): string => `${eventsUrl(calendarId)}/${encodeURIComponent(eventId)}`;\n\n/** PATCH body for an event edit — only the fields the caller actually supplied.\n *\n *  `undefined` means \"leave as is\" and `\"\"` means \"clear it\", so the two cannot\n *  be collapsed: dropping `description: \"\"` would silently ignore a request to\n *  empty the body text. Pure so the distinction is testable without network. */\n/** One end of the patch: the structured value if given, else the flat one\n *  wrapped, else absent (= leave it alone). */\nconst eventEnd = (key: \"start\" | \"end\", structured: CalendarEventTime | undefined, flat: string | undefined): Record<string, CalendarEventTime> => {\n  if (structured !== undefined) return { [key]: structured };\n  return flat !== undefined ? { [key]: { dateTime: flat } } : {};\n};\n\nexport const buildEventPatch = (input: UpdateCalendarEventInput): Record<string, unknown> => ({\n  ...(input.summary !== undefined ? { summary: input.summary } : {}),\n  ...(input.description !== undefined ? { description: input.description } : {}),\n  ...(input.location !== undefined ? { location: input.location } : {}),\n  ...eventEnd(\"start\", input.start, input.startDateTime),\n  ...eventEnd(\"end\", input.end, input.endDateTime),\n  ...(input.colorId ? { colorId: input.colorId } : {}),\n});\n\n/** Edit an existing event in place. PATCH, not PUT — a PUT would need the whole\n *  event and would drop every field the caller never read (attendees,\n *  reminders, recurrence). */\nexport async function updateCalendarEvent(accessToken: string, input: UpdateCalendarEventInput): Promise<CalendarEventSummary> {\n  const url = eventUrl(input.calendarId, input.eventId);\n  const updated = await googleRequest(CALENDAR_API_LABEL, accessToken, url, {\n    method: \"PATCH\",\n    body: JSON.stringify(buildEventPatch(input)),\n    ...(input.ifMatch ? { extraHeaders: { \"If-Match\": input.ifMatch } } : {}),\n  });\n  return toEventSummary(updated);\n}\n\n/** An event plus the version token a conditional write needs. */\nexport interface FetchedCalendarEvent {\n  event: CalendarEventSummary;\n  /** Google's `etag` for this version, `\"\"` when absent. Sent back as\n   *  `If-Match` so a PATCH cannot overwrite a version we never read. */\n  etag: string;\n}\n\n/** Read ONE event, or null when it is gone (404 / 410).\n *\n *  A push needs Google's current value per changed record to tell a local-only\n *  edit from a both-sides conflict. Deliberately not `syncCalendarEvents`: that\n *  consumes the calendar's sync token, and the token's window is served once —\n *  spending it here would make the next pull miss everything in it. */\nexport async function getCalendarEvent(accessToken: string, input: DeleteCalendarEventInput): Promise<FetchedCalendarEvent | null> {\n  try {\n    const fetched = await googleRequest(CALENDAR_API_LABEL, accessToken, eventUrl(input.calendarId, input.eventId));\n    return { event: toEventSummary(fetched), etag: stringField(asRecord(fetched), \"etag\") };\n  } catch (error) {\n    if (isGoogleApiError(error) && EVENT_ABSENT_STATUSES.includes(error.status)) return null;\n    throw error;\n  }\n}\n\n/** Remove an event. Google answers 204 with no body, so there is nothing to\n *  return; a second delete of the same id answers 410, which surfaces as a\n *  GoogleApiError rather than being swallowed. */\nexport async function deleteCalendarEvent(accessToken: string, input: DeleteCalendarEventInput): Promise<void> {\n  await googleRequest(CALENDAR_API_LABEL, accessToken, eventUrl(input.calendarId, input.eventId), { method: \"DELETE\" });\n}\n\nexport async function listCalendarEvents(accessToken: string, input: ListEventsInput = {}): Promise<CalendarEventSummary[]> {\n  const params = new URLSearchParams({\n    timeMin: input.timeMin ?? new Date().toISOString(),\n    maxResults: String(input.maxResults ?? DEFAULT_LIST_MAX_RESULTS),\n    singleEvents: \"true\",\n    orderBy: \"startTime\",\n  });\n  const listed = await googleRequest(CALENDAR_API_LABEL, accessToken, `${eventsUrl(input.calendarId)}?${params.toString()}`);\n  return itemsOf(listed).map(toEventSummary);\n}\n\nexport interface SyncEventsInput {\n  /** Calendar to sync; defaults to the user's primary. */\n  calendarId?: string | undefined;\n  /** Token from the previous sync. Omit for a full sync. */\n  syncToken?: string | undefined;\n  /** Page size for the underlying list calls. */\n  maxResults?: number | undefined;\n}\n\nexport interface CalendarSyncResult {\n  /** Changed events since `syncToken` (all of them on a full sync). Deletions\n   *  arrive here too, as `status: \"cancelled\"`. */\n  events: CalendarEventSummary[];\n  /** Token to pass to the NEXT sync. Absent only if Google omitted it. */\n  nextSyncToken?: string | undefined;\n  /** The stored token had expired (410) — the caller must drop it and re-sync\n   *  from scratch; no events are returned in that case. */\n  fullResyncRequired: boolean;\n  /** The page guard fired with pages still pending, so `events` is a PARTIAL\n   *  window and `nextSyncToken` is absent.\n   *\n   *  Reported rather than left implicit because a truncated walk is otherwise\n   *  byte-identical to a completed one: the caller would apply the partial set,\n   *  report success, and — since only Google's last page carries the token —\n   *  repeat the same truncated walk on every later run, silently (#2850).\n   *  Google states a page \"may be less than this value, or none at all, even if\n   *  there are more events matching the query\", so this is reachable on a real\n   *  calendar, the more so with `singleEvents` expanding unbounded recurrences. */\n  pagesExhausted: boolean;\n}\n\n// Sentinel for \"the syncToken expired\" so the page loop can bail without\n// throwing — 410 is an expected, recoverable state, not a failure.\nconst GONE = Symbol(\"calendar-sync-gone\");\n\nasync function fetchSyncPage(accessToken: string, calendarId: string | undefined, params: URLSearchParams): Promise<unknown> {\n  try {\n    return await googleRequest(CALENDAR_API_LABEL, accessToken, `${eventsUrl(calendarId)}?${params.toString()}`);\n  } catch (err: unknown) {\n    if (isGoogleApiError(err) && err.status === HTTP_GONE) return GONE;\n    throw err;\n  }\n}\n\n/** Incremental sync over the events of one calendar (#2095).\n *\n *  Deliberately separate from `listCalendarEvents`: Google forbids combining\n *  `syncToken` with `timeMin` / `timeMax` / `updatedMin` / `orderBy` / `q`,\n *  and that function always sends `timeMin` + `orderBy`. So this one sends\n *  neither — a sync covers the WHOLE calendar and the caller sorts / windows\n *  client-side. `showDeleted` must stay true or deletions would be invisible.\n *\n *  `nextSyncToken` is only present on the LAST page, so every page must be\n *  walked before the token is worth storing. */\n/** The query for one page of the walk. `showDeleted` must stay true or\n *  deletions would be invisible; `pageToken` is sent ALONGSIDE `syncToken`,\n *  which is what Google requires to page an incremental sync. */\nexport function syncPageParams(input: SyncEventsInput, pageToken: string | undefined): URLSearchParams {\n  const params = new URLSearchParams({\n    singleEvents: \"true\",\n    showDeleted: \"true\",\n    maxResults: String(input.maxResults ?? EVENT_SYNC_PAGE_SIZE),\n  });\n  if (input.syncToken) params.set(\"syncToken\", input.syncToken);\n  if (pageToken) params.set(\"pageToken\", pageToken);\n  return params;\n}\n\nexport async function syncCalendarEvents(accessToken: string, input: SyncEventsInput = {}): Promise<CalendarSyncResult> {\n  const events: CalendarEventSummary[] = [];\n  let pageToken: string | undefined;\n  let nextSyncToken: string | undefined;\n\n  for (let page = 0; page < MAX_EVENT_SYNC_PAGES; page += 1) {\n    const payload = await fetchSyncPage(accessToken, input.calendarId, syncPageParams(input, pageToken));\n    if (payload === GONE) return { events: [], fullResyncRequired: true, pagesExhausted: false };\n\n    const record = asRecord(payload);\n    events.push(...itemsOf(payload).map(toEventSummary));\n    nextSyncToken = stringField(record, \"nextSyncToken\") || undefined;\n    pageToken = stringField(record, \"nextPageToken\") || undefined;\n    if (!pageToken) break;\n    // Google sends the sync token only on the LAST page, so anything read\n    // before one is not a resume point for the walk this call is doing.\n    // Dropped rather than trusted: kept, it would let a caller resume past the\n    // pages this walk never read (Codex review #2853).\n    nextSyncToken = undefined;\n  }\n\n  // A token still in hand means the loop ran out of pages, not out of calendar.\n  return { events, nextSyncToken, fullResyncRequired: false, pagesExhausted: pageToken !== undefined };\n}\n\nexport interface CalendarListPage {\n  items: unknown[];\n  nextPageToken?: string | undefined;\n}\n\n/** Pagination loop for CalendarList.list, extracted so it can be tested without\n *  network. Stops at the last page (no token) or the runaway page cap. */\nexport async function collectCalendarPages(\n  fetchPage: (pageToken?: string) => Promise<CalendarListPage>,\n  maxPages = MAX_CALENDAR_LIST_PAGES,\n): Promise<CalendarSummary[]> {\n  const calendars: CalendarSummary[] = [];\n  let pageToken: string | undefined;\n  for (let page = 0; page < maxPages; page += 1) {\n    const { items, nextPageToken } = await fetchPage(pageToken);\n    calendars.push(...items.map(toCalendarSummary));\n    if (!nextPageToken) break;\n    pageToken = nextPageToken;\n  }\n  return calendars;\n}\n\n/** What a calendar absent from the user's list can still tell about itself. */\nexport interface CalendarMeta {\n  /** IANA zone (`Asia/Tokyo`), `\"\"` when Google omits it. */\n  timeZone: string;\n  /** `owner` / `writer` / `reader` / `freeBusyReader`, `\"\"` when unreported. */\n  accessRole: string;\n}\n\nexport const toCalendarMeta = (value: unknown): CalendarMeta => {\n  const record = asRecord(value);\n  return { timeZone: stringField(record, \"timeZone\"), accessRole: stringField(record, \"accessRole\") };\n};\n\n/** One calendar's zone and the caller's role on it, read by id rather than\n *  looked up in the user's list.\n *\n *  `calendarList` only holds calendars the user has ADDED; a calendar shared\n *  with them can be readable and writable by id without appearing there, and a\n *  push still needs its zone to send an offset-less `dateTime`.\n *\n *  Deliberately `events.list` and NOT `calendars.get`: the latter takes only\n *  full-calendar scopes (`calendar`, `calendar.readonly`, `calendar.calendars*`)\n *  and this app asks for none of them, so it answers 403 for every account it\n *  links (#2735). The events list carries the same `timeZone` — plus the\n *  `accessRole` the calendar resource does not have — under `calendar.events`,\n *  which the push already holds because it writes with it. Only the envelope is\n *  read; the one event asked for is the smallest page Google will serve. */\nexport async function getCalendarMeta(accessToken: string, calendarId: string | undefined): Promise<CalendarMeta> {\n  const params = new URLSearchParams({ maxResults: String(CALENDAR_META_PAGE_SIZE) });\n  return toCalendarMeta(await googleRequest(CALENDAR_API_LABEL, accessToken, `${eventsUrl(calendarId)}?${params.toString()}`));\n}\n\n/** The calendars the user has added/subscribed to (primary + secondary +\n *  shared), each with its id, name and colour, following pagination. Needs the\n *  calendar-list read scope (GOOGLE_CALENDARLIST_SCOPE). */\nexport async function listCalendars(accessToken: string): Promise<CalendarSummary[]> {\n  return collectCalendarPages(async (pageToken) => {\n    const params = new URLSearchParams({ maxResults: String(CALENDAR_LIST_PAGE_SIZE) });\n    if (pageToken) params.set(\"pageToken\", pageToken);\n    const payload = await googleRequest(CALENDAR_API_LABEL, accessToken, `${CALENDAR_BASE_URL}/users/me/calendarList?${params.toString()}`);\n    const record = asRecord(payload);\n    return { items: itemsOf(payload), nextPageToken: typeof record.nextPageToken === \"string\" ? record.nextPageToken : undefined };\n  });\n}\n\n/** Resolve a `colorId` (on an event or calendar) to its hex background/foreground. */\nexport async function getCalendarColors(accessToken: string): Promise<CalendarColors> {\n  const payload = await googleRequest(CALENDAR_API_LABEL, accessToken, `${CALENDAR_BASE_URL}/colors`);\n  const record = asRecord(payload);\n  return { event: toColorMap(record.event), calendar: toColorMap(record.calendar) };\n}\n","// Cross-process exclusion for the calendar state files (#2679).\n//\n// `.sync-state.json` and `.push-state.json` are both read-modify-write over a\n// whole file. Inside one process a write queue orders that; between processes\n// nothing did, so two hosts sharing a workspace could each read the same\n// snapshot and the later write would drop the earlier one's entry.\n//\n// Scoped to the file mutation, NOT to the sync run. The mutation is held for\n// milliseconds, which is what makes this cheap: no \"another host is busy\"\n// result to thread through the routes, and a stale lock can be reclaimed on a\n// short timer instead of needing a heartbeat sized against a multi-minute full\n// walk.\n//\n// What it does NOT do: stop two hosts walking the same calendar at once. That\n// wastes API calls, but the records are upserts and the baseline is now safe,\n// so nothing is lost by it.\nimport { hasStringProp } from \"@mulmoclaude/common\";\nimport { open, readFile, stat, unlink } from \"node:fs/promises\";\nimport { randomUUID } from \"node:crypto\";\nimport { log } from \"./host.js\";\n\nconst LOCK_MODE = 0o600;\n/** How long a lock may sit untouched before another process may reclaim it.\n *\n *  Sized against the HOLD time, which is one read plus one atomic write — a few\n *  milliseconds. Generous by three orders of magnitude so a slow disk never\n *  looks like a crash, while a host that died mid-write blocks the others for\n *  at most this long. */\nconst LOCK_STALE_MS = 10_000;\n/** How long a caller waits before giving up and proceeding unlocked. */\nconst LOCK_WAIT_MS = 5_000;\nconst LOCK_RETRY_MS = 20;\n\n/** Deliberately just the holder. There is no timestamp in here: an age written\n *  by one host and read by another is two different wall clocks compared as if\n *  they were one, which breaks staleness in BOTH directions — a live lock\n *  reclaimed early, or a dead one that never expires (Codex review #2690). The\n *  filesystem's own mtime is the single authority instead. */\ninterface LockFile {\n  /** Not a secret — it only has to be unique per acquisition, so that a release\n   *  removes its OWN lock and never the one that replaced it. */\n  holder: string;\n}\n\n/** The clock and the sleep, injected so the timing rules can be exercised\n *  without real waits. */\nexport interface LockClock {\n  now: () => number;\n  sleep: (delay_ms: number) => Promise<void>;\n}\n\nexport const liveLockClock: LockClock = {\n  now: () => Date.now(),\n  sleep: (delay_ms) => new Promise((resolve) => setTimeout(resolve, delay_ms)),\n};\n\nconst isEexist = (error: unknown): boolean => typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"EEXIST\";\n\n/** The file's bytes, or null when it is not there. */\nasync function readLockRaw(lockPath: string): Promise<string | null> {\n  try {\n    return await readFile(lockPath, \"utf-8\");\n  } catch {\n    return null;\n  }\n}\n\n/** Absent means unheld; UNREADABLE does not — see `stealIfStale`. */\nfunction parseLock(raw: string): LockFile | null {\n  try {\n    const parsed: unknown = JSON.parse(raw);\n    return hasStringProp(parsed, \"holder\") ? { holder: parsed.holder } : null;\n  } catch {\n    return null;\n  }\n}\n\nasync function readLock(lockPath: string): Promise<LockFile | null> {\n  const raw = await readLockRaw(lockPath);\n  return raw === null ? null : parseLock(raw);\n}\n\n/** Take the lock, or answer false because someone else holds a live one. */\nasync function tryAcquire(lockPath: string, holder: string): Promise<boolean> {\n  try {\n    const handle = await open(lockPath, \"wx\", LOCK_MODE);\n    try {\n      await handle.writeFile(JSON.stringify({ holder }));\n    } finally {\n      await handle.close();\n    }\n    return true;\n  } catch (error) {\n    if (!isEexist(error)) throw error;\n    return false;\n  }\n}\n\n/** How long this lock has existed, per the filesystem that holds it.\n *\n *  From mtime rather than anything the holder wrote down, and for every lock\n *  rather than only the unreadable ones. A timestamp written by one host and\n *  compared against another host's clock is two wall clocks pretending to be\n *  one: skew either way reclaims a live lock or immortalises a dead one (Codex\n *  review #2690). Every host reading this workspace sees the SAME mtime.\n *\n *  This still assumes the reader's clock and the filesystem's are within a TTL\n *  of each other — true by construction on a local disk, and a sub-second NTP\n *  question on a network mount, against a window of ten seconds.\n *\n *  Reading mtime also covers the moment `open(\"wx\")` has created the file but\n *  its payload has not landed yet: \"unreadable\" is a state every healthy lock\n *  passes through, and one being born reads as newborn rather than dead.\n *\n *  Null when the file is gone, which is not an age but an absence. */\nasync function lockAgeMs(lockPath: string, clock: LockClock): Promise<number | null> {\n  const stamped = await stat(lockPath).catch(() => null);\n  return stamped === null ? null : clock.now() - stamped.mtimeMs;\n}\n\n/** Drop a lock that has sat untouched past the TTL, whatever it says.\n *\n *  Readable or not, nothing is reclaimed until it is demonstrably stale. For a\n *  readable one the holder is re-read and compared before the unlink, so a\n *  holder that renewed in between is not evicted. That narrows the window\n *  rather than closing it — two processes can still both decide to clear the\n *  same corpse. The cost of losing that race is one lost update, which is what\n *  this file reduces rather than a new failure, and reaching it takes a host\n *  dying mid-write plus a full TTL of waiting. */\nasync function stealIfStale(lockPath: string, clock: LockClock): Promise<void> {\n  const raw = await readLockRaw(lockPath);\n  if (raw === null) return;\n  const held = parseLock(raw);\n  const age_ms = await lockAgeMs(lockPath, clock);\n  if (age_ms === null || age_ms < LOCK_STALE_MS) return;\n  // A lock nobody can read is a lock nobody can release — `release` only removes\n  // a file whose holder matches — so once it IS stale it has to be cleared here\n  // or it would cost every later mutation the full wait before giving up.\n  if (held === null) {\n    log.warn(\"google\", \"reclaiming an unreadable calendar state lock\", { lockPath, age_ms });\n    await unlink(lockPath).catch(() => undefined);\n    return;\n  }\n  const stillHeld = await readLock(lockPath);\n  if (stillHeld?.holder !== held.holder) return;\n  log.warn(\"google\", \"reclaiming a calendar state lock whose holder went quiet\", { lockPath, age_ms });\n  await unlink(lockPath).catch(() => undefined);\n}\n\nasync function release(lockPath: string, holder: string): Promise<void> {\n  const held = await readLock(lockPath);\n  // Only ever remove OUR lock: after a steal the file may belong to whoever\n  // took it next, and unlinking that would hand the same lock to two holders.\n  if (held?.holder !== holder) return;\n  await unlink(lockPath).catch(() => undefined);\n}\n\n/** Run `mutate` with no other process mutating the same file.\n *\n *  Fails OPEN. A workspace that cannot create the lock file — a read-only\n *  mount, a missing directory, an exhausted disk — still syncs, the way it did\n *  before #2679; the lock removes a race, it is not a precondition for\n *  correctness. The same applies to waiting the timeout out: proceeding\n *  unlocked risks the lost update this exists to prevent, whereas refusing\n *  would stop the sync outright.\n *\n *  Note what this cannot promise. `O_EXCL` is atomic on a local filesystem and\n *  on NFSv3+, but a workspace living in a consumer sync folder (Dropbox,\n *  iCloud, Drive) has no such guarantee — there the file is replicated after\n *  the fact and both hosts believe they hold it. */\nexport async function withCalendarStateLock<T>(lockPath: string, mutate: () => Promise<T>, clock: LockClock = liveLockClock): Promise<T> {\n  const holder = randomUUID();\n  const deadline = clock.now() + LOCK_WAIT_MS;\n  let holding = false;\n  try {\n    holding = await acquireBefore(lockPath, holder, deadline, clock);\n    if (!holding) log.warn(\"google\", \"proceeding without the calendar state lock — another host is holding it\", { lockPath });\n    return await mutate();\n  } finally {\n    if (holding) await release(lockPath, holder);\n  }\n}\n\n/** Poll until the lock is ours or the deadline passes. */\nasync function acquireBefore(lockPath: string, holder: string, deadline: number, clock: LockClock): Promise<boolean> {\n  for (;;) {\n    try {\n      if (await tryAcquire(lockPath, holder)) return true;\n      await stealIfStale(lockPath, clock);\n    } catch (error) {\n      log.warn(\"google\", \"could not take the calendar state lock — mutating without it\", { lockPath, error: String(error) });\n      return false;\n    }\n    if (clock.now() >= deadline) return false;\n    await clock.sleep(LOCK_RETRY_MS);\n  }\n}\n\n/** `<state file>.lock`, so each state file is serialised on its own. */\nexport const stateLockPath = (statePath: string): string => `${statePath}.lock`;\n","// Persistence for the Google Calendar incremental-sync tokens (#2095) and the\n// cross-host sync markers (#2678).\n//\n// Unlike the OAuth material in `paths.ts`, this state lives INSIDE the\n// workspace. A syncToken is a claim about which records the workspace already\n// holds: if it survived a workspace reset, the next incremental sync would\n// report \"nothing changed\" against an empty calendar and stay silently empty.\n// Keeping it next to the data it describes makes the two reset together.\nimport path from \"node:path\";\nimport { getWorkspaceRoot } from \"../collection/server/host.js\";\nimport { readJsonOrNull, writeJsonAtomicWithMode } from \"./fsJson.js\";\nimport { canonicalCalendarId } from \"./calendar.js\";\nimport { stateLockPath, withCalendarStateLock } from \"./calendarStateLock.js\";\n\nconst SYNC_STATE_MODE = 0o600;\n\n/** `<workspace>/data/calendar/.sync-state.json` */\nexport function calendarSyncStatePath(workspaceRoot?: string): string {\n  return path.join(workspaceRoot ?? getWorkspaceRoot(), \"data\", \"calendar\", \".sync-state.json\");\n}\n\ninterface CalendarSyncState {\n  /** calendarId → the `nextSyncToken` returned by that calendar's last sync. */\n  tokens: Record<string, string>;\n  /** calendarId → when a sync of it last STARTED (ISO).\n   *\n   *  A separate concern from the token, deliberately: the token says how far\n   *  the workspace has read, never when anyone last ran. Workspace state rather\n   *  than host state, because that is exactly what lets a second host see that\n   *  this calendar is already being synced (#2678, `calendarSyncDue.ts`). */\n  lastSyncedAt: Record<string, string>;\n}\n\n// Shared with the REST layer so a stored token is keyed by exactly the\n// calendar the request addressed — an omitted id and an explicit \"primary\"\n// must never end up as two different keys.\nconst calendarKey = canonicalCalendarId;\n\n/** The key for a consumer that reads a calendar without KEEPING what it reads.\n *\n *  A sync token is a claim about which events the consumer already holds, but\n *  the key above names only the calendar — so every consumer of one calendar\n *  shares one cursor. That is right for the collections bound to it (one fetch\n *  fans out to all of them in the same pass) and wrong for the standalone\n *  `google` tool's `calendarSync`, which walks the calendar, reports a summary\n *  to the LLM and discards the events.\n *\n *  Sharing it lost data both ways (#2850): the tool consumed windows the\n *  collections then never received, and a collection created afterwards resumed\n *  from the tool's cursor — so its \"first sync\" was a delta of a window it had\n *  never held, and the calendar's history never arrived. Same file, separate\n *  keyspace. Idempotent through `calendarKey`, since the result is non-empty. */\nexport const toolCalendarSyncKey = (calendarId: string | undefined): string => `tool:${canonicalCalendarId(calendarId)}`;\n\n/** Read one map out of whatever is on disk. Tolerant on purpose: a file written\n *  before `lastSyncedAt` existed simply lacks the key, and a hand-edited one may\n *  hold anything. A dropped entry costs a full re-walk or a duplicate run, never\n *  data — throwing here would stop the sync entirely. */\nfunction stringRecord(value: unknown): Record<string, string> {\n  if (typeof value !== \"object\" || value === null) return {};\n  return Object.fromEntries(Object.entries(value).filter((entry): entry is [string, string] => typeof entry[1] === \"string\"));\n}\n\nasync function readState(workspaceRoot?: string): Promise<CalendarSyncState> {\n  const stored = await readJsonOrNull<Partial<CalendarSyncState>>(calendarSyncStatePath(workspaceRoot));\n  return { tokens: stringRecord(stored?.tokens), lastSyncedAt: stringRecord(stored?.lastSyncedAt) };\n}\n\n// One file holds every calendar's token, so an unguarded read-modify-write\n// loses updates: two calendars syncing at once both read the same snapshot and\n// the later write drops the earlier one's token — silently forcing that\n// calendar into a full re-walk next run. Serialising the whole cycle keeps\n// them ordered. Scope is this process, which is where the concurrency comes\n// from (parallel tool calls today, the scheduler fanning out over calendars\n// next). Held in a const wrapper so the tail can advance without a `let`.\nconst writeQueue: { tail: Promise<unknown> } = { tail: Promise.resolve() };\n\n/** Decide from the stored state and write the result, both inside the queue, so\n *  nothing in this process can read between the two. A `null` state skips the\n *  write; `result` is what the caller learns about the decision. */\nasync function updateStateWith<T>(decide: (state: CalendarSyncState) => { state: CalendarSyncState | null; result: T }, workspaceRoot?: string): Promise<T> {\n  const statePath = calendarSyncStatePath(workspaceRoot);\n  // The queue orders this process; the file lock orders the others. Both are\n  // needed — the lock is per read-modify-write, so two of THIS process's own\n  // mutations would still interleave without the queue (#2679).\n  const run = writeQueue.tail.then(\n    async () =>\n      await withCalendarStateLock(stateLockPath(statePath), async () => {\n        const { state, result } = decide(await readState(workspaceRoot));\n        if (state) await writeJsonAtomicWithMode(statePath, state, SYNC_STATE_MODE);\n        return result;\n      }),\n  );\n  // Swallow on the queue only — the caller still sees the original rejection.\n  writeQueue.tail = run.catch(() => undefined);\n  return await run;\n}\n\nasync function updateState(mutate: (state: CalendarSyncState) => CalendarSyncState, workspaceRoot?: string): Promise<void> {\n  await updateStateWith((state) => ({ state: mutate(state), result: undefined }), workspaceRoot);\n}\n\nconst withoutKey = (record: Record<string, string>, dropped: string): Record<string, string> =>\n  Object.fromEntries(Object.entries(record).filter(([key]) => key !== dropped));\n\nexport async function loadCalendarSyncToken(calendarId?: string, workspaceRoot?: string): Promise<string | null> {\n  const state = await readState(workspaceRoot);\n  return state.tokens[calendarKey(calendarId)] ?? null;\n}\n\nexport async function saveCalendarSyncToken(calendarId: string | undefined, syncToken: string, workspaceRoot?: string): Promise<void> {\n  const stored = calendarKey(calendarId);\n  await updateState((state) => ({ ...state, tokens: { ...state.tokens, [stored]: syncToken } }), workspaceRoot);\n}\n\n/** Drop one calendar's token — used when Google answers 410, so the next run\n *  starts a clean full sync. Leaves the sync marker alone: \"restart the walk\"\n *  says nothing about \"nobody is running\". */\nexport async function clearCalendarSyncToken(calendarId?: string, workspaceRoot?: string): Promise<void> {\n  const dropped = calendarKey(calendarId);\n  await updateState((state) => ({ ...state, tokens: withoutKey(state.tokens, dropped) }), workspaceRoot);\n}\n\nexport async function loadCalendarLastSyncedAt(calendarId?: string, workspaceRoot?: string): Promise<string | null> {\n  const state = await readState(workspaceRoot);\n  return state.lastSyncedAt[calendarKey(calendarId)] ?? null;\n}\n\n/** Stamp when a sync of this calendar started, but only if `mayClaim` accepts\n *  what the marker currently says. Answers whether THIS call is the one that\n *  claimed it.\n *\n *  The decision and the stamp share one pass through the queue on purpose.\n *  Deciding from a snapshot read earlier — as a caller filtering a whole map of\n *  calendars up front must — leaves both hosts believing the calendar is free,\n *  which is the race this exists to close (Codex review #2680). What remains is\n *  the read→write gap BETWEEN processes; closing that needs a real file lock\n *  (#2679), and this stays a soft dedup until then.\n *\n *  The time is passed in rather than read here so the rule stays testable\n *  without freezing the clock. */\nexport async function claimCalendarSyncIfDue(\n  calendarId: string | undefined,\n  startedAt: string,\n  mayClaim: (lastSyncedAt: string | null) => boolean,\n  workspaceRoot?: string,\n): Promise<boolean> {\n  const stored = calendarKey(calendarId);\n  return await updateStateWith((state) => {\n    const claimed = mayClaim(state.lastSyncedAt[stored] ?? null);\n    return { state: claimed ? { ...state, lastSyncedAt: { ...state.lastSyncedAt, [stored]: startedAt } } : null, result: claimed };\n  }, workspaceRoot);\n}\n\n/** Drop one calendar's marker, so the next tick — on either host — may sync it\n *  immediately instead of waiting the window out. */\nexport async function clearCalendarLastSyncedAt(calendarId?: string, workspaceRoot?: string): Promise<void> {\n  const dropped = calendarKey(calendarId);\n  await updateState((state) => ({ ...state, lastSyncedAt: withoutKey(state.lastSyncedAt, dropped) }), workspaceRoot);\n}\n","// Whether a calendar may be synced now, given when this workspace last STARTED\n// a sync of it (#2678).\n//\n// `googleCalendarSyncTaskDef` is a factory several hosts register — MulmoClaude\n// and a standalone MulmoTerminal/MulmoBooks — and nothing kept them from syncing\n// one calendar at once: the Google link is machine-wide (`~/.config/mulmo`, no\n// app name), the calendar lock is module state, and interval schedules fire on\n// wall-clock boundaries, so every host ticks in the SAME minute. Since `autoPush`\n// (#2620) such a run also writes to Google and rewrites the push baseline.\n//\n// Same soft-dedup feeds already run on `lastFetchedAt`, and with the same limit:\n// the marker is workspace state, so it only speaks for hosts sharing a workspace.\n\n/** Slack between a tick and the marker it writes. Without it a lone host reads\n *  its OWN marker as \"not due yet\" — elapsed lands a hair under the interval\n *  every time — and syncs every other hour. */\nconst TICK_JITTER_TOLERANCE_MS = 5 * 60 * 1000;\n\n/** How long a fresh marker suppresses a sync: one interval, less the slack.\n *  The slack is halved rather than subtracted flat for a very short configured\n *  interval, where a fixed five minutes would swallow the window whole. */\nexport function calendarSyncDueWindowMs(intervalMs: number): number {\n  return intervalMs - Math.min(TICK_JITTER_TOLERANCE_MS, intervalMs / 2);\n}\n\n/** True iff no recent enough run stands in for this one.\n *\n *  A marker that cannot be read as a time is treated as due: a stuck calendar is\n *  worse than a duplicate run, which the sync is built to tolerate anyway\n *  (writes are upserts by event id). */\nexport function isCalendarSyncDue(lastSyncedAt: string | null, windowMs: number, now: number = Date.now()): boolean {\n  if (!lastSyncedAt) return true;\n  const elapsed = now - Date.parse(lastSyncedAt);\n  if (!Number.isFinite(elapsed)) return true;\n  // A marker from a host whose clock runs ahead would otherwise hold this\n  // calendar back for as long as the skew lasts. Beyond a whole window it\n  // cannot be a run worth waiting for.\n  if (elapsed < -windowMs) return true;\n  return elapsed >= windowMs;\n}\n","// Whether a collection has ever received the whole calendar (#2850).\n//\n// The sync token cannot answer this. It is keyed by `calendarId` and nothing\n// else, so it is a claim about the CALENDAR, shared by every consumer of it —\n// the standalone `google` tool's `calendarSync`, and every collection bound to\n// that calendar. Whoever walks first stores a cursor, and the next collection\n// resumes from a window it never received: it gets the delta, reports success\n// with no error, and never holds the history at all.\n//\n// So the claim this file stores is the per-collection one the token is not:\n// \"these records already cover the whole of calendar X\".\n//\n// Kept in the collection's OWN data dir rather than in the shared calendar\n// state, and that placement is the design. The marker describes the records, so\n// it must reset exactly when they do — deleting a collection's data (through\n// `deleteCollection` or by hand, which is what the #2850 reporter did six\n// times) leaves no marker behind claiming a backfill that is gone. The\n// alternative, a slug-keyed entry in `.sync-state.json`, needs a cleanup that\n// stays in step with every delete path — the shape #2428 already was.\n//\n// Dot-prefixed so `listItems` skips it: record files are `<id>.json`, and the\n// reader already ignores names starting with `.`.\nimport path from \"node:path\";\nimport { readJsonOrNull, writeJsonAtomicWithMode } from \"./fsJson.js\";\nimport { canonicalCalendarId } from \"./calendar.js\";\nimport { log } from \"./host.js\";\n\nconst BACKFILL_STATE_MODE = 0o600;\nconst BACKFILL_FILE = \".calendar-sync.json\";\n\ninterface BackfillState {\n  /** The canonical calendar these records were walked in full against. */\n  calendarId: string;\n  /** When that walk landed (ISO). Diagnostics — but its PRESENCE is checked,\n   *  see `needsCalendarBackfill`. */\n  walkedAt: string;\n}\n\n/** `<dataDir>/.calendar-sync.json` */\nexport const calendarBackfillPath = (dataDir: string): string => path.join(dataDir, BACKFILL_FILE);\n\n/** Whether these records still need the whole calendar walked into them.\n *\n *  Compared against the calendar the marker NAMES, not merely its presence: a\n *  schema repointed at another calendar holds a backfill of the wrong one, and\n *  resuming that calendar's cursor would leave the same silent gap this exists\n *  to close.\n *\n *  Unreadable, absent or INCOMPLETE all answer \"yes\" — every field must be\n *  there, not just the one being compared. A redundant full walk costs API\n *  calls and rewrites records that already match; skipping a needed one loses\n *  the history silently, which is the failure being fixed, so the bias only\n *  ever goes one way (CodeRabbit review #2853). */\nexport async function needsCalendarBackfill(dataDir: string, calendarId: string | undefined): Promise<boolean> {\n  const stored = await readJsonOrNull<Partial<BackfillState>>(calendarBackfillPath(dataDir));\n  if (typeof stored?.walkedAt !== \"string\" || stored.walkedAt === \"\") return true;\n  return stored.calendarId !== canonicalCalendarId(calendarId);\n}\n\n/** Record that these records now cover the whole calendar. Never throws: the\n *  marker is an optimisation over re-walking, so a workspace that cannot write\n *  it syncs correctly and simply walks in full again next time. */\nexport async function markCalendarBackfilled(dataDir: string, calendarId: string | undefined, walkedAt: string): Promise<void> {\n  const state: BackfillState = { calendarId: canonicalCalendarId(calendarId), walkedAt };\n  try {\n    await writeJsonAtomicWithMode(calendarBackfillPath(dataDir), state, BACKFILL_STATE_MODE);\n  } catch (error) {\n    log.warn(\"google\", \"could not record the calendar backfill marker — the next sync will walk the calendar again\", { dataDir, error: String(error) });\n  }\n}\n","// What Google last reported for each synced event — the baseline a push diffs\n// against (#2598).\n//\n// Without it, a local edit and an untouched record are indistinguishable: the\n// pull writes Google's value into the record and keeps no copy, so \"the record\n// says 09:30\" answers nothing about who put it there.\n//\n// It holds the RAW Google values, not a hash of them, because the push has to\n// REBUILD a Google time from a stored one and `toCollectionDateTime` is lossy —\n// it drops the zone offset and flattens all-day into `…T00:00`. The baseline is\n// where both come back from (see `pushDateTime.ts`).\n//\n// Lives inside the workspace next to the sync token, and for the same reason\n// (`calendarSyncStore.ts`): it is a claim about which records the workspace\n// holds, so it must reset when the workspace does.\nimport path from \"node:path\";\nimport { getWorkspaceRoot } from \"../collection/server/host.js\";\nimport { readJsonOrNull, writeJsonAtomicWithMode } from \"./fsJson.js\";\nimport { canonicalCalendarId, type CalendarEventSummary } from \"./calendar.js\";\nimport { stateLockPath, withCalendarStateLock } from \"./calendarStateLock.js\";\n\nconst PUSH_STATE_MODE = 0o600;\n\n/** The pushable subset of an event. `htmlLink` and `status` are read-only on\n *  Google's side, so a baseline for them could never be acted on. */\nexport type ShadowEvent = Pick<CalendarEventSummary, \"summary\" | \"start\" | \"end\" | \"colorId\" | \"description\" | \"location\">;\n\n/** calendarId → eventId → what Google last said. */\ninterface PushState {\n  events: Record<string, Record<string, ShadowEvent>>;\n}\n\n/** `<workspace>/data/calendar/.push-state.json` */\nexport function calendarPushStatePath(workspaceRoot?: string): string {\n  return path.join(workspaceRoot ?? getWorkspaceRoot(), \"data\", \"calendar\", \".push-state.json\");\n}\n\n// A state file written before `description` / `location` joined the pushable set\n// simply lacks those keys. That needs no migration: `fieldText(undefined)` and\n// the `\"\"` Google reports for an unset field both compare as empty, so an\n// upgraded host does not read every record as locally edited.\nexport const toShadowEvent = (event: CalendarEventSummary): ShadowEvent => ({\n  summary: event.summary,\n  start: event.start,\n  end: event.end,\n  colorId: event.colorId,\n  description: event.description,\n  location: event.location,\n});\n\nasync function readState(workspaceRoot?: string): Promise<PushState> {\n  const stored = await readJsonOrNull<PushState>(calendarPushStatePath(workspaceRoot));\n  return stored?.events ? stored : { events: {} };\n}\n\n// One file holds every calendar's baseline, so an unguarded read-modify-write\n// loses updates — the same trap `calendarSyncStore.ts` documents. Serialising\n// the whole cycle keeps concurrent calendars ordered.\nconst writeQueue: { tail: Promise<unknown> } = { tail: Promise.resolve() };\n\nasync function updateState(mutate: (events: PushState[\"events\"]) => PushState[\"events\"], workspaceRoot?: string): Promise<void> {\n  const statePath = calendarPushStatePath(workspaceRoot);\n  // Same pairing as `calendarSyncStore.ts`: the queue orders this process, the\n  // file lock orders the others (#2679).\n  const run = writeQueue.tail.then(\n    async () =>\n      await withCalendarStateLock(stateLockPath(statePath), async () => {\n        const state = await readState(workspaceRoot);\n        await writeJsonAtomicWithMode(statePath, { events: mutate(state.events) }, PUSH_STATE_MODE);\n      }),\n  );\n  // Swallow on the queue only — the caller still sees the original rejection.\n  writeQueue.tail = run.catch(() => undefined);\n  return await run;\n}\n\nexport async function loadCalendarShadow(calendarId?: string, workspaceRoot?: string): Promise<Record<string, ShadowEvent>> {\n  const state = await readState(workspaceRoot);\n  return state.events[canonicalCalendarId(calendarId)] ?? {};\n}\n\n/** Apply a batch to one calendar's baseline. `null` removes an entry (the event\n *  was cancelled), so a cancel-then-recreate cannot resume from a stale one.\n *  Merged rather than replaced: a sync window describes only what changed. */\nexport function mergeShadow(current: Record<string, ShadowEvent>, updates: Record<string, ShadowEvent | null>): Record<string, ShadowEvent> {\n  const removed = new Set(\n    Object.entries(updates)\n      .filter(([, entry]) => entry === null)\n      .map(([eventId]) => eventId),\n  );\n  const kept = Object.entries(current).filter(([eventId]) => !removed.has(eventId));\n  const written = Object.entries(updates).filter((entry): entry is [string, ShadowEvent] => entry[1] !== null);\n  return Object.fromEntries([...kept, ...written]);\n}\n\nexport async function saveCalendarShadow(calendarId: string | undefined, updates: Record<string, ShadowEvent | null>, workspaceRoot?: string): Promise<void> {\n  if (Object.keys(updates).length === 0) return;\n  const key = canonicalCalendarId(calendarId);\n  await updateState((events) => ({ ...events, [key]: mergeShadow(events[key] ?? {}, updates) }), workspaceRoot);\n}\n\n/** Drop one calendar's whole baseline. Called wherever the sync token is\n *  cleared: a baseline outliving its token would let a push diff against events\n *  the workspace no longer holds. */\nexport async function clearCalendarShadow(calendarId?: string, workspaceRoot?: string): Promise<void> {\n  const dropped = canonicalCalendarId(calendarId);\n  await updateState((events) => Object.fromEntries(Object.entries(events).filter(([key]) => key !== dropped)), workspaceRoot);\n}\n","// Serialising everything that touches ONE calendar's sync state.\n//\n// A leaf on purpose: both directions need it (the pull in `collectionSync.ts`,\n// the push in `collectionPush.ts`), and the pull now drives the push, so leaving\n// the lock inside either one would make the two import each other.\nimport { canonicalCalendarId } from \"./calendar.js\";\n\n/** Serialise `run` against whatever is already running for `key`.\n *\n *  `locks` is passed in so the queuing rule is testable without module state;\n *  the key is dropped once nothing is queued behind it, so the map cannot grow\n *  an entry per calendar forever. A failed predecessor still releases the\n *  queue — `then(run, run)`. */\nexport async function withKeyedLock<T>(locks: Map<string, Promise<unknown>>, key: string, run: () => Promise<T>): Promise<T> {\n  const previous = locks.get(key) ?? Promise.resolve();\n  const result = previous.then(run, run);\n  const tail = result.then(\n    () => undefined,\n    () => undefined,\n  );\n  locks.set(key, tail);\n  try {\n    return await result;\n  } finally {\n    if (locks.get(key) === tail) locks.delete(key);\n  }\n}\n\n/** In-flight work per canonical calendar id. Module state on purpose: the\n *  scheduler, the create trigger and the Refresh button are three doors into\n *  the same calendar (CodeRabbit review #2566). */\nconst calendarLocks = new Map<string, Promise<unknown>>();\n\n/** Serialise anything that touches ONE calendar's sync state.\n *\n *  Shared with the push path (#2598), not only the pull doors: a push that\n *  overtakes an in-flight pull would record a baseline for events the pull is\n *  still writing, so the next push would diff against a future it never saw.\n *\n *  NOT reentrant — a holder must call the lock-free form of whatever it drives,\n *  or it waits on itself forever. */\nexport async function withCalendarLock<T>(calendarId: string | undefined, run: () => Promise<T>): Promise<T> {\n  return await withKeyedLock(calendarLocks, canonicalCalendarId(calendarId), run);\n}\n","// Collection `datetime` value → the Google Calendar event time to push (#2598).\n//\n// The inverse of `toCollectionDateTime`, which is deliberately LOSSY: it drops\n// the RFC3339 zone designator (so the stored clock stays the one the user reads\n// off Google, independent of the machine that synced) and flattens an all-day\n// `YYYY-MM-DD` into `…T00:00`. Neither the zone nor the all-day-ness survives in\n// the stored value, so they cannot be derived from it.\n//\n// What Google last reported for the event carries both, so a push that has that\n// value reuses it. Only a locally-created record has no such history, and it\n// falls back to the calendar's own timezone — never the host's, which is the\n// machine-dependence `collectionDateTime.ts` exists to avoid.\n//\n// Pure: no I/O, no clock, no locale.\nimport { parseIsoDate, parseIsoDateTime } from \"../collection/core/calendarGrid.js\";\nimport type { CalendarEventTime } from \"./calendar.js\";\n\n/** RFC3339 zone designator: `Z`, `+09:00`, `-0500`. */\nconst ZONE_SUFFIX_RE = /(?:Z|[+-]\\d{2}:?\\d{2})$/;\nconst FRACTIONAL_SECONDS_RE = /\\.\\d+$/;\nconst TWO_DIGITS_RE = /^\\d{2}$/;\nconst CLOCK_SEPARATOR = \":\";\nconst DATE_TIME_SEPARATOR = \"T\";\nconst MIN_CLOCK_SEGMENTS = 2;\nconst MAX_CLOCK_SEGMENTS = 3;\nconst WHOLE_MINUTE_SECONDS = \"00\";\nconst MAX_SECONDS = 59;\n\n/** Whether `value` is a real calendar day (`2026-02-30` is not).\n *\n *  Delegated to the parser the record lint, the calendar grid and the day view\n *  already share, so the push agrees with the rest of the app on what a valid\n *  stored date IS — and rejects an impossible one here instead of turning it\n *  into an opaque Google 400. */\nconst isCalendarDate = (value: string): boolean => parseIsoDate(value) !== null;\n\n/** A value Google already accepts verbatim, as the event time to send, or null.\n *\n *  Reached when the schema maps `start` onto a `string` field: `projectValue`\n *  normalises only `datetime` fields, so that column holds Google's own text,\n *  zone and all. The civil part is still validated — matching a trailing `Z` is\n *  not evidence that the rest is a date-time. */\nfunction rawGoogleTime(value: string): CalendarEventTime | null {\n  if (isCalendarDate(value)) return { date: value };\n  const zone = value.match(ZONE_SUFFIX_RE)?.[0];\n  if (zone === undefined) return null;\n  // Google may answer fractional seconds, which the shared parser rejects.\n  // Validate without them, then send the original — Calendar accepts them.\n  const civil = value.slice(0, value.length - zone.length).replace(FRACTIONAL_SECONDS_RE, \"\");\n  return parseIsoDateTime(civil) === null ? null : { dateTime: value };\n}\n\n/** Google's own all-day `end` is EXCLUSIVE — a single day on the 12th ends on\n *  the 13th. The pull stores that as `2026-05-13T00:00` and this takes the date\n *  part straight back, so the exclusive end round-trips untouched. Looks\n *  off-by-one; adjusting it would shorten every all-day event by a day. */\nfunction allDayFrom(datePart: string): CalendarEventTime {\n  return { date: datePart };\n}\n\n/** Split `HH:MM[:SS]` into RFC3339's always-three segments, or null.\n *\n *  Split rather than one regex: expressing the optional seconds as `(?::(\\d{2}))?`\n *  nests a quantifier inside a quantified group, which the ReDoS lint rejects. */\nfunction clockSegments(clockPart: string): string | null {\n  const segments = clockPart.split(CLOCK_SEPARATOR);\n  if (segments.length < MIN_CLOCK_SEGMENTS || segments.length > MAX_CLOCK_SEGMENTS) return null;\n  if (!segments.every((segment) => TWO_DIGITS_RE.test(segment))) return null;\n  const [hours, minutes, seconds] = segments;\n  // `parseIsoDateTime` validated the hours and minutes but ignores seconds.\n  if (seconds !== undefined && Number(seconds) > MAX_SECONDS) return null;\n  return [hours, minutes, seconds ?? WHOLE_MINUTE_SECONDS].join(CLOCK_SEPARATOR);\n}\n\n/** `YYYY-MM-DDTHH:MM[:SS]` → its date and its RFC3339 clock, or null.\n *\n *  Validity comes from the shared strict parser, so an out-of-range clock or an\n *  impossible day is refused here rather than sent to Google. */\nfunction parseStoredDateTime(value: string): { datePart: string; clock: string } | null {\n  if (parseIsoDateTime(value) === null) return null;\n  const [datePart, clockPart, ...extra] = value.split(DATE_TIME_SEPARATOR);\n  if (datePart === undefined || clockPart === undefined || extra.length > 0) return null;\n  const clock = clockSegments(clockPart);\n  return clock === null ? null : { datePart, clock };\n}\n\n/** The zone designator to re-attach, or null when `previous` carries none. */\nexport function zoneSuffixOf(previous: string | undefined): string | null {\n  if (previous === undefined) return null;\n  return previous.match(ZONE_SUFFIX_RE)?.[0] ?? null;\n}\n\n/** Build the event time to send for one field.\n *\n *  `previous` is the value Google last reported for this event, absent for a\n *  locally-created record. Returns null when `local` is neither a stored\n *  datetime nor a raw Google value — the caller reports that record rather than\n *  letting this invent a time Google would silently accept. */\nexport function toGoogleEventTime(local: unknown, previous: string | undefined, calendarTimeZone: string): CalendarEventTime | null {\n  if (typeof local !== \"string\") return null;\n  const trimmed = local.trim();\n  if (trimmed.length === 0) return null;\n  const raw = rawGoogleTime(trimmed);\n  if (raw !== null) return raw;\n  const parts = parseStoredDateTime(trimmed);\n  if (parts === null) return null;\n  // An all-day event stays all-day even when its date moves: the user edited a\n  // date in a collection, not the event's kind.\n  if (previous !== undefined && isCalendarDate(previous)) return allDayFrom(parts.datePart);\n  const zone = zoneSuffixOf(previous);\n  const dateTime = `${parts.datePart}${DATE_TIME_SEPARATOR}${parts.clock}`;\n  return zone === null ? { dateTime, timeZone: calendarTimeZone } : { dateTime: `${dateTime}${zone}` };\n}\n","// Google Calendar date/dateTime → the shape a collection `datetime` field is\n// validated against (#2310).\n//\n// Google answers with RFC3339 including a zone designator for timed events\n// (`2026-05-12T08:45:00+09:00`, sometimes `…Z`) and a bare `YYYY-MM-DD` for\n// all-day ones. `parseIsoDateTime` — which the record lint, the calendar grid\n// and the day view all share — accepts only `YYYY-MM-DDTHH:MM[:SS]`, so both\n// shapes were stored and then reported as data problems on every synced record.\n//\n// The offset is DROPPED, not applied: the stored clock is then the one the user\n// reads off Google Calendar, and it stays that clock wherever the workspace is\n// opened. Converting to the host's local time would make a record's value\n// depend on the machine that happened to run the sync, and silently shift every\n// record when that machine moves timezone.\n//\n// Pure: no I/O, no clock, no locale.\n\n/** All-day events carry no clock; midnight is where the day view places them. */\nconst ALL_DAY_CLOCK = \"00:00\";\nconst DATE_ONLY_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n/** RFC3339 zone designator: `Z`, `+09:00`, `-0500`. */\nconst ZONE_SUFFIX_RE = /(?:Z|[+-]\\d{2}:?\\d{2})$/;\n/** Fractional seconds have no place in the collection datetime shape. */\nconst FRACTIONAL_SECONDS_RE = /\\.\\d+$/;\n\n/** Normalise one Google Calendar time value for storage in a `datetime` field.\n *  Anything that isn't a recognisable Google shape (empty string, non-string,\n *  free text) is returned untouched — the record lint should report it rather\n *  than have this function invent a value. */\nexport function toCollectionDateTime(value: unknown): unknown {\n  if (typeof value !== \"string\") return value;\n  const trimmed = value.trim();\n  if (DATE_ONLY_RE.test(trimmed)) return `${trimmed}T${ALL_DAY_CLOCK}`;\n  if (!trimmed.includes(\"T\")) return value;\n  return trimmed.replace(ZONE_SUFFIX_RE, \"\").replace(FRACTIONAL_SECONDS_RE, \"\");\n}\n","// Google event → collection record. Pure: no I/O, no clock, no workspace.\n//\n// A leaf on purpose. The pull writes records with it and the push rebuilds its\n// baseline with it (`pushPlan.ts`), which is what makes \"unchanged\" mean exactly\n// \"the pull would produce this\". Leaving it inside the pull would make the push\n// import the pull, and the pull now drives the push.\nimport type { CollectionFieldSpec, CollectionItem } from \"../collection/core/schema.js\";\nimport type { GOOGLE_CALENDAR_SOURCE_FIELDS } from \"../collection/core/schemaZ.js\";\nimport type { CalendarEventSummary } from \"./calendar.js\";\nimport { toCollectionDateTime } from \"./collectionDateTime.js\";\n\n/** The event fields a schema may map from — narrowed to real keys of\n *  `CalendarEventSummary` so the projection below needs no cast. */\nexport type GoogleCalendarSourceField = (typeof GOOGLE_CALENDAR_SOURCE_FIELDS)[number];\n\nconst DATETIME_FIELD_TYPE = \"datetime\";\n\n/** Own-property lookup, mirroring what the record lint reads: a spec reachable\n *  only through the prototype chain is not a DECLARED field, so it must not\n *  decide how a value is stored. */\nfunction declaredSpec(fields: Record<string, CollectionFieldSpec>, field: string): CollectionFieldSpec | undefined {\n  return Object.hasOwn(fields, field) ? fields[field] : undefined;\n}\n\n/** Google's raw value is normalised only into a field the schema declares as\n *  `datetime` — that is the type whose stored shape the record lint, the\n *  calendar grid and the day view all parse (#2310). A user who maps `start`\n *  onto a `string` field asked for Google's value verbatim and keeps it. */\nfunction projectValue(fields: Record<string, CollectionFieldSpec>, field: string, value: string): unknown {\n  return declaredSpec(fields, field)?.type === DATETIME_FIELD_TYPE ? toCollectionDateTime(value) : value;\n}\n\n/** Project one Google event onto the collection's own field names. The\n *  primary field always takes the event id — upsert-by-id is what keeps the\n *  sync idempotent, so it is deliberately not remappable. */\nexport function toCollectionRecord(\n  event: CalendarEventSummary,\n  map: Record<string, GoogleCalendarSourceField>,\n  primaryKey: string,\n  fields: Record<string, CollectionFieldSpec>,\n): CollectionItem {\n  const mapped = Object.entries(map).map(([field, source]): [string, unknown] => [field, projectValue(fields, field, event[source])]);\n  return { ...Object.fromEntries(mapped), [primaryKey]: event.id };\n}\n","// Deciding what a Collection → Google push should do to each record (#2598).\n//\n// Pure: the whole diff rule is expressed against three plain values — the local\n// record, the baseline Google last reported (`calendarPushState.ts`), and\n// Google's value right now — so it is testable without a workspace or a grant.\nimport type { CollectionFieldSpec, CollectionItem } from \"../collection/core/schema.js\";\nimport type { CalendarEventSummary } from \"./calendar.js\";\nimport type { ShadowEvent } from \"./calendarPushState.js\";\nimport { toCollectionRecord } from \"./collectionProjection.js\";\n\n/** The event fields Google lets a caller write. `htmlLink` and `status` are\n *  read-only, so a record column mapped to either is ignored here rather than\n *  rejected — the mapping was authored for the pull, and a push has no business\n *  invalidating it. */\nexport const PUSHABLE_SOURCE_FIELDS = [\"summary\", \"start\", \"end\", \"colorId\", \"description\", \"location\"] as const;\n\nexport type PushableSourceField = (typeof PUSHABLE_SOURCE_FIELDS)[number];\n\nconst isPushableSource = (source: string): source is PushableSourceField => PUSHABLE_SOURCE_FIELDS.some((field) => field === source);\n\n/** The subset of a schema's `map` a push can act on: collection field → event field. */\nexport function pushableMap(map: Record<string, string>): Record<string, PushableSourceField> {\n  return Object.fromEntries(Object.entries(map).filter((entry): entry is [string, PushableSourceField] => isPushableSource(entry[1])));\n}\n\n/** Event field → the record column feeding it. The schema authors the map the\n *  other way round (for the pull); a push looks up by event field. Two columns\n *  mapped to one event field is already ambiguous for the pull — last wins. */\nexport function bySourceField(map: Record<string, PushableSourceField>): Partial<Record<PushableSourceField, string>> {\n  return Object.fromEntries(Object.entries(map).map(([field, source]) => [source, field]));\n}\n\n/** Google accepts a caller-chosen event id of 5-1024 base32hex characters. A\n *  record id outside that (a semantic id like `team-standup`) cannot be used,\n *  and v1 reports it instead of re-keying the record — see the plan. */\nconst CLIENT_EVENT_ID_RE = /^[0-9a-v]{5,1024}$/;\n\nexport const isClientSettableEventId = (eventId: string): boolean => CLIENT_EVENT_ID_RE.test(eventId);\n\n/** A field's value as comparable/sendable text.\n *\n *  Absent, null and `\"\"` all collapse to `\"\"`: Google reports an inherited colour\n *  as `\"\"` while the record file may omit the key entirely, and treating those as\n *  different would report a local edit on every single push.\n *\n *  A non-primitive is JSON-encoded rather than left to `String`, whose\n *  `[object Object]` would make two DIFFERENT objects compare equal — a\n *  silently-skipped edit. */\nexport const fieldText = (value: unknown): string => {\n  if (value === undefined || value === null) return \"\";\n  if (typeof value === \"string\") return value;\n  if (typeof value === \"number\" || typeof value === \"boolean\" || typeof value === \"bigint\") return String(value);\n  // `JSON.stringify` answers undefined for a function or symbol — neither is a\n  // field value any schema produces, and \"\" keeps them comparing as absent.\n  return JSON.stringify(value) ?? \"\";\n};\n\n/** A stored datetime with no seconds — what the collection's own date-time input\n *  produces. Google's values always carry seconds, so the pull writes them and a\n *  hand-typed value does not. */\nconst SECONDLESS_DATETIME_RE = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$/;\nconst WHOLE_MINUTE = \":00\";\n\n/** Field text with `…T09:30` and `…T09:30:00` collapsed to one spelling.\n *\n *  Without this, a record the user typed a time into never stops looking edited:\n *  the push writes it, Google answers with seconds, the baseline projection keeps\n *  them, and the seconds-less record then differs from its own baseline on every\n *  later push — re-PATCHing the same event on every click, forever.\n *\n *  A value that carries a zone offset (a `start` mapped onto a `string` field) is\n *  left alone: it is Google's own text on both sides already. */\nconst comparableText = (value: unknown): string => {\n  const text = fieldText(value);\n  return SECONDLESS_DATETIME_RE.test(text) ? `${text}${WHOLE_MINUTE}` : text;\n};\n\n/** A baseline shaped as the event it came from, so the comparison can run\n *  through the very projection that wrote the record. `htmlLink`/`status` are\n *  never pushable, so their absence from the baseline cannot matter. */\nconst asEventSummary = (eventId: string, shadow: ShadowEvent): CalendarEventSummary => ({ id: eventId, htmlLink: \"\", status: \"\", ...shadow });\n\n/** What the record WOULD hold if nobody had edited it since the baseline.\n *\n *  Deliberately `toCollectionRecord` — the same function the pull writes with —\n *  so \"unchanged\" here means exactly \"the pull would produce this\". A private\n *  re-implementation would drift from it and report phantom edits. */\nexport function baselineRecord(\n  eventId: string,\n  shadow: ShadowEvent,\n  map: Record<string, PushableSourceField>,\n  primaryKey: string,\n  fields: Record<string, CollectionFieldSpec>,\n): CollectionItem {\n  return toCollectionRecord(asEventSummary(eventId, shadow), map, primaryKey, fields);\n}\n\n/** The mapped fields whose local value no longer matches the baseline. */\nexport function locallyChangedFields(record: CollectionItem, baseline: CollectionItem, map: Record<string, PushableSourceField>): PushableSourceField[] {\n  return Object.entries(map)\n    .filter(([field]) => comparableText(record[field]) !== comparableText(baseline[field]))\n    .map(([, source]) => source);\n}\n\n/** The fields Google changed since the baseline, restricted to the ones the\n *  local edit touches.\n *\n *  Field-level on purpose: Google moving an event the user retitled locally is\n *  not a conflict, because the patch only carries the title. Comparing whole\n *  events would refuse those pushes for no reason. */\nexport function conflictingFields(shadow: ShadowEvent, current: ShadowEvent, changed: readonly PushableSourceField[]): PushableSourceField[] {\n  return changed.filter((source) => comparableText(shadow[source]) !== comparableText(current[source]));\n}\n\nexport type RecordPlan =\n  | { kind: \"create\" }\n  | { kind: \"unchanged\" }\n  /** Locally edited; `fields` is what to PATCH, pending the conflict check. */\n  | { kind: \"changed\"; fields: PushableSourceField[] };\n\n/** Classify one record against its baseline, before Google is consulted.\n *\n *  No baseline means the record never came from a sync, so it is local-only and\n *  has to be created. Google is queried ONLY for a `changed` record, which is\n *  what keeps a push over a large collection cheap. */\nexport function planRecord(\n  eventId: string,\n  record: CollectionItem,\n  shadow: ShadowEvent | undefined,\n  map: Record<string, PushableSourceField>,\n  primaryKey: string,\n  fields: Record<string, CollectionFieldSpec>,\n): RecordPlan {\n  if (shadow === undefined) return { kind: \"create\" };\n  const changed = locallyChangedFields(record, baselineRecord(eventId, shadow, map, primaryKey, fields), map);\n  return changed.length === 0 ? { kind: \"unchanged\" } : { kind: \"changed\", fields: changed };\n}\n\n/** Whether an event that already holds this record's id may be adopted as its\n *  baseline.\n *\n *  A 409 from `insert` usually means a previous push created the event but its\n *  baseline never landed. It can ALSO mean an unrelated event happens to hold\n *  this id, and adopting that would bind the record to a stranger's event — so\n *  adoption needs evidence the two are the same logical event, not just the same\n *  id (Codex review).\n *\n *  The evidence is that the remote event ALREADY equals what this record would\n *  write. Adoption then records a baseline and writes nothing, so it cannot\n *  modify whatever that event is. Anything else is refused. */\nexport function mayAdoptExisting(\n  eventId: string,\n  record: CollectionItem,\n  existing: ShadowEvent,\n  map: Record<string, PushableSourceField>,\n  primaryKey: string,\n  fields: Record<string, CollectionFieldSpec>,\n): boolean {\n  return locallyChangedFields(record, baselineRecord(eventId, existing, map, primaryKey, fields), map).length === 0;\n}\n\n/** Record ids the baseline knows but the collection no longer holds — deleted\n *  locally. v1 reports the count and pushes nothing: `calendarDeleteEvent`\n *  removes the event for every attendee and cannot be undone. */\nexport function locallyDeletedIds(shadow: Record<string, ShadowEvent>, presentIds: readonly string[]): string[] {\n  const present = new Set(presentIds);\n  return Object.keys(shadow).filter((eventId) => !present.has(eventId));\n}\n","// Collection → Google Calendar push, on demand (#2598).\n//\n// The mirror of `collectionSync.ts`, and deliberately NOT its automatic twin:\n// this runs only when the user clicks, because a push writes to a calendar other\n// people may be reading. v1 creates and updates; it never deletes (a Google\n// delete removes the event for every attendee and cannot be undone).\n//\n// Both directions share `withCalendarLock`, so a push can never interleave with\n// the pull whose baseline it depends on.\nimport { discoverCollections } from \"../collection/server/discovery.js\";\nimport type { LoadedCollection } from \"../collection/server/discoveredCollection.js\";\nimport { storeFor } from \"../collection/server/store.js\";\nimport type { CollectionFieldSpec, CollectionItem } from \"../collection/core/schema.js\";\nimport { getGoogleAccessToken } from \"./auth.js\";\nimport { isGoogleApiError, HTTP_FORBIDDEN } from \"./apiClient.js\";\nimport {\n  canonicalCalendarId,\n  createCalendarEvent,\n  getCalendarEvent,\n  getCalendarMeta,\n  listCalendars,\n  updateCalendarEvent,\n  CANCELLED_EVENT_STATUS,\n  HTTP_CONFLICT,\n  HTTP_PRECONDITION_FAILED,\n  type CalendarEventInput,\n  type CalendarEventSummary,\n  type CalendarEventTime,\n  type UpdateCalendarEventInput,\n} from \"./calendar.js\";\nimport { loadCalendarShadow, saveCalendarShadow, toShadowEvent, type ShadowEvent } from \"./calendarPushState.js\";\nimport { withCalendarLock } from \"./calendarLock.js\";\nimport { toGoogleEventTime } from \"./pushDateTime.js\";\nimport {\n  bySourceField,\n  conflictingFields,\n  fieldText,\n  isClientSettableEventId,\n  locallyDeletedIds,\n  mayAdoptExisting,\n  planRecord,\n  pushableMap,\n  type PushableSourceField,\n} from \"./pushPlan.js\";\nimport { loadGoogleTokens } from \"./tokenStore.js\";\nimport { log } from \"./host.js\";\n\n/** Access roles that may write events. `reader` / `freeBusyReader` cannot, and a\n *  push under them fails per event with an opaque 403 — checked once up front so\n *  the user is told the real reason. */\nconst WRITABLE_ACCESS_ROLES: readonly string[] = [\"owner\", \"writer\"];\n\n/** What the up-front writability gate knows about the target calendar. */\nexport interface CalendarWriteTarget {\n  /** The caller's role on the calendar, or null when neither source reported one.\n   *  Null means unknown, never read-only: hard-denying an unknown role would\n   *  refuse a calendar the user can in fact write to. */\n  accessRole: string | null;\n  /** IANA zone for rebuilding a zone-less stored clock, `\"\"` when unreported. */\n  timeZone: string;\n}\n\n/** Whether to refuse the whole push before touching a single event.\n *\n *  Only on POSITIVE evidence of a non-writable role. A calendar whose role\n *  neither source reported is unknown and must fall through: hard-denying it\n *  would block the feature outright for a calendar the user can in fact write\n *  to. Those attempts surface Google's own 403 per record if the write really is\n *  not allowed. */\nexport const isDeniedAccessRole = (accessRole: string | null): boolean => accessRole !== null && !WRITABLE_ACCESS_ROLES.includes(accessRole);\n\n/** A role Google left blank is UNKNOWN, not a denial. `isDeniedAccessRole`\n *  refuses every role it does not recognise — `\"\"` included — so passing an\n *  unreported one straight through would refuse a calendar Google simply said\n *  nothing about. */\nexport const reportedAccessRole = (accessRole: string): string | null => accessRole || null;\n\nexport interface CalendarCollectionPushResult {\n  slug: string;\n  created: number;\n  updated: number;\n  /** Edited on both sides; skipped so neither version is destroyed. */\n  conflicts: number;\n  /** Records deleted locally. Reported only — v1 never deletes in Google. */\n  localDeletes: number;\n  /** Records that cannot be pushed as they stand, each with the reason. */\n  skipped: string[];\n  errors: string[];\n  /** Records whose local edit did NOT reach Google and would be destroyed by\n   *  the pull that follows an automatic push (#2620).\n   *\n   *  Only the two states where overwriting loses work with nowhere to recover\n   *  it from: a both-sides conflict, and an unexpected failure. A `skipped`\n   *  record is deliberately absent — several of its reasons (an id already\n   *  taken in Google, an unmappable span) are recovered BY letting the pull\n   *  write, so protecting them would stall the collection instead. */\n  unpushedIds: string[];\n}\n\nexport type CalendarPushOutcome =\n  | { kind: \"pushed\"; result: CalendarCollectionPushResult }\n  | { kind: \"not-a-calendar\" }\n  | { kind: \"not-linked\" }\n  | { kind: \"read-only\"; accessRole: string }\n  /** The push never got as far as a record: a revoked grant, an unreachable\n   *  Calendar API, a workspace read failure. Typed rather than left to throw so\n   *  the route answers in the same shape as the other setup problems instead of\n   *  an opaque 500. */\n  | { kind: \"failed\"; message: string };\n\n/** The I/O a push crosses, injected so every outcome can be exercised with fakes\n *  instead of a workspace on disk and a live Google grant. */\nexport interface CalendarPushDeps {\n  findCollection: (slug: string, workspaceRoot: string) => Promise<LoadedCollection | null>;\n  isLinked: () => Promise<boolean>;\n  accessToken: () => Promise<string>;\n  calendarMeta: (accessToken: string, calendarId: string | undefined) => Promise<CalendarWriteTarget>;\n}\n\n/** Resolve the calendar a schema names, for its writability and its timezone.\n *\n *  The list is consulted first because it answers for every added calendar in\n *  one call the push already makes. `\"primary\"` is matched on the `primary`\n *  flag, not the id — the primary calendar's own id is the account's email\n *  address, which never equals the literal the schema declares.\n *\n *  A calendar absent from the list is NOT denied: `calendarList` holds only the\n *  calendars the user has added, so one shared with write access can be missing\n *  from it (Codex review). It is asked about itself instead, which reports the\n *  same zone AND the real role. A 404 there means the calendar is genuinely\n *  unreachable, and propagates as a `failed` outcome. */\nasync function liveCalendarMeta(accessToken: string, calendarId: string | undefined): Promise<CalendarWriteTarget> {\n  const key = canonicalCalendarId(calendarId);\n  const calendars = await listCalendars(accessToken);\n  const listed = key === \"primary\" ? calendars.find((calendar) => calendar.primary) : calendars.find((calendar) => calendar.id === key);\n  if (listed) return { accessRole: listed.accessRole, timeZone: listed.timeZone };\n  const direct = await getCalendarMeta(accessToken, calendarId);\n  log.info(\"google\", \"calendar is not in the user's list — role read from the calendar itself\", { calendarId: key, accessRole: direct.accessRole });\n  return { accessRole: reportedAccessRole(direct.accessRole), timeZone: direct.timeZone };\n}\n\nasync function findCalendarCollection(slug: string, workspaceRoot: string): Promise<LoadedCollection | null> {\n  const all = await discoverCollections({ workspaceRoot });\n  return all.find((collection) => collection.slug === slug && collection.schema.googleCalendar) ?? null;\n}\n\nconst liveDeps: CalendarPushDeps = {\n  findCollection: findCalendarCollection,\n  isLinked: async () => Boolean((await loadGoogleTokens())?.refresh_token),\n  accessToken: getGoogleAccessToken,\n  calendarMeta: liveCalendarMeta,\n};\n\n/** Everything one record's push needs, resolved once for the whole run. */\ninterface PushContext {\n  accessToken: string;\n  calendarId: string | undefined;\n  /** The calendar's IANA zone, `\"\"` when Google did not report one. */\n  timeZone: string;\n  map: Record<string, PushableSourceField>;\n  bySource: Partial<Record<PushableSourceField, string>>;\n  primaryKey: string;\n  fields: Record<string, CollectionFieldSpec>;\n  shadow: Record<string, ShadowEvent>;\n}\n\ntype PushOutcome =\n  | { kind: \"created\"; event: CalendarEventSummary }\n  | { kind: \"updated\"; event: CalendarEventSummary }\n  | { kind: \"conflict\" }\n  /** `event` present only when this record's baseline had to be repaired (an\n   *  event that already existed in Google), so the write path can persist it. */\n  | { kind: \"unchanged\"; event?: CalendarEventSummary }\n  | { kind: \"skipped\"; message: string }\n  | { kind: \"error\"; message: string };\n\nexport type PushOutcomeKind = PushOutcome[\"kind\"];\n\nconst localValue = (ctx: PushContext, record: CollectionItem, source: PushableSourceField): unknown => {\n  const field = ctx.bySource[source];\n  return field === undefined ? undefined : record[field];\n};\n\ntype TimeResult = { ok: true; time: CalendarEventTime } | { ok: false; reason: string };\n\n/** One end of the span, rebuilt from the stored clock plus whatever the baseline\n *  remembers about the original (zone, all-day). */\nfunction eventTime(ctx: PushContext, record: CollectionItem, source: \"start\" | \"end\", previous: string | undefined): TimeResult {\n  const value = localValue(ctx, record, source);\n  if (value === undefined) return { ok: false, reason: `no \\`${source}\\` field is mapped` };\n  const time = toGoogleEventTime(value, previous, ctx.timeZone);\n  if (time === null) return { ok: false, reason: `\\`${source}\\` is not a date-time Google accepts (got ${JSON.stringify(value)})` };\n  // A zone-less dateTime needs `timeZone`; without one Google answers an opaque 400.\n  if (\"dateTime\" in time && time.timeZone === \"\") return { ok: false, reason: `the calendar reports no timezone, so \\`${source}\\` cannot be sent without one` };\n  return { ok: true, time };\n}\n\n/** `description` / `location` for a create — omitted when empty, since there is\n *  nothing to state about an event that carries neither. */\nconst optionalText = (ctx: PushContext, record: CollectionItem): Pick<CalendarEventInput, \"description\" | \"location\"> => {\n  const description = fieldText(localValue(ctx, record, \"description\"));\n  const location = fieldText(localValue(ctx, record, \"location\"));\n  return { ...(description ? { description } : {}), ...(location ? { location } : {}) };\n};\n\nasync function createFromRecord(ctx: PushContext, eventId: string, record: CollectionItem): Promise<PushOutcome> {\n  if (!isClientSettableEventId(eventId)) {\n    return { kind: \"skipped\", message: `${eventId}: the record id cannot be used as a Google event id (needs 5-1024 characters from 0-9a-v)` };\n  }\n  const start = eventTime(ctx, record, \"start\", undefined);\n  const end = eventTime(ctx, record, \"end\", undefined);\n  if (!start.ok) return { kind: \"skipped\", message: `${eventId}: ${start.reason}` };\n  if (!end.ok) return { kind: \"skipped\", message: `${eventId}: ${end.reason}` };\n  const colorId = fieldText(localValue(ctx, record, \"colorId\"));\n  const event = await createCalendarEvent(ctx.accessToken, {\n    eventId,\n    calendarId: ctx.calendarId,\n    summary: fieldText(localValue(ctx, record, \"summary\")),\n    start: start.time,\n    end: end.time,\n    ...optionalText(ctx, record),\n    ...(colorId ? { colorId } : {}),\n  });\n  return { kind: \"created\", event };\n}\n\ntype EventPatch = Pick<UpdateCalendarEventInput, \"summary\" | \"colorId\" | \"start\" | \"end\" | \"description\" | \"location\">;\ntype PatchResult = { ok: true; patch: EventPatch } | { ok: false; reason: string };\n\n/** The free-text fields of a patch, each only when the local edit touched it.\n *  Unlike `colorId`, `\"\"` is a real value here — Google clears the field. */\nconst textPatch = (ctx: PushContext, record: CollectionItem, changed: readonly PushableSourceField[]): EventPatch => {\n  const touched = ([\"summary\", \"description\", \"location\"] as const).filter((source) => changed.includes(source));\n  return Object.fromEntries(touched.map((source) => [source, fieldText(localValue(ctx, record, source))]));\n};\n\n/** Only the fields the local edit touched — PATCH semantics, so an untouched\n *  attendee list / recurrence rule is left alone. */\nfunction buildPatch(ctx: PushContext, record: CollectionItem, changed: readonly PushableSourceField[], shadow: ShadowEvent): PatchResult {\n  const resolved = ([\"start\", \"end\"] as const)\n    .filter((source) => changed.includes(source))\n    .map((source) => ({ source, result: eventTime(ctx, record, source, shadow[source]) }));\n  const [failure] = resolved.flatMap((entry) => (entry.result.ok ? [] : [entry.result.reason]));\n  if (failure !== undefined) return { ok: false, reason: failure };\n  const colorId = fieldText(localValue(ctx, record, \"colorId\"));\n  // Calendar rejects \"\" as a palette id, so a cleared colour cannot be sent.\n  // Reported rather than dropped: a silently dropped field leaves the record\n  // looking edited forever, re-pushing it on every click.\n  if (changed.includes(\"colorId\") && colorId === \"\") return { ok: false, reason: \"Google cannot clear an event colour once set\" };\n  return {\n    ok: true,\n    patch: {\n      ...textPatch(ctx, record, changed),\n      ...(changed.includes(\"colorId\") ? { colorId } : {}),\n      ...Object.fromEntries(resolved.flatMap((entry) => (entry.result.ok ? [[entry.source, entry.result.time]] : []))),\n    },\n  };\n}\n\n/** Google has dropped the event this record mirrors.\n *\n *  The advice used to be \"sync first, then re-create it\", which sent people\n *  nowhere: Google never resends a cancellation, so a sync sees nothing to do —\n *  and before #2688 the sync that DID see it deleted the record along with the\n *  edit. Deliberately silent on whether Google will hand the id back; that is\n *  unverified. */\nconst goneFromGoogle = (eventId: string): PushOutcome => ({\n  kind: \"skipped\",\n  message: `${eventId}: Google no longer has this event, and your local edit was kept. Delete this record, or copy it into a new one.`,\n});\n\nasync function updateFromRecord(\n  ctx: PushContext,\n  eventId: string,\n  record: CollectionItem,\n  changed: readonly PushableSourceField[],\n  shadow: ShadowEvent,\n): Promise<PushOutcome> {\n  const fetched = await getCalendarEvent(ctx.accessToken, { calendarId: ctx.calendarId, eventId });\n  if (fetched === null || fetched.event.status === CANCELLED_EVENT_STATUS) return goneFromGoogle(eventId);\n  if (conflictingFields(shadow, toShadowEvent(fetched.event), changed).length > 0) return { kind: \"conflict\" };\n  const built = buildPatch(ctx, record, changed, shadow);\n  if (!built.ok) return { kind: \"skipped\", message: `${eventId}: ${built.reason}` };\n  try {\n    // `If-Match` closes the window between the read above and this write: without\n    // it a change landing in between is silently overwritten, which is exactly\n    // what the conflict check exists to prevent.\n    const event = await updateCalendarEvent(ctx.accessToken, { eventId, calendarId: ctx.calendarId, ifMatch: fetched.etag, ...built.patch });\n    return { kind: \"updated\", event };\n  } catch (error) {\n    if (isGoogleApiError(error) && error.status === HTTP_PRECONDITION_FAILED) return { kind: \"conflict\" };\n    throw error;\n  }\n}\n\n/** Create, recovering from the one 409 that is provably safe to recover from.\n *\n *  A 409 means the id is taken, which is USUALLY a previous push whose baseline\n *  never landed. It can also be an unrelated event that happens to hold this id,\n *  so the recovery never writes: it adopts the baseline only when the remote\n *  event already equals what this record would write, and otherwise reports the\n *  fix. Patching whatever answered the 409 would let a push modify a stranger's\n *  event (Codex review).\n *\n *  Refusal is not a dead end — a Sync writes both the record and its baseline,\n *  after which the record pushes normally. */\nasync function createOrAdopt(ctx: PushContext, eventId: string, record: CollectionItem): Promise<PushOutcome> {\n  try {\n    return await createFromRecord(ctx, eventId, record);\n  } catch (error) {\n    if (!isGoogleApiError(error) || error.status !== HTTP_CONFLICT) throw error;\n    const fetched = await getCalendarEvent(ctx.accessToken, { calendarId: ctx.calendarId, eventId });\n    if (fetched === null) throw error;\n    if (mayAdoptExisting(eventId, record, toShadowEvent(fetched.event), ctx.map, ctx.primaryKey, ctx.fields)) {\n      return { kind: \"unchanged\", event: fetched.event };\n    }\n    return {\n      kind: \"skipped\",\n      message: `${eventId}: an event with this id already exists in Google and differs from this record — press Sync to adopt it, then push again`,\n    };\n  }\n}\n\n/** A 403 on a write is a permissions answer, but `googleApiError` appends the\n *  \"is the API enabled for the Cloud project?\" hint to every 403 — true for a\n *  disabled API, misleading here. Reached when the up-front gate let the push\n *  through on an unknown role, which is exactly when the user needs the real\n *  reason. */\nfunction writeFailure(eventId: string, error: unknown): PushOutcome {\n  if (isGoogleApiError(error) && error.status === HTTP_FORBIDDEN) {\n    return { kind: \"skipped\", message: `${eventId}: Google refused the write — you may not have permission to change events on this calendar` };\n  }\n  return { kind: \"error\", message: `${eventId}: ${String(error)}` };\n}\n\nasync function pushRecord(ctx: PushContext, eventId: string, record: CollectionItem): Promise<PushOutcome> {\n  try {\n    const shadow = ctx.shadow[eventId];\n    const plan = planRecord(eventId, record, shadow, ctx.map, ctx.primaryKey, ctx.fields);\n    if (plan.kind === \"unchanged\") return { kind: \"unchanged\" };\n    if (plan.kind === \"create\" || shadow === undefined) return await createOrAdopt(ctx, eventId, record);\n    return await updateFromRecord(ctx, eventId, record, plan.fields, shadow);\n  } catch (error) {\n    // One rejected event must not abandon the rest of the collection.\n    return writeFailure(eventId, error);\n  }\n}\n\n/** The baseline an outcome establishes, so the next push sees the record as\n *  unchanged instead of pushing it again. Empty for anything that wrote nothing\n *  and had no baseline to repair. */\nfunction pushedShadow(outcomes: readonly PushOutcome[]): Record<string, ShadowEvent> {\n  const written = outcomes.flatMap((outcome) => (\"event\" in outcome && outcome.event !== undefined ? [outcome.event] : []));\n  return Object.fromEntries(written.map((event) => [event.id, toShadowEvent(event)]));\n}\n\n/** One record's push, kept with the id so the pull can be told which records to\n *  leave alone. */\ninterface PushAttempt {\n  eventId: string;\n  outcome: PushOutcome;\n}\n\n/** See `unpushedIds` on the result for why `skipped` is not in this set. */\nconst UNPUSHED_KINDS: readonly PushOutcome[\"kind\"][] = [\"conflict\", \"error\"];\n\n/** Whether this outcome leaves a local edit that only exists locally, so the\n *  pull must not overwrite the record and the baseline must not advance. */\nexport const isUnpushed = (kind: PushOutcomeKind): boolean => UNPUSHED_KINDS.includes(kind);\n\nfunction tally(slug: string, attempts: readonly PushAttempt[], localDeletes: number): CalendarCollectionPushResult {\n  const outcomes = attempts.map((attempt) => attempt.outcome);\n  const count = (kind: PushOutcome[\"kind\"]): number => outcomes.filter((outcome) => outcome.kind === kind).length;\n  return {\n    slug,\n    created: count(\"created\"),\n    updated: count(\"updated\"),\n    conflicts: count(\"conflict\"),\n    localDeletes,\n    skipped: outcomes.flatMap((outcome) => (outcome.kind === \"skipped\" ? [outcome.message] : [])),\n    errors: outcomes.flatMap((outcome) => (outcome.kind === \"error\" ? [outcome.message] : [])),\n    unpushedIds: attempts.filter((attempt) => isUnpushed(attempt.outcome.kind)).map((attempt) => attempt.eventId),\n  };\n}\n\n/** Record ids whose local value no longer matches the baseline — edits that have\n *  not reached Google.\n *\n *  Pure, and deliberately built from `planRecord`, so it answers with exactly the\n *  records a push WOULD have acted on. A private re-implementation would drift\n *  from the push and protect the wrong set. */\nexport function locallyEditedIds(\n  records: readonly CollectionItem[],\n  shadow: Record<string, ShadowEvent>,\n  map: Record<string, PushableSourceField>,\n  primaryKey: string,\n  fields: Record<string, CollectionFieldSpec>,\n): string[] {\n  const ids = records.map((record) => ({ eventId: fieldText(record[primaryKey]), record }));\n  return ids\n    .filter(({ eventId, record }) => planRecord(eventId, record, shadow[eventId], map, primaryKey, fields).kind !== \"unchanged\")\n    .map(({ eventId }) => eventId);\n}\n\n/** The same set, read from the workspace.\n *\n *  Needed when a push cannot run AT ALL — a calendar whose role degraded to\n *  reader, a revoked grant, an IO failure. The pull still runs, since reading\n *  needs no write access, and would overwrite exactly these records while\n *  advancing their baseline past the edit (CodeRabbit review #2666). Computed\n *  from the stored baseline alone, so it works precisely when Google is the\n *  thing that is unreachable. */\nexport async function unsentLocalEdits(collection: LoadedCollection, workspaceRoot: string): Promise<string[]> {\n  const { schema } = collection;\n  const shadow = await loadCalendarShadow(schema.googleCalendar?.calendarId, workspaceRoot);\n  const records = await storeFor(collection, { workspaceRoot }).list();\n  return locallyEditedIds(records, shadow, pushableMap(schema.googleCalendar?.map ?? {}), schema.primaryKey, schema.fields);\n}\n\n/** Push one collection WITHOUT taking the calendar lock.\n *\n *  The caller must already hold it. The scheduled push→pull cycle\n *  (`collectionSync.ts`) runs inside the lock it took for the pull, and the lock\n *  is not reentrant — going through `pushCalendarForCollection` there would make\n *  the sync wait on itself forever. */\nexport async function pushCollectionNow(collection: LoadedCollection, workspaceRoot: string, deps: CalendarPushDeps = liveDeps): Promise<CalendarPushOutcome> {\n  const { schema, slug } = collection;\n  const calendarId = schema.googleCalendar?.calendarId;\n  const accessToken = await deps.accessToken();\n  const meta = await deps.calendarMeta(accessToken, calendarId);\n  if (isDeniedAccessRole(meta.accessRole)) return { kind: \"read-only\", accessRole: meta.accessRole ?? \"\" };\n\n  const map = pushableMap(schema.googleCalendar?.map ?? {});\n  const shadow = await loadCalendarShadow(calendarId, workspaceRoot);\n  const records = await storeFor(collection, { workspaceRoot }).list();\n  const ctx: PushContext = {\n    accessToken,\n    calendarId,\n    timeZone: meta.timeZone,\n    map,\n    bySource: bySourceField(map),\n    primaryKey: schema.primaryKey,\n    fields: schema.fields,\n    shadow,\n  };\n\n  const attempts: PushAttempt[] = [];\n  for (const record of records) {\n    const eventId = fieldText(record[schema.primaryKey]);\n    const outcome = await pushRecord(ctx, eventId, record);\n    attempts.push({ eventId, outcome });\n    // Persisted per record, not once at the end: an interruption after a\n    // successful write would otherwise leave that event with no baseline, and\n    // the next push would retry it as a create and hit Google's duplicate-id\n    // 409. Costs nothing for a record that wrote nothing (empty batch).\n    await saveCalendarShadow(calendarId, pushedShadow([outcome]), workspaceRoot);\n  }\n  const deletes = locallyDeletedIds(\n    shadow,\n    records.map((record) => fieldText(record[schema.primaryKey])),\n  );\n  const result = tally(slug, attempts, deletes.length);\n  if (result.errors.length > 0) log.warn(\"google\", \"calendar push finished with errors\", { slug, errors: result.errors.length });\n  return { kind: \"pushed\", result };\n}\n\n/** Push ONE collection's records to the calendar it declares.\n *\n *  \"Does this collection sync at all\" is answered before \"is Google linked\", for\n *  the reason `syncCalendarForCollection` gives: telling someone to link their\n *  account for a collection that never declared a calendar sends them fixing the\n *  wrong thing. */\nexport async function pushCalendarForCollection(slug: string, workspaceRoot: string, deps: CalendarPushDeps = liveDeps): Promise<CalendarPushOutcome> {\n  try {\n    const collection = await deps.findCollection(slug, workspaceRoot);\n    if (collection === null) return { kind: \"not-a-calendar\" };\n    if (!(await deps.isLinked())) return { kind: \"not-linked\" };\n    return await withCalendarLock(collection.schema.googleCalendar?.calendarId, () => pushCollectionNow(collection, workspaceRoot, deps));\n  } catch (error) {\n    // A throw here is a setup failure (revoked grant, Calendar API unreachable,\n    // unreadable workspace), not a per-record one. Reported in the same typed\n    // shape as the other setup problems so the route need not guess.\n    log.warn(\"google\", \"calendar push could not start\", { slug, error: String(error) });\n    return { kind: \"failed\", message: String(error) };\n  }\n}\n","// LLM-free Google Calendar → collection sync (#2095).\n//\n// The whole point of #2095 is that syncing must not cost tokens: this path\n// runs on the scheduler, calls the Calendar REST API directly, and writes\n// records itself. No chat, no agent, no MCP round-trip.\n//\n// Destination is not hardcoded — a collection opts in by declaring\n// `googleCalendar` in its schema, exactly the way a feed opts in by declaring\n// `ingest`. There is no preset calendar collection (the standalone Calendar\n// view was removed in 0.7.0); the user asks for one and the agent authors it.\nimport { stat } from \"node:fs/promises\";\nimport { MISSED_RUN_POLICIES, SCHEDULE_TYPES } from \"@receptron/task-scheduler\";\nimport type { SystemTaskDef } from \"../scheduler/adapter.js\";\nimport type { CollectionItem } from \"../collection/core/schema.js\";\nimport { discoverCollections } from \"../collection/server/discovery.js\";\nimport { getWorkspaceRoot } from \"../collection/server/host.js\";\nimport type { LoadedCollection } from \"../collection/server/discoveredCollection.js\";\nimport type { DeleteItemResult, WriteItemResult } from \"../collection/server/io.js\";\nimport { storeFor } from \"../collection/server/store.js\";\nimport { getGoogleAccessToken } from \"./auth.js\";\nimport { canonicalCalendarId, syncCalendarEvents, CANCELLED_EVENT_STATUS, type CalendarEventSummary } from \"./calendar.js\";\nimport { withCalendarLock } from \"./calendarLock.js\";\nimport { mergeIntoExisting } from \"../collection/core/project.js\";\nimport { toCollectionRecord } from \"./collectionProjection.js\";\nimport { pushCollectionNow, unsentLocalEdits, type CalendarCollectionPushResult, type CalendarPushOutcome } from \"./collectionPush.js\";\nimport {\n  claimCalendarSyncIfDue,\n  clearCalendarLastSyncedAt,\n  clearCalendarSyncToken,\n  loadCalendarSyncToken,\n  saveCalendarSyncToken,\n} from \"./calendarSyncStore.js\";\nimport { calendarSyncDueWindowMs, isCalendarSyncDue } from \"./calendarSyncDue.js\";\nimport { markCalendarBackfilled, needsCalendarBackfill } from \"./calendarBackfillState.js\";\nimport { clearCalendarShadow, loadCalendarShadow, saveCalendarShadow, toShadowEvent, type ShadowEvent } from \"./calendarPushState.js\";\nimport { baselineRecord, locallyChangedFields, pushableMap } from \"./pushPlan.js\";\nimport { loadGoogleTokens } from \"./tokenStore.js\";\nimport { log } from \"./host.js\";\n\nexport const GOOGLE_CALENDAR_SYNC_TASK_ID = \"system:google-calendar-sync\";\nconst DEFAULT_SYNC_INTERVAL_MS = 60 * 60 * 1000;\n\nexport interface CalendarCollectionSyncResult {\n  slug: string;\n  written: number;\n  removed: number;\n  /** Events that can NEVER be stored — e.g. an id the record-file sanitiser\n   *  rejects. Reported and skipped rather than retried; see `classifyWrite`. */\n  unwritable: string[];\n  /** Events left alone because the record they would overwrite holds an edit\n   *  Google has not seen. NOT an error — the token still advances past them; only\n   *  the baseline is held back, so the next push reports the conflict (#2684). */\n  withheld: string[];\n  /** Retryable failures. Any of these hold the sync token back. */\n  errors: string[];\n}\n\n/** `skipped` is a benign no-op; `unwritable` can never succeed so it must NOT\n *  hold the token; `error` is retryable and does hold it; `withheld` is a\n *  deliberate refusal to overwrite a local edit. */\ntype ApplyOutcome =\n  | { kind: \"written\" }\n  | { kind: \"removed\" }\n  | { kind: \"skipped\" }\n  | { kind: \"withheld\" }\n  | { kind: \"unwritable\"; message: string }\n  | { kind: \"error\"; message: string };\n\n// `writeItem` / `deleteItem` report most failures by RETURNING a non-`ok` kind\n// rather than throwing. Ignoring that would let the token advance past events\n// that never landed, and Google never resends them (Codex review #2184).\n//\n// But not every failure is worth retrying: an id the record-file sanitiser\n// rejects can never become valid, so holding the token for it would re-fetch\n// the same window forever and permanently kill that calendar's sync — far\n// worse than dropping the one event. Google's own ids are base32hex and pass,\n// but an imported/client-set id may contain characters the sanitiser refuses.\n// (Observed during Claude review, not flagged by a bot.)\nexport function classifyWrite(eventId: string, kind: WriteItemResult[\"kind\"]): ApplyOutcome {\n  if (kind === \"ok\") return { kind: \"written\" };\n  if (kind === \"invalid-id\") return { kind: \"unwritable\", message: `write ${eventId}: invalid-id` };\n  return { kind: \"error\", message: `write ${eventId}: ${kind}` };\n}\n\nexport function classifyDelete(eventId: string, kind: DeleteItemResult[\"kind\"]): ApplyOutcome {\n  if (kind === \"ok\") return { kind: \"removed\" };\n  // Cancelling an event we never stored is normal, not a failure.\n  if (kind === \"not-found\") return { kind: \"skipped\" };\n  if (kind === \"invalid-id\") return { kind: \"unwritable\", message: `delete ${eventId}: invalid-id` };\n  return { kind: \"error\", message: `delete ${eventId}: ${kind}` };\n}\n\n/** Whether this record still says what the workspace last saw Google say.\n *\n *  Compared against the baseline as it stood when this run STARTED READING, not\n *  as it stands now: a full re-walk clears the baseline before writing a new one\n *  (`restartFullSync`), so reading it live would answer \"no baseline\" for every\n *  event and protect nothing exactly when the window is widest (#2684).\n *\n *  A record with no baseline at all is NOT withheld — that is an event this\n *  workspace has never held, so there is no local edit to lose. */\nexport function unsentEditGuard(\n  schema: LoadedCollection[\"schema\"],\n  baseline: Record<string, ShadowEvent>,\n): (existing: CollectionItem, eventId: string) => boolean {\n  // Built once per collection rather than per event: a full walk runs this over\n  // every event the calendar has (Sourcery review #2687).\n  const map = pushableMap(schema.googleCalendar?.map ?? {});\n  return (existing, eventId) => {\n    const shadow = baseline[eventId];\n    if (shadow === undefined) return false;\n    return locallyChangedFields(existing, baselineRecord(eventId, shadow, map, schema.primaryKey, schema.fields), map).length > 0;\n  };\n}\n\n/** What the pull may do to the record behind one event.\n *\n *  The guard runs BEFORE the status is consulted, and that order is the whole\n *  fix: \"is there something local to lose here?\" outranks \"what did Google do\n *  to it?\". Asking about the status first is how a cancellation kept deleting\n *  records that held an edit Google had never seen (#2688), long after the\n *  same guard had been put in front of the overwrite (#2684). */\nexport function applyPlanFor(\n  existing: CollectionItem | null,\n  event: CalendarEventSummary,\n  hasUnsentEdit: (existing: CollectionItem, eventId: string) => boolean,\n): \"withhold\" | \"delete\" | \"write\" {\n  if (existing !== null && hasUnsentEdit(existing, event.id)) return \"withhold\";\n  return event.status === CANCELLED_EVENT_STATUS ? \"delete\" : \"write\";\n}\n\n/** Lay Google's mapped values over whatever the record already holds. Split out\n *  so `applyEvent` reads as read → decide → act rather than carrying the write\n *  itself (CodeRabbit review #2689). */\nasync function writeProjected(\n  write: NonNullable<ReturnType<typeof storeFor>[\"write\"]>,\n  event: CalendarEventSummary,\n  schema: LoadedCollection[\"schema\"],\n  existing: CollectionItem | null,\n): Promise<ApplyOutcome> {\n  const record = toCollectionRecord(event, schema.googleCalendar?.map ?? {}, schema.primaryKey, schema.fields);\n  return classifyWrite(event.id, (await write(event.id, mergeIntoExisting(existing, record))).kind);\n}\n\nasync function applyEvent(\n  collection: LoadedCollection,\n  event: CalendarEventSummary,\n  workspaceRoot: string,\n  hasUnsentEdit: (existing: CollectionItem, eventId: string) => boolean,\n): Promise<ApplyOutcome> {\n  const { schema } = collection;\n  try {\n    // Discovery rejects googleCalendar on a read-only (dataSource) schema,\n    // so absent write/delete is defense in depth, not a live path. The store\n    // threads the slug into the change publish, so an open view updates live.\n    const store = storeFor(collection, { workspaceRoot });\n    if (!store.write || !store.delete) return { kind: \"unwritable\", message: `collection '${collection.slug}' is read-only` };\n    // Read and decide immediately before acting. The set computed back at push\n    // time cannot cover an edit made while the window was in flight — minutes\n    // of it, on a full walk (#2684).\n    const existing = await store.read(event.id);\n    const plan = applyPlanFor(existing, event, hasUnsentEdit);\n    if (plan === \"withhold\") return { kind: \"withheld\" };\n    if (plan === \"delete\") return classifyDelete(event.id, (await store.delete(event.id)).kind);\n    return await writeProjected(store.write, event, schema, existing);\n  } catch (error) {\n    // A thrown IO error (EACCES, ENOSPC, …) must not abort the remaining events\n    // or the other collections on this calendar — record it as retryable so the\n    // token holds and the next run retries only what failed (CodeRabbit #2184).\n    return { kind: \"error\", message: `apply ${event.id}: ${String(error)}` };\n  }\n}\n\n/** The events of a window a pull may act on.\n *\n *  A record the push just refused to send is edited on BOTH sides. Writing\n *  Google's value over it destroys the local edit the push declined to resolve,\n *  so the local one stands. Single-sourced because the record write and the\n *  baseline write must agree exactly on which events they skip — disagreeing is\n *  what would silently overwrite Google on the next push (#2620). */\nexport function pullableEvents(events: readonly CalendarEventSummary[], unpushed: ReadonlySet<string>): CalendarEventSummary[] {\n  return events.filter((event) => !unpushed.has(event.id));\n}\n\n/** Report what an automatic push did, since nobody is watching a scheduled run.\n *  A conflict or an error means that record now diverges from Google until\n *  someone resolves it, which must not be silent. */\nfunction reportAutoPush(slug: string, result: CalendarCollectionPushResult): void {\n  const { created, updated, conflicts, skipped, errors, unpushedIds } = result;\n  if (created + updated > 0) log.info(\"google\", \"auto-pushed local calendar edits\", { slug, created, updated });\n  if (unpushedIds.length > 0) {\n    log.warn(\"google\", \"records the auto push could not send — the pull will leave them alone\", { slug, conflicts, unpushedIds });\n  }\n  if (skipped.length > 0) log.warn(\"google\", \"records the auto push skipped\", { slug, skipped });\n  if (errors.length > 0) log.warn(\"google\", \"auto push errors\", { slug, errors });\n}\n\n/** What an automatic push protected, per collection slug. `null` means it could\n *  not be worked out at all — see `PROTECTION_UNKNOWN`. */\nexport type UnpushedBySlug = ReadonlyMap<string, ReadonlySet<string> | null>;\n\nconst NOTHING_UNPUSHED: ReadonlySet<string> = new Set();\n\n/** A collection whose protection is unknown must not be pulled this run.\n *\n *  Reported through the retryable `errors` channel rather than as a special case:\n *  that already holds the sync token AND skips the baseline save, so the window\n *  simply replays next run with nothing lost. Failing OPEN here — pulling with no\n *  protection — would overwrite the very edits this exists to protect; the read\n *  that failed is no evidence that the pull's own writes would fail too, so they\n *  would land (CodeRabbit review #2666). */\nexport const PROTECTION_UNKNOWN = \"could not work out which records to protect from the pull\";\n\n/** What ONE collection's pull must leave alone: only what ITS OWN push did not\n *  send.\n *\n *  Scoped per collection because a calendar can back several of them, and one\n *  collection's unsent edit says nothing about the others. Sharing one set across\n *  the group starved a collection that never even declares `autoPush`: a\n *  neighbour's conflict froze its records — and the sync token still advanced, so\n *  Google never resent them (Codex review #2666). */\nexport const unpushedFor = (unpushed: UnpushedBySlug, slug: string): ReadonlySet<string> | null => {\n  const protection = unpushed.get(slug);\n  return protection === undefined ? NOTHING_UNPUSHED : protection;\n};\n\n/** What the calendar's BASELINE must leave alone: the union over every\n *  collection.\n *\n *  Deliberately not per collection, unlike the records above. `.push-state.json`\n *  holds ONE baseline per calendar, shared by every collection on it, so there is\n *  no per-collection baseline to hold back. Advancing it while any collection\n *  still has an unresolved conflict is the failure that silently overwrites\n *  Google on the next push, and holding it back only ever means \"keep reporting\n *  the conflict\" — so the union is the safe side of an asymmetry the shared\n *  storage forces.\n *\n *  A `null` (unknown) entry contributes nothing, because that collection reports\n *  a retryable error instead — which stops the baseline being saved at all. */\nexport const allUnpushed = (unpushed: UnpushedBySlug): ReadonlySet<string> => new Set([...unpushed.values()].flatMap((ids) => (ids === null ? [] : [...ids])));\n\n/** What a collection's pull must protect when its push did not run AT ALL.\n *\n *  Registering nothing here was a silent data loss of exactly the kind this\n *  feature exists to prevent: a calendar whose role degrades to reader refuses\n *  the whole push, yet the pull still runs — reading needs no write access — and\n *  overwrote every unsent local edit while advancing its baseline past it, so the\n *  next push could not even report the conflict (CodeRabbit review #2666). A\n *  collection that never declares `autoPush` is in that same state on every\n *  single run, which is how the loss reappeared without a failure in sight\n *  (#2683).\n *\n *  Protects the edited records rather than all of them, so an unchanged record\n *  keeps syncing normally. `null` when even that could not be worked out — the\n *  caller then refuses to pull the collection at all, because failing open here\n *  destroys exactly what this protects. */\nasync function protectUnsentEdits(collection: LoadedCollection, workspaceRoot: string, deps: PullProtectionDeps): Promise<ReadonlySet<string> | null> {\n  try {\n    const edited = await deps.unsentEdits(collection, workspaceRoot);\n    if (edited.length > 0) log.warn(\"google\", \"protecting local edits that have not reached Google\", { slug: collection.slug, edited });\n    return new Set(edited);\n  } catch (error) {\n    log.warn(\"google\", PROTECTION_UNKNOWN, { slug: collection.slug, error: String(error) });\n    return null;\n  }\n}\n\n/** The I/O the protection rule crosses, injected so every branch of it can be\n *  exercised with fakes instead of a workspace on disk and a live Google grant.\n *  The rule is what #2666 and #2683 both got wrong, so it is worth pinning. */\nexport interface PullProtectionDeps {\n  pushNow: (collection: LoadedCollection, workspaceRoot: string) => Promise<CalendarPushOutcome>;\n  unsentEdits: (collection: LoadedCollection, workspaceRoot: string) => Promise<string[]>;\n}\n\nconst livePullProtectionDeps: PullProtectionDeps = { pushNow: pushCollectionNow, unsentEdits: unsentLocalEdits };\n\n/** What ONE collection's pull must leave alone, pushing it first if it asked to\n *  be pushed.\n *\n *  A collection WITHOUT `autoPush` never pushes, so its local edits are unsent by\n *  definition — the same state a failed push leaves behind, and it needs the same\n *  protection. Pulling over them destroys the edit AND advances the baseline past\n *  it, after which no conflict can be detected any more (#2683). \"The push did not\n *  run\" is the condition that matters here; why it did not run is not.\n *\n *  MUST run inside the calendar lock the caller already holds — hence\n *  `pushCollectionNow` rather than `pushCalendarForCollection`, which would take\n *  the same non-reentrant lock and wait on itself forever.\n *\n *  A failed push must not stop the pull: the pull is what keeps the collection\n *  fresh, and a revoked write grant is no reason to freeze reading. */\nexport async function pullProtectionFor(\n  collection: LoadedCollection,\n  workspaceRoot: string,\n  deps: PullProtectionDeps = livePullProtectionDeps,\n): Promise<ReadonlySet<string> | null> {\n  if (!collection.schema.googleCalendar?.autoPush) return await protectUnsentEdits(collection, workspaceRoot, deps);\n  try {\n    const outcome = await deps.pushNow(collection, workspaceRoot);\n    if (outcome.kind === \"pushed\") {\n      reportAutoPush(collection.slug, outcome.result);\n      return new Set(outcome.result.unpushedIds);\n    }\n    log.warn(\"google\", \"auto push did not run\", { slug: collection.slug, reason: outcome.kind });\n  } catch (error) {\n    log.warn(\"google\", \"auto push failed — pulling anyway\", { slug: collection.slug, error: String(error) });\n  }\n  return await protectUnsentEdits(collection, workspaceRoot, deps);\n}\n\n/** Push the `autoPush` collections in this group, and answer with what every\n *  collection's pull must leave alone, keyed by collection. */\nexport async function pushAndProtect(\n  collections: readonly LoadedCollection[],\n  workspaceRoot: string,\n  deps: PullProtectionDeps = livePullProtectionDeps,\n): Promise<UnpushedBySlug> {\n  const unpushed = new Map<string, ReadonlySet<string> | null>();\n  for (const collection of collections) {\n    unpushed.set(collection.slug, await pullProtectionFor(collection, workspaceRoot, deps));\n  }\n  return unpushed;\n}\n\nasync function restartFullSync(accessToken: string, calendarId: string | undefined, workspaceRoot: string) {\n  await clearCalendarSyncToken(calendarId, workspaceRoot);\n  // The push baseline describes the records the consumed token accounted for, so\n  // it must not outlive that token — a full re-walk rewrites it from scratch.\n  await clearCalendarShadow(calendarId, workspaceRoot);\n  return await syncCalendarEvents(accessToken, { calendarId });\n}\n\n/** Whether a run may take the calendar, given what the shared marker says.\n *\n *  The scheduled door defers to it; every user-facing door claims regardless,\n *  because a Refresh click that silently returns nothing reads as an empty\n *  calendar, not as \"another host has this\". */\nexport type ClaimGuard = (lastSyncedAt: string | null) => boolean;\n\nconst ALWAYS_CLAIM: ClaimGuard = () => true;\n\n/** Sync ONE calendar and fan its events out to every collection bound to it.\n *\n *  The fan-out is not an optimisation, it is correctness: the sync token is\n *  keyed by `calendarId`, so syncing collection-by-collection would let the\n *  first collection advance the shared token and leave every later collection\n *  on the same calendar reading an already-consumed window — silently missing\n *  those events forever. Fetch once, apply to all, then advance the token.\n *  (Codex + CodeRabbit review on #2184.)\n *\n *  Queued per calendar for the same reason the fan-out exists: two passes over\n *  one calendar (a Refresh click landing during the scheduled run) would each\n *  load the SAME stored token and walk the same window. That is idempotent —\n *  writes are upserts by event id — but it is a wasted full walk. Queued, the\n *  second pass resumes from the token the first just stored and fetches only\n *  what is genuinely new. That queue is module state, so it orders the doors\n *  into THIS process only; `claimThenSync` is what other hosts can see. */\nexport async function syncCalendarGroup(\n  calendarId: string | undefined,\n  collections: readonly LoadedCollection[],\n  workspaceRoot: string,\n  mayClaim: ClaimGuard = ALWAYS_CLAIM,\n): Promise<CalendarCollectionSyncResult[]> {\n  return await withCalendarLock(calendarId, () => claimThenSync(calendarId, collections, workspaceRoot, mayClaim));\n}\n\n/** Take the workspace-shared marker BEFORE the calendar is touched, sync only if\n *  this run got it, and drop it again if the run could not happen at all.\n *\n *  Claiming first is the whole point: hosts tick in the same minute (interval\n *  schedules align to wall-clock boundaries), so a marker written on completion\n *  would leave the entire run — minutes, for a first full walk — open for a\n *  second host to start alongside this one and lose the push baseline it writes.\n *\n *  Released only when the sync could not run at all. A run that finished with\n *  retryable per-collection errors KEEPS the marker: it already held the token\n *  and the baseline back, so it replays on the next tick either way, and\n *  releasing would hand a permanently-failing calendar back to both hosts at\n *  once — the very overlap this guards (observed during Claude review). */\nasync function claimThenSync(\n  calendarId: string | undefined,\n  collections: readonly LoadedCollection[],\n  workspaceRoot: string,\n  mayClaim: ClaimGuard,\n): Promise<CalendarCollectionSyncResult[]> {\n  if (!(await claimCalendarSync(calendarId, workspaceRoot, mayClaim))) {\n    log.info(\"google\", \"skipping a calendar this workspace synced recently — another host may be on it\", { calendarId });\n    return [];\n  }\n  try {\n    return await syncCalendarGroupNow(calendarId, collections, workspaceRoot);\n  } catch (error) {\n    await releaseCalendarSyncClaim(calendarId, workspaceRoot);\n    throw error;\n  }\n}\n\n/** Never throws, and fails OPEN: the marker guards against duplicate work, it is\n *  not a precondition for syncing correctly. A workspace that cannot store it\n *  syncs the way it did before #2678. */\nasync function claimCalendarSync(calendarId: string | undefined, workspaceRoot: string, mayClaim: ClaimGuard): Promise<boolean> {\n  try {\n    return await claimCalendarSyncIfDue(calendarId, new Date().toISOString(), mayClaim, workspaceRoot);\n  } catch (error) {\n    log.warn(\"google\", \"could not stamp the calendar sync marker — another host may sync this calendar in parallel\", { calendarId, error: String(error) });\n    return true;\n  }\n}\n\nasync function releaseCalendarSyncClaim(calendarId: string | undefined, workspaceRoot: string): Promise<void> {\n  try {\n    await clearCalendarLastSyncedAt(calendarId, workspaceRoot);\n  } catch (error) {\n    log.warn(\"google\", \"could not release the calendar sync marker after a failed run\", { calendarId, error: String(error) });\n  }\n}\n\n/** Every event whose baseline must NOT advance: what the push could not send,\n *  plus what the apply refused to overwrite.\n *\n *  The two must agree exactly. The apply's refusals only became visible after it\n *  ran, so they join the union here rather than at push time — leaving them out\n *  would advance the baseline past a record the pull deliberately left holding a\n *  local edit, which is the silent overwrite this whole path exists to stop. */\nexport const heldBack = (unpushed: UnpushedBySlug, results: readonly CalendarCollectionSyncResult[]): ReadonlySet<string> =>\n  new Set([...allUnpushed(unpushed), ...results.flatMap((result) => result.withheld)]);\n\n/** The collections in this group that have never received the whole calendar,\n *  by slug. Asked of each collection's OWN records (`calendarBackfillState.ts`),\n *  because the sync token cannot answer it — it is keyed by calendar and shared\n *  by every consumer of that calendar (#2850). */\nexport async function collectionsNeedingBackfill(collections: readonly LoadedCollection[]): Promise<string[]> {\n  const checked = await Promise.all(\n    collections.map(async (collection) => {\n      const pending = await needsCalendarBackfill(collection.dataDir, collection.schema.googleCalendar?.calendarId);\n      return pending ? collection.slug : null;\n    }),\n  );\n  return checked.filter((slug): slug is string => slug !== null);\n}\n\n/** The token this run may resume from — none while any collection in the group\n *  still needs the whole calendar.\n *\n *  A stored token says how far THE CALENDAR has been read, by whoever read it:\n *  the standalone `google` tool's `calendarSync`, or a sibling collection that\n *  synced before this one existed. Resuming from it hands a brand-new collection\n *  a delta of a window it never received — a handful of records, reported as a\n *  success with no error, and never the history the docs promise (#2850).\n *\n *  The full walk fans out to the whole group, which is free: writes are upserts,\n *  so the collections that were already current simply rewrite what they hold. */\nexport async function resumableToken(\n  calendarId: string | undefined,\n  collections: readonly LoadedCollection[],\n  workspaceRoot: string,\n): Promise<string | undefined> {\n  const pending = await collectionsNeedingBackfill(collections);\n  if (pending.length === 0) return (await loadCalendarSyncToken(calendarId, workspaceRoot)) ?? undefined;\n  log.info(\"google\", \"walking the whole calendar — these collections have never received it\", { calendarId, collections: pending });\n  return undefined;\n}\n\n/** Record the backfill for every collection the window landed in. Called only\n *  after a FULL walk that fully landed: marking one after an incremental window\n *  would claim a history the records do not hold. */\nasync function markGroupBackfilled(calendarId: string | undefined, collections: readonly LoadedCollection[]): Promise<void> {\n  const walkedAt = new Date().toISOString();\n  await Promise.all(collections.map((collection) => markCalendarBackfilled(collection.dataDir, collection.schema.googleCalendar?.calendarId, walkedAt)));\n}\n\n/** What a partial window reports. Names the cause the user can actually act on:\n *  a page guard is reached when Google has to expand an enormous number of\n *  recurring instances, and one series with no end date is enough. */\nexport const PARTIAL_CALENDAR_WINDOW =\n  \"Google returned more pages of events than one sync pass walks, so only part of the calendar was copied. \" +\n  \"Give any recurring event with no end date a finite end date, then sync again.\";\n\n/** A truncated walk must not read as a completed one (#2850).\n *\n *  Added AFTER the token/baseline gate, not through it. A short fetch is not a\n *  failed write: the events that did arrive are Google's own and landed\n *  correctly, so their baseline is true and holding it back would freeze them —\n *  a record with no baseline reads as an unsent local edit, which the next pull\n *  then refuses to touch (#2683). What must not happen is the walk claiming\n *  completeness, and that is covered without the gate: `nextSyncToken` only\n *  appears on Google's last page, so a truncated walk has no token to advance,\n *  and the backfill marker is withheld separately. */\nexport const withPartialWindowError = (results: readonly CalendarCollectionSyncResult[], pagesExhausted: boolean): CalendarCollectionSyncResult[] =>\n  pagesExhausted ? results.map((result) => ({ ...result, errors: [...result.errors, PARTIAL_CALENDAR_WINDOW] })) : [...results];\n\n/** What a finished window is allowed to advance.\n *\n *  One pure rule rather than three conditions spread through the write-back,\n *  because the three answers genuinely differ and each wrong one is a SILENT\n *  data bug rather than a visible failure: a baseline held back freezes the\n *  records it describes (#2683), a baseline advanced past a failed write hides\n *  a conflict (#2620), a backfill marker set on a partial walk re-creates the\n *  #2850 gap, and a token advanced past either loses the window for good. */\nexport interface WindowAdvance {\n  /** Record what Google now says, so the next push can tell a local edit from a pull. */\n  baseline: boolean;\n  /** Claim these records now hold the WHOLE calendar. */\n  backfill: boolean;\n  /** Move the shared cursor past this window. */\n  token: boolean;\n}\n\n/** `landed` is the write-side verdict (`windowFullyLanded`); the rest describe\n *  the window Google returned.\n *\n *  A page-capped walk (`pagesExhausted`) keeps its baseline — the events that\n *  arrived are Google's own and were written correctly — but must NOT claim the\n *  backfill, or the collection would stop asking for the rest of its calendar.\n *  Its token is refused too, though Google makes that moot by sending\n *  `nextSyncToken` only on the last page. */\nexport function windowAdvance(window: { landed: boolean; walkedInFull: boolean; pagesExhausted: boolean; nextSyncToken?: string | undefined }): WindowAdvance {\n  if (!window.landed) return { baseline: false, backfill: false, token: false };\n  const complete = !window.pagesExhausted;\n  return { baseline: true, backfill: window.walkedInFull && complete, token: complete && window.nextSyncToken !== undefined };\n}\n\n/** The window this run should read, and whether it was a full walk. A 410\n *  restart is a full walk too, so it backfills just as well as a forced one. */\nasync function readWindow(\n  calendarId: string | undefined,\n  collections: readonly LoadedCollection[],\n  workspaceRoot: string,\n): Promise<{ result: Awaited<ReturnType<typeof syncCalendarEvents>>; walkedInFull: boolean }> {\n  const accessToken = await getGoogleAccessToken();\n  const resumeFrom = await resumableToken(calendarId, collections, workspaceRoot);\n  const first = await syncCalendarEvents(accessToken, { calendarId, syncToken: resumeFrom });\n  const result = first.fullResyncRequired ? await restartFullSync(accessToken, calendarId, workspaceRoot) : first;\n  if (result.pagesExhausted) {\n    log.warn(\"google\", \"the calendar walk ran out of pages — only part of it was copied\", { calendarId, events: result.events.length });\n  }\n  return { result, walkedInFull: resumeFrom === undefined || first.fullResyncRequired };\n}\n\nasync function syncCalendarGroupNow(\n  calendarId: string | undefined,\n  collections: readonly LoadedCollection[],\n  workspaceRoot: string,\n): Promise<CalendarCollectionSyncResult[]> {\n  // Push BEFORE the window is fetched, so a local edit is already in Google when\n  // the pull reads it: the record then comes back holding Google's own canonical\n  // value, and the baseline agrees with both. Pulling first would overwrite the\n  // very edit that was waiting to go up (#2620).\n  const unpushed = await pushAndProtect(collections, workspaceRoot);\n\n  // Snapshot AFTER the push and BEFORE the window is read. After, because the\n  // push writes a baseline per record it sends, and an earlier snapshot would\n  // read those records as locally edited. Before, because `restartFullSync`\n  // clears the baseline — the snapshot is what survives that clear and lets the\n  // apply still tell an edit from an untouched record (#2684).\n  const baseline = await loadCalendarShadow(calendarId, workspaceRoot);\n\n  const { result, walkedInFull } = await readWindow(calendarId, collections, workspaceRoot);\n  const applied = await applyWindowToGroup(collections, result.events, workspaceRoot, unpushed, baseline);\n  const advance = windowAdvance({\n    landed: windowFullyLanded(calendarId, applied),\n    walkedInFull,\n    pagesExhausted: result.pagesExhausted,\n    nextSyncToken: result.nextSyncToken,\n  });\n\n  if (advance.baseline) await saveCalendarShadow(calendarId, shadowUpdates(result.events, heldBack(unpushed, applied), baseline), workspaceRoot);\n  if (advance.backfill) await markGroupBackfilled(calendarId, collections);\n  if (advance.token && result.nextSyncToken) await advanceToken(calendarId, result.nextSyncToken, collections, workspaceRoot);\n  return withPartialWindowError(applied, result.pagesExhausted);\n}\n\n/** Apply one window to every collection on the calendar, honouring what each\n *  one's own push protected. A collection whose protection could not be worked\n *  out is not pulled at all — it reports a retryable error instead, which holds\n *  the token and the baseline back for the whole group. */\nasync function applyWindowToGroup(\n  collections: readonly LoadedCollection[],\n  events: readonly CalendarEventSummary[],\n  workspaceRoot: string,\n  unpushed: UnpushedBySlug,\n  baseline: Record<string, ShadowEvent>,\n): Promise<CalendarCollectionSyncResult[]> {\n  const results: CalendarCollectionSyncResult[] = [];\n  for (const collection of collections) {\n    const protection = unpushedFor(unpushed, collection.slug);\n    results.push(\n      protection === null\n        ? { slug: collection.slug, written: 0, removed: 0, unwritable: [], withheld: [], errors: [PROTECTION_UNKNOWN] }\n        : await applyEventsToCollection(collection, events, workspaceRoot, protection, baseline),\n    );\n  }\n  return results;\n}\n\n/** Whether the token and baseline may advance past this window.\n *\n *  Google never resends a window, so advancing past a failed write would lose\n *  those events for good; holding the token back just replays them next run\n *  (writes are idempotent). An `unwritable` event can never succeed, so it does\n *  NOT hold the token — but it is logged loudly, since it will silently never\n *  appear in the collection. */\nfunction windowFullyLanded(calendarId: string | undefined, results: readonly CalendarCollectionSyncResult[]): boolean {\n  // Logged per collection, not flattened across the group: a calendar can back\n  // several, and \"one of them is stuck\" is unactionable without the slug — the\n  // more so now that `autoPush` runs this unattended (CodeRabbit review #2666).\n  results\n    .filter((entry) => entry.unwritable.length > 0)\n    .forEach((entry) =>\n      log.warn(\"google\", \"skipping calendar events that can never be stored\", { calendarId, slug: entry.slug, unwritable: entry.unwritable }),\n    );\n  // A withheld event is a decision, not a failure: the record kept a local edit\n  // Google has not seen, so re-fetching the window would only refuse it again.\n  // The token advances past it and the BASELINE is what holds back, which is\n  // what keeps the next push able to report the conflict (#2684).\n  results\n    .filter((entry) => entry.withheld.length > 0)\n    .forEach((entry) =>\n      log.warn(\"google\", \"leaving calendar records alone — they hold edits Google has not seen\", { calendarId, slug: entry.slug, withheld: entry.withheld }),\n    );\n  const failed = results.filter((entry) => entry.errors.length > 0);\n  failed.forEach((entry) => log.warn(\"google\", \"holding back calendar sync token after failed writes\", { calendarId, slug: entry.slug, errors: entry.errors }));\n  return failed.length === 0;\n}\n\n/** The baseline this window establishes: what Google now says per event, and\n *  `null` for a cancelled one so a recreate cannot resume from a dead baseline.\n *\n *  An event whose record the push could not send is left OUT, and that omission\n *  is load-bearing. Advancing its baseline to Google's new value while the record\n *  keeps the local one would make the next push read a plain one-sided edit —\n *  no conflict to detect any more — and quietly overwrite Google. Held back, the\n *  baseline stays older than both sides, so the conflict keeps being reported\n *  until someone resolves it (#2620).\n *\n *  `held` carries what those events must KEEP. Omitting them is enough on an\n *  incremental run, where the file is merged rather than replaced — but a full\n *  re-walk CLEARS the baseline first (`restartFullSync`), and there omission\n *  drops the entry for good. The next push would then read a conflicted record\n *  as a brand-new create, hit Google's duplicate-id 409 and refuse it, instead\n *  of reporting the conflict it actually is. Re-stating the pre-run value makes\n *  a held-back event behave the same either way (observed during Claude review;\n *  no bot flagged it). */\nexport function shadowUpdates(\n  events: readonly CalendarEventSummary[],\n  unpushed: ReadonlySet<string> = new Set(),\n  held: Record<string, ShadowEvent> = {},\n): Record<string, ShadowEvent | null> {\n  const advanced = pullableEvents(events, unpushed).map((event): [string, ShadowEvent | null] => [\n    event.id,\n    event.status === CANCELLED_EVENT_STATUS ? null : toShadowEvent(event),\n  ]);\n  const kept = [...unpushed].flatMap((eventId): [string, ShadowEvent][] => (held[eventId] === undefined ? [] : [[eventId, held[eventId]]]));\n  return Object.fromEntries([...kept, ...advanced]);\n}\n\n/** Save the window's token unless every collection that consumed it was deleted\n *  while the sync was in flight.\n *\n *  The sync opens with a `discoverCollections()` snapshot, so a delete landing\n *  mid-run has already cleared this calendar's token by the time we get here\n *  (`releaseOrphanedCalendarToken`). Saving anyway would resurrect exactly the\n *  orphan the delete removed, and the next collection on this calendar would\n *  resume from a token describing records it never received (#2428).\n *\n *  One survivor is enough: it still needs the incremental position. */\nasync function advanceToken(\n  calendarId: string | undefined,\n  nextSyncToken: string,\n  collections: readonly LoadedCollection[],\n  workspaceRoot: string,\n): Promise<void> {\n  if (!(await anySyncedCollectionSurvives(collections))) {\n    log.info(\"google\", \"not advancing the sync token — every collection on this calendar was deleted mid-sync\", { calendarId });\n    return;\n  }\n  await saveCalendarSyncToken(calendarId, nextSyncToken, workspaceRoot);\n}\n\nasync function pathExists(absPath: string): Promise<boolean> {\n  try {\n    await stat(absPath);\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/** Liveness of the collections a sync just wrote to, checked against the skill\n *  dir `deleteCollection` removes. `exists` is injected so the rule is testable\n *  without a filesystem. An empty group has no survivor by definition. */\nexport async function anySyncedCollectionSurvives(\n  collections: readonly Pick<LoadedCollection, \"skillDir\">[],\n  exists: (absPath: string) => Promise<boolean> = pathExists,\n): Promise<boolean> {\n  const alive = await Promise.all(collections.map((collection) => exists(collection.skillDir)));\n  return alive.some(Boolean);\n}\n\nasync function applyEventsToCollection(\n  collection: LoadedCollection,\n  events: readonly CalendarEventSummary[],\n  workspaceRoot: string,\n  unpushed: ReadonlySet<string>,\n  baseline: Record<string, ShadowEvent>,\n): Promise<CalendarCollectionSyncResult> {\n  const hasUnsentEdit = unsentEditGuard(collection.schema, baseline);\n  const attempts: { eventId: string; outcome: ApplyOutcome }[] = [];\n  for (const event of pullableEvents(events, unpushed)) {\n    attempts.push({ eventId: event.id, outcome: await applyEvent(collection, event, workspaceRoot, hasUnsentEdit) });\n  }\n  const outcomes = attempts.map((attempt) => attempt.outcome);\n  return {\n    slug: collection.slug,\n    written: outcomes.filter((outcome) => outcome.kind === \"written\").length,\n    removed: outcomes.filter((outcome) => outcome.kind === \"removed\").length,\n    withheld: attempts.flatMap((attempt) => (attempt.outcome.kind === \"withheld\" ? [attempt.eventId] : [])),\n    unwritable: outcomes.flatMap((outcome) => (outcome.kind === \"unwritable\" ? [outcome.message] : [])),\n    errors: outcomes.flatMap((outcome) => (outcome.kind === \"error\" ? [outcome.message] : [])),\n  };\n}\n\n/** A refresh token is what lets the scheduler run unattended; without one\n *  there is nothing to sync with. */\nasync function isGoogleLinked(): Promise<boolean> {\n  return Boolean((await loadGoogleTokens())?.refresh_token);\n}\n\n/** Group the declaring collections by the calendar they read, so each calendar\n *  is fetched exactly once.\n *\n *  Keyed by the CANONICAL id, not the declared one: an omitted `calendarId` and\n *  an explicit `\"primary\"` address the same calendar and therefore share one\n *  sync token, so grouping them apart would let one group advance the token out\n *  from under the other — the very loss this grouping exists to prevent\n *  (Codex review #2184). */\nexport function groupByCalendar(collections: readonly LoadedCollection[]): Map<string, LoadedCollection[]> {\n  const groups = new Map<string, LoadedCollection[]>();\n  for (const collection of collections) {\n    const key = canonicalCalendarId(collection.schema.googleCalendar?.calendarId);\n    groups.set(key, [...(groups.get(key) ?? []), collection]);\n  }\n  return groups;\n}\n\n/** The minimum a value needs for the orphan check: just the calendar it reads.\n *  Structural so the rule can be exercised without building a LoadedCollection. */\nexport interface CalendarDeclaring {\n  googleCalendar?: { calendarId?: string | undefined } | undefined;\n}\n\n/** The canonical calendar whose sync token nothing needs any more, or null.\n *\n *  Sync tokens are keyed by calendar, NOT by collection, so a deleted\n *  collection's token outlives it. Recreating a collection on the same calendar\n *  then resumes from that token and receives only the delta — the new\n *  collection never gets the history (#2428).\n *\n *  Returns null while ANY remaining collection still reads that calendar:\n *  clearing a live calendar's token costs a full re-walk on the next sync. The\n *  comparison is on the CANONICAL id for the same reason `groupByCalendar` is —\n *  an omitted `calendarId` and an explicit `\"primary\"` are one calendar. */\nexport function orphanedCalendarId(deleted: CalendarDeclaring, remaining: readonly CalendarDeclaring[]): string | null {\n  if (!deleted.googleCalendar) return null;\n  const key = canonicalCalendarId(deleted.googleCalendar.calendarId);\n  const stillRead = remaining.some((other) => other.googleCalendar && canonicalCalendarId(other.googleCalendar.calendarId) === key);\n  return stillRead ? null : key;\n}\n\n/** Drop the sync token of a just-deleted collection's calendar, unless another\n *  collection still reads it. Call AFTER the delete lands — the check reads the\n *  collections that survive it.\n *\n *  Returns the cleared calendar id, or null when nothing was cleared. Never\n *  throws: a failed cleanup must not fail the delete it follows. */\nexport async function releaseOrphanedCalendarToken(deleted: CalendarDeclaring, workspaceRoot: string): Promise<string | null> {\n  try {\n    if (!deleted.googleCalendar) return null;\n    const remaining = await discoverCollections({ workspaceRoot });\n    const orphaned = orphanedCalendarId(\n      deleted,\n      remaining.map((collection) => collection.schema),\n    );\n    if (orphaned === null) return null;\n    await clearCalendarSyncToken(orphaned, workspaceRoot);\n    await clearCalendarShadow(orphaned, workspaceRoot);\n    await clearCalendarLastSyncedAt(orphaned, workspaceRoot);\n    log.info(\"google\", \"cleared the sync token of a calendar no collection reads any more\", { calendarId: orphaned });\n    return orphaned;\n  } catch (error) {\n    log.warn(\"google\", \"could not release the deleted collection's calendar sync token\", { error: String(error) });\n    return null;\n  }\n}\n\n/** Every declaring collection, grouped by the calendar it reads. */\nasync function declaringGroups(workspaceRoot: string): Promise<Map<string, LoadedCollection[]>> {\n  const all = await discoverCollections({ workspaceRoot });\n  return groupByCalendar(all.filter((collection) => collection.schema.googleCalendar));\n}\n\n/** Whether a background sync of these groups may run at all.\n *\n *  Authoring the collection before linking the account is an expected state,\n *  not a failure. Checking once here keeps it a quiet skip instead of an\n *  access-token throw per calendar, every hour, until the user links (#2188).\n *  A user-triggered sync answers differently — it says so out loud.\n *\n *  Deliberately not phrased as a per-host state: the link lives in\n *  `~/.config/mulmo`, which carries no app name, so every host on the machine\n *  shares one answer. Reading it as \"linked on this host only\" is what made the\n *  calendar look implicitly exclusive when it never was (#2678). */\nasync function backgroundSyncAllowed(groups: Map<string, LoadedCollection[]>): Promise<boolean> {\n  if (groups.size === 0) return false;\n  if (await isGoogleLinked()) return true;\n  log.info(\"google\", \"skipping calendar sync — no Google account linked\", { calendars: groups.size });\n  return false;\n}\n\n/** Run each group, isolating failures per calendar — one unreachable calendar\n *  (or a revoked grant) must not stop the others. `mayClaim` is evaluated per\n *  calendar as it comes up, never once for the whole map: an earlier calendar's\n *  full walk takes minutes, and a decision made before it started says nothing\n *  about who holds this one now. */\nasync function runCalendarGroups(\n  groups: Map<string, LoadedCollection[]>,\n  workspaceRoot: string,\n  mayClaim: ClaimGuard = ALWAYS_CLAIM,\n): Promise<CalendarCollectionSyncResult[]> {\n  const results: CalendarCollectionSyncResult[] = [];\n  for (const [calendarId, collections] of groups) {\n    try {\n      results.push(...(await syncCalendarGroup(calendarId, collections, workspaceRoot, mayClaim)));\n    } catch (error) {\n      log.warn(\"google\", \"calendar sync failed\", { calendarId, error: String(error) });\n      results.push(\n        ...collections.map((collection) => ({ slug: collection.slug, written: 0, removed: 0, unwritable: [], withheld: [], errors: [String(error)] })),\n      );\n    }\n  }\n  return results;\n}\n\n/** Sync every collection whose calendar is due — no host in this workspace has\n *  started one within `intervalMs` (#2678). Without that gate this walked every\n *  declaring group on every tick, so a second host registering the same task\n *  simply doubled the runs, concurrently.\n *\n *  Dueness is not decided here, only described: the guard is handed down and\n *  evaluated where the marker is written, so the answer cannot go stale between\n *  deciding and claiming (Codex review #2680). */\nexport async function syncDueCalendarCollections(\n  workspaceRoot: string,\n  intervalMs: number = DEFAULT_SYNC_INTERVAL_MS,\n): Promise<CalendarCollectionSyncResult[]> {\n  const groups = await declaringGroups(workspaceRoot);\n  if (!(await backgroundSyncAllowed(groups))) return [];\n  const windowMs = calendarSyncDueWindowMs(intervalMs);\n  return await runCalendarGroups(groups, workspaceRoot, (lastSyncedAt) => isCalendarSyncDue(lastSyncedAt, windowMs));\n}\n\n/** The groups holding a collection that still needs the whole calendar.\n *\n *  This used to ask whether the CALENDAR had a stored token, which is not the\n *  same question and is why the trigger silently did nothing for the #2850\n *  reporter: their calendar already had a token — from the standalone `google`\n *  tool, and on later attempts from the collection they had just deleted by\n *  hand — so the brand-new collection matched nothing and its first sync never\n *  ran at all. `pending` is injected so the rule is testable without a\n *  workspace on disk.\n *\n *  Still self-silencing: a landed full walk marks its collections, so a group\n *  stops matching once every collection in it holds the history. */\nexport async function groupsNeedingBackfill<T>(groups: Map<string, T>, pending: (value: T) => Promise<boolean>): Promise<Map<string, T>> {\n  const checked = await Promise.all([...groups].map(async (entry) => ((await pending(entry[1])) ? entry : null)));\n  return new Map(checked.filter((entry): entry is [string, T] => entry !== null));\n}\n\n/** Sync only the calendars a collection has never received in full — the first\n *  sync for a just-created collection, which otherwise stays empty until the\n *  hourly scheduler run (#2427). Cheap and safe to call on every config write. */\nexport async function syncNewCalendarCollections(workspaceRoot: string): Promise<CalendarCollectionSyncResult[]> {\n  const groups = await declaringGroups(workspaceRoot);\n  const pending = await groupsNeedingBackfill(groups, async (collections) => (await collectionsNeedingBackfill(collections)).length > 0);\n  if (!(await backgroundSyncAllowed(pending))) return [];\n  log.info(\"google\", \"running the first sync for calendars a collection has never received in full\", { calendars: [...pending.keys()] });\n  return await runCalendarGroups(pending, workspaceRoot);\n}\n\n/** A user-triggered sync's outcome. `not-a-calendar` and `not-linked` are\n *  states the caller must report rather than swallow: a Refresh click that\n *  quietly returns \"0 written\" reads as an empty calendar, not as a setup gap. */\nexport type ManualCalendarSyncOutcome = { kind: \"synced\"; results: CalendarCollectionSyncResult[] } | { kind: \"not-a-calendar\" } | { kind: \"not-linked\" };\n\n/** The I/O a manual sync crosses, injectable so the three outcomes can be\n *  exercised with fakes instead of a workspace on disk and a live Google grant\n *  (CodeRabbit review #2566). */\nexport interface ManualCalendarSyncDeps {\n  loadGroups: (workspaceRoot: string) => Promise<Map<string, LoadedCollection[]>>;\n  isLinked: () => Promise<boolean>;\n  runGroups: (groups: Map<string, LoadedCollection[]>, workspaceRoot: string) => Promise<CalendarCollectionSyncResult[]>;\n}\n\nconst liveManualSyncDeps: ManualCalendarSyncDeps = { loadGroups: declaringGroups, isLinked: isGoogleLinked, runGroups: runCalendarGroups };\n\n/** Sync the calendar ONE collection reads, on demand (the Refresh button).\n *\n *  Deliberately syncs the whole group, not just `slug`: the sync token is keyed\n *  by calendar, so consuming a window for one collection would leave the others\n *  on that calendar reading an already-consumed one. Returns every result of the\n *  group so the caller can report the requested slug's own counts.\n *\n *  \"Does this collection sync at all\" is answered BEFORE \"is Google linked\":\n *  telling someone to link their account for a collection that never declared a\n *  calendar sends them fixing the wrong thing. */\nexport async function syncCalendarForCollection(\n  slug: string,\n  workspaceRoot: string,\n  deps: ManualCalendarSyncDeps = liveManualSyncDeps,\n): Promise<ManualCalendarSyncOutcome> {\n  const groups = await deps.loadGroups(workspaceRoot);\n  const owning = [...groups].filter(([, collections]) => collections.some((collection) => collection.slug === slug));\n  if (owning.length === 0) return { kind: \"not-a-calendar\" };\n  if (!(await deps.isLinked())) return { kind: \"not-linked\" };\n  return { kind: \"synced\", results: await deps.runGroups(new Map(owning), workspaceRoot) };\n}\n\n/** The interval this task is scheduled on right now. A non-interval schedule\n *  cannot be reached — the factory below builds an interval one — but the type\n *  allows it, so it falls back rather than asserting. */\nfunction scheduledIntervalMs(task: SystemTaskDef): number {\n  return task.schedule.type === SCHEDULE_TYPES.interval ? task.schedule.intervalMs : DEFAULT_SYNC_INTERVAL_MS;\n}\n\n/** Scheduler registration, shaped like `feedRefreshTaskDef` so hosts wire it\n *  with a single line.\n *\n *  `run` reads the interval back off the definition instead of closing over the\n *  option: a host rewrites `schedule` from its own overrides file AFTER this\n *  returns, and the due window has to follow it. Frozen at the default, a\n *  shortened interval would tick often and skip nearly every tick. */\nexport function googleCalendarSyncTaskDef(opts?: { workspaceRoot?: string; intervalMs?: number }): SystemTaskDef {\n  const def: SystemTaskDef = {\n    id: GOOGLE_CALENDAR_SYNC_TASK_ID,\n    name: \"Google Calendar sync\",\n    description: \"Pulls changed Google Calendar events into any collection declaring `googleCalendar`, without invoking the LLM.\",\n    schedule: { type: SCHEDULE_TYPES.interval, intervalMs: opts?.intervalMs ?? DEFAULT_SYNC_INTERVAL_MS },\n    missedRunPolicy: MISSED_RUN_POLICIES.runOnce,\n    run: () => syncDueCalendarCollections(opts?.workspaceRoot ?? getWorkspaceRoot(), scheduledIntervalMs(def)).then(() => {}),\n  };\n  return def;\n}\n","// Google Tasks v1 REST calls. `@default` is Google's alias for the user's\n// default task list, so callers can stay list-agnostic.\nimport { asRecord, googleRequest, itemsOf, stringField, DEFAULT_LIST_MAX_RESULTS } from \"./apiClient.js\";\n\nconst TASKS_BASE_URL = \"https://tasks.googleapis.com/tasks/v1\";\nconst TASKS_API_LABEL = \"Google Tasks API\";\nconst DEFAULT_TASK_LIST_ID = \"@default\";\nconst TASK_STATUS_COMPLETED = \"completed\";\nconst TASK_STATUS_NEEDS_ACTION = \"needsAction\";\nconst MAX_TASK_LISTS = 50;\n\nexport interface TaskListSummary {\n  id: string;\n  title: string;\n}\n\nexport interface TaskSummary {\n  id: string;\n  title: string;\n  status: string;\n  due: string;\n  notes: string;\n}\n\nexport interface ListTasksInput {\n  taskListId?: string | undefined;\n  maxResults?: number | undefined;\n  showCompleted?: boolean | undefined;\n}\n\nexport interface CreateTaskInput {\n  title: string;\n  notes?: string | undefined;\n  /** RFC3339. Google stores a DATE only — the time part is recorded but\n   *  ignored by the UI, so callers should not promise time-of-day fidelity. */\n  due?: string | undefined;\n  taskListId?: string | undefined;\n}\n\nexport interface CompleteTaskInput {\n  taskId: string;\n  taskListId?: string | undefined;\n}\n\nexport interface UpdateTaskInput {\n  taskId: string;\n  title?: string | undefined;\n  /** `\"\"` clears the notes; omit to leave them untouched. */\n  notes?: string | undefined;\n  /** RFC3339. Same DATE-only caveat as `CreateTaskInput.due`. Google rejects\n   *  `\"\"`, so a due date can be changed but not removed through this call. */\n  due?: string | undefined;\n  taskListId?: string | undefined;\n}\n\nexport const toTaskListSummary = (value: unknown): TaskListSummary => {\n  const record = asRecord(value);\n  return { id: stringField(record, \"id\"), title: stringField(record, \"title\") };\n};\n\nexport const toTaskSummary = (value: unknown): TaskSummary => {\n  const record = asRecord(value);\n  return {\n    id: stringField(record, \"id\"),\n    title: stringField(record, \"title\"),\n    status: stringField(record, \"status\"),\n    due: stringField(record, \"due\"),\n    notes: stringField(record, \"notes\"),\n  };\n};\n\n/** Resolve a declared taskListId to the one the API addresses. `||` (not `??`)\n *  so a blank string also falls back instead of building a malformed\n *  `/lists//tasks` URL — the same rule, and the same reason, as\n *  `canonicalCalendarId`. */\nexport const canonicalTaskListId = (taskListId: string | undefined): string => taskListId?.trim() || DEFAULT_TASK_LIST_ID;\n\nconst tasksUrl = (taskListId: string | undefined, suffix = \"\"): string =>\n  `${TASKS_BASE_URL}/lists/${encodeURIComponent(canonicalTaskListId(taskListId))}/tasks${suffix}`;\n\nexport async function listTaskLists(accessToken: string): Promise<TaskListSummary[]> {\n  const listed = await googleRequest(TASKS_API_LABEL, accessToken, `${TASKS_BASE_URL}/users/@me/lists?maxResults=${MAX_TASK_LISTS}`);\n  return itemsOf(listed).map(toTaskListSummary);\n}\n\nexport async function listTasks(accessToken: string, input: ListTasksInput = {}): Promise<TaskSummary[]> {\n  const params = new URLSearchParams({\n    maxResults: String(input.maxResults ?? DEFAULT_LIST_MAX_RESULTS),\n    showCompleted: String(input.showCompleted ?? false),\n  });\n  const listed = await googleRequest(TASKS_API_LABEL, accessToken, `${tasksUrl(input.taskListId)}?${params.toString()}`);\n  return itemsOf(listed).map(toTaskSummary);\n}\n\nexport async function createTask(accessToken: string, input: CreateTaskInput): Promise<TaskSummary> {\n  const body = { title: input.title, notes: input.notes, due: input.due };\n  const created = await googleRequest(TASKS_API_LABEL, accessToken, tasksUrl(input.taskListId), { method: \"POST\", body: JSON.stringify(body) });\n  return toTaskSummary(created);\n}\n\n/** PATCH body for a task edit — only the fields the caller supplied. As with\n *  events, `undefined` means \"leave as is\" and `\"\"` means \"clear it\", so the\n *  two must stay distinct. `status` is deliberately absent: `completeTask` /\n *  `uncompleteTask` own that transition, and two ways to set it would drift\n *  apart. */\nexport const buildTaskPatch = (input: UpdateTaskInput): Record<string, unknown> => ({\n  ...(input.title !== undefined ? { title: input.title } : {}),\n  ...(input.notes !== undefined ? { notes: input.notes } : {}),\n  ...(input.due !== undefined ? { due: input.due } : {}),\n});\n\nexport async function updateTask(accessToken: string, input: UpdateTaskInput): Promise<TaskSummary> {\n  const url = tasksUrl(input.taskListId, `/${encodeURIComponent(input.taskId)}`);\n  const updated = await googleRequest(TASKS_API_LABEL, accessToken, url, { method: \"PATCH\", body: JSON.stringify(buildTaskPatch(input)) });\n  return toTaskSummary(updated);\n}\n\n/** The only two states this module transitions a task between. Spelled as a\n *  union rather than `string` so the compiler, not just the doc comment,\n *  enforces that there is no third one. */\ntype TaskStatus = typeof TASK_STATUS_COMPLETED | typeof TASK_STATUS_NEEDS_ACTION;\n\n/** Shared body of the two status transitions. PATCH keeps the rest of the task\n *  intact — a PUT would need the full body and would silently drop fields the\n *  caller never read. Private on purpose: the exported `completeTask` /\n *  `uncompleteTask` names are the API, so no caller can invent a third target\n *  state by passing an arbitrary status through. */\nasync function patchTaskStatus(accessToken: string, input: CompleteTaskInput, status: TaskStatus): Promise<TaskSummary> {\n  const url = tasksUrl(input.taskListId, `/${encodeURIComponent(input.taskId)}`);\n  const updated = await googleRequest(TASKS_API_LABEL, accessToken, url, { method: \"PATCH\", body: JSON.stringify({ status }) });\n  return toTaskSummary(updated);\n}\n\nexport async function completeTask(accessToken: string, input: CompleteTaskInput): Promise<TaskSummary> {\n  return patchTaskStatus(accessToken, input, TASK_STATUS_COMPLETED);\n}\n\n/** Send a completed task back to the to-do list.\n *\n *  Its own function rather than a flag on `completeTask`, and deliberately not\n *  a `status` field on `updateTask`: one kind per target state keeps the name\n *  honest and leaves exactly one code path setting each value.\n *\n *  The patch carries `status` alone — mirroring `completeTask`, which also\n *  sets only `status` and lets Google fill in the `completed` timestamp. Note\n *  that whether Google *clears* that timestamp on the way back is its\n *  behaviour, not ours, and is unverified here: `TaskSummary` doesn't carry\n *  `completed`, so nothing in this codebase would show a stale one. If a\n *  reopened task ever displays a completion date in Google's own UI, this is\n *  the place to add `completed: null` to the patch. */\nexport async function uncompleteTask(accessToken: string, input: CompleteTaskInput): Promise<TaskSummary> {\n  return patchTaskStatus(accessToken, input, TASK_STATUS_NEEDS_ACTION);\n}\n\nexport async function deleteTask(accessToken: string, input: CompleteTaskInput): Promise<void> {\n  const url = tasksUrl(input.taskListId, `/${encodeURIComponent(input.taskId)}`);\n  await googleRequest(TASKS_API_LABEL, accessToken, url, { method: \"DELETE\" });\n}\n","// Google Drive v3 REST calls under the `drive.file` scope — the app only\n// ever sees files IT created, never the user's wider Drive. That narrow\n// scope is why this is non-sensitive and needs no Google verification\n// review; keep every call inside it.\nimport { randomBytes } from \"node:crypto\";\nimport { asRecord, googleRequest, stringField, DEFAULT_LIST_MAX_RESULTS } from \"./apiClient.js\";\n\nconst DRIVE_FILES_URL = \"https://www.googleapis.com/drive/v3/files\";\nconst DRIVE_UPLOAD_URL = \"https://www.googleapis.com/upload/drive/v3/files\";\nconst DRIVE_API_LABEL = \"Google Drive API\";\nconst DEFAULT_MIME_TYPE = \"text/plain\";\nconst FILE_FIELDS = \"id,name,mimeType,webViewLink,modifiedTime\";\n// Reading a huge blob into a tool result would blow the context window; the\n// tool surface is for app-created text documents, not media.\nconst MAX_READ_CHARS = 100_000;\nconst TEXT_MIME_PREFIXES = [\"text/\", \"application/json\", \"application/xml\", \"application/javascript\"];\n\nexport interface DriveFileSummary {\n  id: string;\n  name: string;\n  mimeType: string;\n  webViewLink: string;\n  modifiedTime: string;\n}\n\nexport interface ListDriveFilesInput {\n  maxResults?: number | undefined;\n}\n\nexport interface CreateDriveFileInput {\n  name: string;\n  content: string;\n  mimeType?: string | undefined;\n}\n\nexport interface ReadDriveFileInput {\n  fileId: string;\n}\n\nexport const toDriveFileSummary = (value: unknown): DriveFileSummary => {\n  const record = asRecord(value);\n  return {\n    id: stringField(record, \"id\"),\n    name: stringField(record, \"name\"),\n    mimeType: stringField(record, \"mimeType\"),\n    webViewLink: stringField(record, \"webViewLink\"),\n    modifiedTime: stringField(record, \"modifiedTime\"),\n  };\n};\n\nexport const isTextMimeType = (mimeType: string): boolean => TEXT_MIME_PREFIXES.some((prefix) => mimeType.startsWith(prefix));\n\nexport async function listDriveFiles(accessToken: string, input: ListDriveFilesInput = {}): Promise<DriveFileSummary[]> {\n  const params = new URLSearchParams({\n    pageSize: String(input.maxResults ?? DEFAULT_LIST_MAX_RESULTS),\n    fields: `files(${FILE_FIELDS})`,\n    orderBy: \"modifiedTime desc\",\n  });\n  const listed = await googleRequest(DRIVE_API_LABEL, accessToken, `${DRIVE_FILES_URL}?${params.toString()}`);\n  const record = asRecord(listed);\n  const files = Array.isArray(record.files) ? record.files : [];\n  return files.map(toDriveFileSummary);\n}\n\n// Multipart upload: metadata part + media part in one request. Built by hand\n// because the `googleapis` SDK is the only alternative and this is the sole\n// multipart call in the engine.\nconst BOUNDARY_BYTES = 16;\n\n/** Fresh per request: a fixed boundary appearing inside file content would\n *  split the payload at the wrong place, so Drive would store a truncated or\n *  mangled file. Random 128 bits makes an accidental — or crafted — collision\n *  infeasible; `newMultipartBoundary` is re-derived until it is absent from\n *  the body it must delimit. */\nconst newMultipartBoundary = (): string => `mulmo-drive-${randomBytes(BOUNDARY_BYTES).toString(\"hex\")}`;\n\n// RFC 2045 token + parameters: no CR/LF (header injection into the part\n// headers) and no quotes/semicolons that would let a crafted value forge\n// extra headers or parameters.\nconst MIME_TYPE_RE = /^[A-Za-z0-9!#$&^_.+-]+\\/[A-Za-z0-9!#$&^_.+-]+$/;\n\nexport const assertSafeMimeType = (mimeType: string): string => {\n  if (!MIME_TYPE_RE.test(mimeType)) throw new Error(`Google Drive API: invalid mimeType '${mimeType}' — expected a plain type/subtype such as text/plain`);\n  return mimeType;\n};\n\nexport const buildMultipartBody = (metadata: Record<string, string>, content: string, mimeType: string, boundary: string): string =>\n  [\n    `--${boundary}`,\n    \"Content-Type: application/json; charset=UTF-8\",\n    \"\",\n    JSON.stringify(metadata),\n    `--${boundary}`,\n    `Content-Type: ${mimeType}`,\n    \"\",\n    content,\n    `--${boundary}--`,\n    \"\",\n  ].join(\"\\r\\n\");\n\n/** A boundary that appears nowhere in the parts it delimits. */\nexport const pickBoundary = (parts: string[], generate: () => string = newMultipartBoundary): string => {\n  const collides = (candidate: string): boolean => parts.some((part) => part.includes(candidate));\n  let boundary = generate();\n  while (collides(boundary)) boundary = generate();\n  return boundary;\n};\n\nexport async function createDriveFile(accessToken: string, input: CreateDriveFileInput): Promise<DriveFileSummary> {\n  const mimeType = assertSafeMimeType(input.mimeType ?? DEFAULT_MIME_TYPE);\n  const metadata = { name: input.name, mimeType };\n  const boundary = pickBoundary([input.content, JSON.stringify(metadata)]);\n  const params = new URLSearchParams({ uploadType: \"multipart\", fields: FILE_FIELDS });\n  const created = await googleRequest(DRIVE_API_LABEL, accessToken, `${DRIVE_UPLOAD_URL}?${params.toString()}`, {\n    method: \"POST\",\n    contentType: `multipart/related; boundary=${boundary}`,\n    body: buildMultipartBody(metadata, input.content, mimeType, boundary),\n  });\n  return toDriveFileSummary(created);\n}\n\nexport async function readDriveFile(accessToken: string, input: ReadDriveFileInput): Promise<{ file: DriveFileSummary; content: string }> {\n  const fileId = encodeURIComponent(input.fileId);\n  const metadata = await googleRequest(DRIVE_API_LABEL, accessToken, `${DRIVE_FILES_URL}/${fileId}?fields=${FILE_FIELDS}`);\n  const file = toDriveFileSummary(metadata);\n  if (!isTextMimeType(file.mimeType)) {\n    throw new Error(`Google Drive API: '${file.name}' is ${file.mimeType || \"a binary file\"} — only text files can be read as content`);\n  }\n  const raw = await googleRequest(DRIVE_API_LABEL, accessToken, `${DRIVE_FILES_URL}/${fileId}?alt=media`, { expectText: true });\n  const content = typeof raw === \"string\" ? raw.slice(0, MAX_READ_CHARS) : \"\";\n  return { file, content };\n}\n\nexport async function deleteDriveFile(accessToken: string, input: ReadDriveFileInput): Promise<void> {\n  await googleRequest(DRIVE_API_LABEL, accessToken, `${DRIVE_FILES_URL}/${encodeURIComponent(input.fileId)}`, { method: \"DELETE\" });\n}\n"],"mappings":"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"}