// SPEC: PJ2026-01040111 long-running Workbench observation.
// Responsibility: Runner Workbench session, provider, and Project/MDTODO command source fragment.

export function nodeWebObserveRunnerWorkbenchSource(): string {
  return String.raw`async function selectProvider(provider) {
  const target = String(provider || "").trim();
  if (!target) throw new Error("selectProvider requires provider name");
  const beforeUrl = currentPageUrl();
  const beforePath = safeUrlPath(beforeUrl);
  if (!String(beforePath || "").startsWith("/workbench")) throw new Error("selectProvider requires a Workbench page; currentPath=" + (beforePath || "-") + "; run observe command --type goto --path /workbench or --type newSession first");
  const nativeSelect = await page.evaluate((name) => {
    const normalized = String(name).toLowerCase();
    const visible = (element) => {
      const rect = element.getBoundingClientRect();
      const style = window.getComputedStyle(element);
      return rect.width > 0 && rect.height > 0 && style.visibility !== "hidden" && style.display !== "none";
    };
    for (const select of Array.from(document.querySelectorAll("select")).filter(visible)) {
      const options = Array.from(select.options || []);
      const option = options.find((item) => String(item.value || "").toLowerCase().includes(normalized) || String(item.textContent || "").toLowerCase().includes(normalized));
      if (!option) continue;
      select.value = option.value;
      select.dispatchEvent(new Event("input", { bubbles: true }));
      select.dispatchEvent(new Event("change", { bubbles: true }));
      return { kind: "native-select", value: option.value, text: option.textContent || "" };
    }
    return null;
  }, target).catch(() => null);
  if (nativeSelect) {
    await page.waitForTimeout(500);
    return { beforeUrl, afterUrl: currentPageUrl(), provider: target, selected: nativeSelect, pageId };
  }
  const optionVisible = page.getByText(target, { exact: false }).last();
  if (await optionVisible.isVisible().catch(() => false)) {
    await optionVisible.click();
    await page.waitForTimeout(500);
    return { beforeUrl, afterUrl: currentPageUrl(), provider: target, selected: { kind: "visible-text" }, pageId };
  }
  const control = page.locator([
    '[data-testid*="provider" i]',
    '[data-testid*="model" i]',
    '[aria-label*="provider" i]',
    '[aria-label*="model" i]',
    '[aria-label*="模型"]',
    '[aria-label*="提供"]',
    '[role="combobox"]',
    '[aria-haspopup="listbox"]',
  ].join(", "));
  const count = Math.min(await control.count().catch(() => 0), 60);
  const attempts = [];
  for (let index = count - 1; index >= 0; index -= 1) {
    const item = control.nth(index);
    const visible = await item.isVisible().catch(() => false);
    if (!visible) continue;
    const label = await item.evaluate((element) => String(element.getAttribute("aria-label") || element.getAttribute("data-testid") || element.textContent || "").slice(0, 200)).catch(() => "");
    if (isProviderNavigationLabel(label)) continue;
    if (!/provider|profile|model|模型|提供|codex|openai|moon|api/iu.test(label)) continue;
    attempts.push({ index, label });
    await item.click({ timeout: 3000 }).catch(() => null);
    await page.waitForTimeout(300);
    const option = page.getByText(target, { exact: false }).last();
    if (await option.isVisible().catch(() => false)) {
      await option.click();
      await page.waitForTimeout(700);
      return { beforeUrl, afterUrl: currentPageUrl(), provider: target, selected: { kind: "opened-control", controlIndex: index, label }, attempts, pageId };
    }
    await page.keyboard.press("Escape").catch(() => null);
  }
  const providerCandidates = await collectProviderCandidates();
  throw new Error("provider option not found: " + target + "; providerCandidates=" + JSON.stringify(providerCandidates.slice(0, 50)) + "; attempts=" + JSON.stringify(attempts.slice(-10)));
}

async function collectProviderCandidates() {
  return page.evaluate(() => {
    const rows = [];
    const seen = new Set();
    const isNavigationLabel = (value) => {
      const label = String(value || "").replace(/\s+/gu, " ").trim().toLowerCase();
      if (!label) return false;
      if (label === "api keys" || label === "kapi keys" || label === "profiles" || label === "rprofiles") return true;
      return /^(?:api keys|profiles)$/iu.test(label.replace(/^[a-z]\s*/iu, ""));
    };
    const visible = (element) => {
      const rect = element.getBoundingClientRect();
      const style = window.getComputedStyle(element);
      return rect.width > 0 && rect.height > 0 && style.visibility !== "hidden" && style.display !== "none";
    };
    const push = (kind, element, value, text) => {
      const normalizedValue = String(value || "").trim().slice(0, 160);
      const normalizedText = String(text || "").replace(/\s+/gu, " ").trim().slice(0, 220);
      const label = (normalizedValue + " " + normalizedText).trim();
      if (!label) return;
      const key = (kind + ":" + label).toLowerCase();
      if (seen.has(key)) return;
      seen.add(key);
      rows.push({ kind, value: normalizedValue, text: normalizedText, testId: String(element?.getAttribute?.("data-testid") || "").slice(0, 120), ariaLabel: String(element?.getAttribute?.("aria-label") || "").slice(0, 160) });
    };
    for (const select of Array.from(document.querySelectorAll("select")).filter(visible)) {
      for (const option of Array.from(select.options || [])) push("select-option", select, option.value, option.textContent || "");
    }
    const selector = [
      '[role="option"]',
      '[role="menuitem"]',
      '[data-radix-collection-item]',
      '[data-testid*="provider" i]',
      '[data-testid*="profile" i]',
      '[data-testid*="model" i]',
      '[aria-label*="provider" i]',
      '[aria-label*="profile" i]',
      '[aria-label*="model" i]',
      '[aria-label*="模型"]',
      '[aria-label*="提供"]',
      'button'
    ].join(", ");
    for (const element of Array.from(document.querySelectorAll(selector)).filter(visible)) {
      const text = String(element.textContent || "").replace(/\s+/gu, " ").trim();
      const ariaLabel = String(element.getAttribute("aria-label") || "");
      const testId = String(element.getAttribute("data-testid") || "");
      const haystack = text + " " + ariaLabel + " " + testId;
      if (isNavigationLabel(haystack)) continue;
      if (!/provider|profile|model|模型|提供|codex|openai|deepseek|gpt|api|flash|spark|claude|gemini|moon/iu.test(haystack)) continue;
      push("visible-control", element, element.getAttribute("value") || "", text || ariaLabel || testId);
    }
    return rows.slice(0, 80);
  }).catch((error) => [{ kind: "candidate-scan-error", value: "", text: String(error?.message || error).slice(0, 240), testId: "", ariaLabel: "" }]);
}

function isProviderNavigationLabel(value) {
  const text = String(value || "").replace(/\s+/gu, " ").trim().toLowerCase();
  if (!text) return false;
  if (text === "api keys" || text === "kapi keys" || text === "profiles" || text === "rprofiles") return true;
  return /^(?:api keys|profiles)$/iu.test(text.replace(/^[a-z]\s*/iu, ""));
}

async function clickSession(sessionId) {
  if (!sessionId) throw new Error("clickSession requires --session-id or --value");
  const beforeUrl = currentPageUrl();
  const escaped = cssEscape(sessionId);
  const cssCandidate = page.locator("[data-session-id=\"" + escaped + "\"], [href*=\"" + escaped + "\"]").first();
  const candidate = await visibleLocator(cssCandidate) ? cssCandidate : page.getByText(sessionId, { exact: true }).first();
  await candidate.waitFor({ state: "visible", timeout: 15000 });
  await candidate.click();
  await page.waitForTimeout(1000);
  return { beforeUrl, afterUrl: currentPageUrl(), sessionId, pageId };
}

async function refreshCurrentSession(command) {
  const beforeUrl = currentPageUrl();
  const before = await workbenchSessionSnapshot();
  const sessionId = String(command.sessionId || command.canarySessionId || before?.routeSessionId || before?.activeSessionId || "").trim();
  if (!sessionId) throw new Error("refreshCurrentSession requires a current Workbench session");
  const navigation = await gotoTarget("/workbench/sessions/" + encodeURIComponent(sessionId));
  const after = await workbenchSessionSnapshot();
  return {
    beforeUrl,
    afterUrl: currentPageUrl(),
    type: "refreshCurrentSession",
    afterRound: integerOrNull(command.afterRound),
    canarySessionId: sessionId,
    routeSessionId: after?.routeSessionId ?? null,
    activeSessionId: after?.activeSessionId ?? null,
    routeOk: after?.routeSessionId === sessionId,
    activeOk: after?.activeSessionId === sessionId,
    composerReady: after?.composerReady === true,
    navigation,
    pageId,
    valuesRedacted: true,
  };
}

async function switchAwayAndBack(command) {
  const beforeUrl = currentPageUrl();
  const before = await workbenchSessionSnapshot();
  const canarySessionId = String(command.sessionId || command.canarySessionId || before?.routeSessionId || before?.activeSessionId || "").trim();
  if (!canarySessionId) throw new Error("switchAwayAndBack requires a current canary Workbench session");
  const strategy = String(command.alternateSessionStrategy || command.strategy || "existing-or-create");
  const beforeSessionIds = await visibleWorkbenchSessionIds();
  let alternateSessionId = beforeSessionIds.find((item) => item && item !== canarySessionId) || null;
  let alternateSource = alternateSessionId === null ? null : "existing";
  let createResult = null;
  if (alternateSessionId === null && strategy === "existing-or-create") {
    createResult = await createSessionFromUi();
    alternateSessionId = createResult?.sessionId || createResult?.createdSessionId || null;
    alternateSource = "created";
  }
  if (!alternateSessionId) throw new Error("switchAwayAndBack could not find an alternate session with strategy=" + strategy);
  const switchAway = await clickSession(alternateSessionId);
  const awaySettle = await waitForWorkbenchSessionHydrated(page, alternateSessionId, { timeoutMs: 15000 });
  const away = awaySettle.snapshot ?? await workbenchSessionSnapshot();
  let switchBack;
  try {
    switchBack = await clickSession(canarySessionId);
  } catch (error) {
    const fallbackNavigation = await gotoTarget("/workbench/sessions/" + encodeURIComponent(canarySessionId));
    switchBack = { ok: fallbackNavigation?.readiness?.ok === true, fallback: "gotoTarget", clickError: errorSummary(error), navigation: fallbackNavigation, valuesRedacted: true };
  }
  let backSettle = await waitForWorkbenchSessionHydrated(page, canarySessionId, { timeoutMs: 15000 });
  let after = backSettle.snapshot ?? await workbenchSessionSnapshot();
  let switchBackRecovery = null;
  let routeOk = after?.routeSessionId === canarySessionId;
  let activeOk = after?.activeSessionId === canarySessionId;
  if (!routeOk || !activeOk) {
    switchBackRecovery = await recoverControlPageSessionHydration(canarySessionId, backSettle);
    if (switchBackRecovery?.ok === true) {
      switchBack = { ...switchBack, recoveryApplied: true, recoveryNavigation: switchBackRecovery.navigation, valuesRedacted: true };
      backSettle = switchBackRecovery.settle;
      after = switchBackRecovery.snapshot ?? after;
      routeOk = after?.routeSessionId === canarySessionId;
      activeOk = after?.activeSessionId === canarySessionId;
    }
  }
  if (awaySettle.ok !== true) {
    const error = new Error("switchAwayAndBack did not settle on the alternate session");
    error.details = { canarySessionId, alternateSessionId, routeSessionId: away?.routeSessionId ?? null, activeSessionId: away?.activeSessionId ?? null, settle: awaySettle, pageId, valuesRedacted: true };
    throw error;
  }
  if (!routeOk || !activeOk) {
    const error = new Error("switchAwayAndBack did not return to the canary session");
    error.details = { canarySessionId, alternateSessionId, routeSessionId: after?.routeSessionId ?? null, activeSessionId: after?.activeSessionId ?? null, settle: backSettle, recovery: switchBackRecovery, pageId, valuesRedacted: true };
    throw error;
  }
  return {
    beforeUrl,
    afterUrl: currentPageUrl(),
    type: "switchAwayAndBack",
    afterRound: integerOrNull(command.afterRound),
    canarySessionId,
    alternateSessionId,
    alternateSessionStrategy: strategy,
    alternateSource,
    beforeSessionCount: beforeSessionIds.length,
    createResult: createResult === null ? null : { sessionId: alternateSessionId, valuesRedacted: true },
    switchAway,
    awaySettle,
    away: {
      routeSessionId: away?.routeSessionId ?? null,
      activeSessionId: away?.activeSessionId ?? null,
      messageCount: away?.messageCount ?? null,
      valuesRedacted: true,
    },
    switchBack,
    backSettle,
    switchBackRecovery,
    routeSessionId: after?.routeSessionId ?? null,
    activeSessionId: after?.activeSessionId ?? null,
    routeOk,
    activeOk,
    composerReady: after?.composerReady === true,
    pageId,
    valuesRedacted: true,
  };
}

async function recoverControlPageSessionHydration(sessionId, previousSettle) {
  const observerHydration = observerPage && !observerPage.isClosed()
    ? await waitForWorkbenchSessionHydrated(observerPage, sessionId, { timeoutMs: 5000 })
    : { ok: false, reason: "observer-page-unavailable", valuesRedacted: true };
  await recreateControlPageForNavigation(observerHydration.ok === true ? "switch-back-hydration-retry" : "switch-back-control-retry-without-observer", 1);
  const navigation = await gotoTarget("/workbench/sessions/" + encodeURIComponent(sessionId));
  const settle = await waitForWorkbenchSessionHydrated(page, sessionId, { timeoutMs: 15000 });
  const snapshot = settle.snapshot ?? await workbenchSessionSnapshot();
  const routeOk = snapshot?.routeSessionId === sessionId;
  const activeOk = snapshot?.activeSessionId === sessionId;
  return {
    ok: settle.ok === true && routeOk && activeOk,
    attempted: true,
    reason: settle.ok === true ? "control-page-recreated" : settle.reason || "control-session-hydration-retry-failed",
    previousSettle,
    observerHydrated: observerHydration.ok === true,
    observerHydration,
    navigation,
    settle,
    snapshot: {
      routeSessionId: snapshot?.routeSessionId ?? null,
      activeSessionId: snapshot?.activeSessionId ?? null,
      composerReady: snapshot?.composerReady === true,
      messageCount: snapshot?.messageCount ?? null,
      valuesRedacted: true,
    },
    pageId,
    valuesRedacted: true,
  };
}

async function assertSessionInvariant(command) {
  const beforeUrl = currentPageUrl();
  const snapshot = await workbenchSessionSnapshot();
  const canarySessionId = String(command.sessionId || command.canarySessionId || snapshot?.routeSessionId || snapshot?.activeSessionId || "").trim();
  if (!canarySessionId) throw new Error("assertSessionInvariant requires a current canary Workbench session");
  const routeOk = snapshot?.routeSessionId === canarySessionId;
  const activeOk = snapshot?.activeSessionId === canarySessionId;
  const composerReady = snapshot?.composerReady === true;
  if (!routeOk || !activeOk) {
    const error = new Error("assertSessionInvariant saw route/active session mismatch");
    error.details = { canarySessionId, routeSessionId: snapshot?.routeSessionId ?? null, activeSessionId: snapshot?.activeSessionId ?? null, pageId, valuesRedacted: true };
    throw error;
  }
  if (command.requireComposerReady === true && !composerReady) {
    const error = new Error("assertSessionInvariant requires composer ready for the next round");
    error.details = { canarySessionId, routeSessionId: snapshot?.routeSessionId ?? null, activeSessionId: snapshot?.activeSessionId ?? null, warning: snapshot?.warning ?? null, pageId, valuesRedacted: true };
    throw error;
  }
  const messageOrder = await visibleMessageOrderSummary();
  await samplePage("assert-session-invariant", { refreshObserver: false, screenshot: false }).catch((error) => appendJsonl(files.errors, eventRecord("assert-session-invariant-sample-error", { error: errorSummary(error), pageId, valuesRedacted: true })));
  return {
    beforeUrl,
    afterUrl: currentPageUrl(),
    type: "assertSessionInvariant",
    afterRound: integerOrNull(command.afterRound),
    canarySessionId,
    routeSessionId: snapshot?.routeSessionId ?? null,
    activeSessionId: snapshot?.activeSessionId ?? null,
    routeOk,
    activeOk,
    composerReady,
    expectedSentinelRange: command.expectedSentinelRange || null,
    findingId: command.findingId || "workbench-message-order-user-clustered-after-navigation",
    severity: command.severity || "amber",
    blocking: command.blocking === true,
    messageOrder,
    sampleSeq,
    pageRole: "control",
    pageId,
    valuesRedacted: true,
  };
}

async function visibleWorkbenchSessionIds() {
  return page.evaluate(() => {
    const visible = (element) => {
      if (!element) return false;
      const rect = element.getBoundingClientRect();
      const style = window.getComputedStyle(element);
      return rect.width > 0 && rect.height > 0 && style.visibility !== "hidden" && style.display !== "none";
    };
    const sessionIdForElement = (element) => {
      const direct = element.getAttribute("data-session-id");
      if (direct) return direct;
      const href = element.getAttribute("href") || element.closest("a[href]")?.getAttribute("href") || "";
      const match = String(href || "").match(/\/(?:workbench|workspace)\/sessions\/([^/?#]+)/u);
      return match ? decodeURIComponent(match[1] || "") : null;
    };
    const ids = [];
    for (const element of Array.from(document.querySelectorAll(".session-tab[data-session-id], [role='tab'][data-session-id], [data-testid*='session' i][data-session-id], a[href*='/workbench/sessions/'], a[href*='/workspace/sessions/']"))) {
      if (!visible(element)) continue;
      const id = sessionIdForElement(element);
      if (id && !ids.includes(id)) ids.push(id);
    }
    return ids.slice(0, 50);
  }).catch(() => []);
}

async function visibleMessageOrderSummary() {
  const rawMessages = await page.evaluate(() => {
    const trim = (value, limit = 1600) => String(value || "").replace(/\s+/gu, " ").trim().slice(0, limit);
    const visible = (element) => {
      if (!element) return false;
      const rect = element.getBoundingClientRect();
      const style = window.getComputedStyle(element);
      return rect.width > 0 && rect.height > 0 && style.visibility !== "hidden" && style.display !== "none";
    };
    const stableMessageText = (element) => {
      const selectors = [".message-markdown.message-text", ".message-text", "[data-message-body]", "[data-testid='message-body']", "[data-testid*='message-text' i]", "[data-testid*='final-response' i]"];
      const parts = [];
      for (const selector of selectors) {
        for (const candidate of Array.from(element.querySelectorAll(selector))) {
          if (!visible(candidate)) continue;
          const text = trim(candidate.textContent || "", 1200);
          if (text && !parts.includes(text)) parts.push(text);
        }
      }
      return parts.length > 0 ? parts.join(" ") : trim(element.textContent || "", 1200);
    };
    return Array.from(document.querySelectorAll('article.message-card, .message-card[data-message-id], article[data-message-id]')).filter(visible).slice(-80).map((element, index) => ({
      index,
      dataRole: element.getAttribute("data-role") || null,
      role: element.getAttribute("role") || null,
      testId: element.getAttribute("data-testid") || null,
      status: element.getAttribute("data-status") || element.getAttribute("aria-busy") || null,
      sessionId: element.getAttribute("data-session-id") || null,
      messageId: element.getAttribute("data-message-id") || element.getAttribute("id") || null,
      traceId: element.getAttribute("data-trace-id") || null,
      turnId: element.getAttribute("data-turn-id") || null,
      text: stableMessageText(element),
    }));
  }).catch(() => []);
  const entries = Array.isArray(rawMessages) ? rawMessages.map((item, index) => {
    const text = String(item?.text || "");
    const kind = messageKindForOrder(item, text);
    const markers = sentinelMarkers(text);
    return {
      index: Number.isFinite(Number(item?.index)) ? Number(item.index) : index,
      kind,
      role: item?.dataRole || item?.role || null,
      status: item?.status || null,
      sessionId: item?.sessionId || null,
      messageId: item?.messageId || null,
      traceId: item?.traceId || null,
      turnId: item?.turnId || null,
      markers,
      textHash: sha256Text(text),
      textBytes: Buffer.byteLength(text),
      valuesRedacted: true,
    };
  }) : [];
  const clusters = userClusters(entries);
  const maxCluster = clusters.slice().sort((a, b) => b.consecutiveUserMessageCount - a.consecutiveUserMessageCount)[0] || null;
  return {
    messageCount: entries.length,
    sequence: entries.map((item) => ({
      index: item.index,
      kind: item.kind,
      marker: item.markers[0] || null,
      markerCount: item.markers.length,
      traceId: item.traceId,
      status: item.status,
      textHash: item.textHash,
      textBytes: item.textBytes,
      valuesRedacted: true,
    })).slice(-40),
    userClustered: clusters.length > 0,
    consecutiveUserMessageCount: maxCluster?.consecutiveUserMessageCount ?? 0,
    sentinelRange: maxCluster?.sentinelRange ?? null,
    traceIds: uniqueStrings(entries.map((item) => item.traceId)).slice(0, 12),
    clusters,
    valuesRedacted: true,
  };
}

function messageKindForOrder(item, text) {
  const signal = [item?.dataRole, item?.role, item?.testId].map((value) => String(value || "").toLowerCase()).join(" ");
  if (/\b(user|human)\b/u.test(signal)) return "user";
  if (/\b(assistant|agent|code-agent|system)\b/u.test(signal)) return "agent";
  const body = String(text || "").trim();
  if (/^Run\s+/iu.test(body) && /\bsentinel-(?:0[1-9]|10)\b/u.test(body)) return "user";
  if (/\b(?:final response|assistant|code agent)\b/iu.test(body)) return "agent";
  return "unknown";
}

function sentinelMarkers(text) {
  return uniqueStrings(Array.from(String(text || "").matchAll(/\bsentinel-(?:0[1-9]|10)\b/giu)).map((match) => match[0].toLowerCase()));
}

function userClusters(entries) {
  const clusters = [];
  let current = [];
  const flush = () => {
    if (current.length >= 2) {
      const markers = current.flatMap((item) => item.markers);
      clusters.push({
        startIndex: current[0].index,
        endIndex: current[current.length - 1].index,
        consecutiveUserMessageCount: current.length,
        sentinelRange: sentinelRange(markers),
        markers: uniqueStrings(markers).slice(0, 12),
        messageTextHashes: current.map((item) => item.textHash).slice(0, 12),
        traceIds: uniqueStrings(current.map((item) => item.traceId)).slice(0, 12),
        valuesRedacted: true,
      });
    }
    current = [];
  };
  for (const entry of entries) {
    if (entry.kind === "user") {
      current.push(entry);
    } else {
      flush();
    }
  }
  flush();
  return clusters.slice(0, 20);
}

function sentinelRange(markers) {
  const unique = uniqueStrings(markers).sort((a, b) => sentinelMarkerNumber(a) - sentinelMarkerNumber(b));
  if (unique.length === 0) return null;
  return unique.length === 1 ? unique[0] : unique[0] + ".." + unique[unique.length - 1];
}

function sentinelMarkerNumber(marker) {
  const match = String(marker || "").match(/sentinel-(\d+)/u);
  return match ? Number(match[1]) : Number.MAX_SAFE_INTEGER;
}

function uniqueStrings(values) {
  return Array.from(new Set((values || []).map((value) => String(value || "").trim()).filter(Boolean)));
}

function integerOrNull(value) {
  const parsed = Number(value);
  return Number.isInteger(parsed) ? parsed : null;
}

function ensureProjectManagementCommand(type) {
  if (projectManagement.enabled !== true) throw new Error(type + " requires config/hwlab-node-lanes.yaml webProbe.projectManagement.enabled=true for the selected node/lane");
  if (!projectManagement.commandAllowlist.includes(type)) throw new Error(type + " is not in webProbe.projectManagement.commandAllowlist for the selected node/lane");
}

async function gotoProjectMdtodo() {
  ensureProjectManagementCommand("gotoProjectMdtodo");
  return gotoTarget("/projects/mdtodo");
}

function commandValue(command, keys) {
  for (const key of keys) {
    const value = command?.[key];
    if (typeof value === "string" && value.trim()) return value.trim();
  }
  return "";
}

async function visibleLocator(locator) {
  return await locator.count().catch(() => 0) > 0 && await locator.first().isVisible().catch(() => false);
}

function normalizedProjectText(value) {
  return String(value || "").replace(/\s+/gu, " ").trim().toLowerCase();
}

function projectSnapshotMatchesCommandSelection(type, raw, selected, targetValue) {
  if (!raw || typeof raw !== "object") return false;
  const selectedValue = String(selected?.selectedValue || "");
  const normalizedTarget = normalizedProjectText(targetValue);
  if (type === "selectMdtodoFile") {
    const selectedFileRef = String(raw.selectedFileRefRaw || "");
    const selectedFileLabel = normalizedProjectText(raw.selectedFileLabel);
    return Boolean(
      (selectedValue && selectedFileRef === selectedValue)
      || (normalizedTarget && selectedFileLabel.includes(normalizedTarget))
    ) && Number(raw.fileCount || 0) > 0;
  }
  if (type === "selectMdtodoSource" || type === "selectProjectSource") {
    const selectedSourceId = String(raw.selectedSourceIdRaw || "");
    return Boolean(selectedValue && selectedSourceId === selectedValue) && Number(raw.sourceCount || 0) > 0;
  }
  return true;
}

async function waitForProjectCommandSelection({ type, selected, targetValue, timeoutMs = 30000 }) {
  const started = Date.now();
  let lastProject = null;
  do {
    await page.waitForTimeout(300);
    lastProject = await projectManagementCommandSnapshot({ includeRaw: true });
    if (projectSnapshotMatchesCommandSelection(type, lastProject, selected, targetValue)) return lastProject;
  } while (Date.now() - started < timeoutMs);
  return lastProject || await projectManagementCommandSnapshot({ includeRaw: true });
}

async function selectHtmlOptionByValueOrLabel(locator, value) {
  const select = locator.first();
  const targetValue = typeof value === "string" && value.trim() ? value.trim() : "";
  if (targetValue) {
    const byValue = await select.selectOption({ value: targetValue }).then((selected) => ({ ok: true, selected })).catch(() => ({ ok: false, selected: [] }));
    if (byValue.ok && byValue.selected.length > 0) return { ok: true, mode: "select-value", selectedValue: byValue.selected[0] || targetValue };
    const byLabel = await select.selectOption({ label: targetValue }).then((selected) => ({ ok: true, selected })).catch(() => ({ ok: false, selected: [] }));
    if (byLabel.ok && byLabel.selected.length > 0) return { ok: true, mode: "select-label", selectedValue: byLabel.selected[0] || targetValue };
    return await select.evaluate((element, target) => {
      const normalize = (raw) => String(raw || "").replace(/\s+/gu, " ").trim().toLowerCase();
      const normalizedTarget = normalize(target);
      const options = Array.from(element.options || []).filter((option) => !option.disabled && option.value).map((option) => ({
        value: option.value,
        label: String(option.textContent || "").replace(/\s+/gu, " ").trim(),
      }));
      const matchers = [
        { mode: "select-value-normalized", test: (option) => normalize(option.value) === normalizedTarget },
        { mode: "select-label-normalized", test: (option) => normalize(option.label) === normalizedTarget },
        { mode: "select-label-contains", test: (option) => normalize(option.label).includes(normalizedTarget) },
        { mode: "select-value-contains", test: (option) => normalize(option.value).includes(normalizedTarget) },
      ];
      for (const matcher of matchers) {
        const chosen = options.find(matcher.test);
        if (!chosen) continue;
        element.value = chosen.value;
        element.dispatchEvent(new Event("input", { bubbles: true }));
        element.dispatchEvent(new Event("change", { bubbles: true }));
        return {
          ok: true,
          mode: matcher.mode,
          selectedValue: chosen.value,
          selectedLabel: chosen.label,
          optionCount: options.length,
          optionLabelSamples: options.map((option) => option.label).filter(Boolean).slice(0, 8),
          valuesRedacted: true,
        };
      }
      return {
        ok: false,
        mode: "select-target-not-found",
        selectedValue: element.value || "",
        optionCount: options.length,
        optionLabelSamples: options.map((option) => option.label).filter(Boolean).slice(0, 8),
        valuesRedacted: true,
      };
    }, targetValue);
  }
  const selectedValue = await select.evaluate((element) => {
    const options = Array.from(element.options || []).filter((option) => !option.disabled && option.value);
    const chosen = options[0] || null;
    if (!chosen) return "";
    element.value = chosen.value;
    element.dispatchEvent(new Event("input", { bubbles: true }));
    element.dispatchEvent(new Event("change", { bubbles: true }));
    return chosen.value;
  });
  return { ok: true, mode: "select-first", selectedValue };
}

async function clickProjectItemByAttr({ type, attr, value, fallbackSelector, selectTestId }) {
  ensureProjectManagementCommand(type);
  const beforeUrl = currentPageUrl();
  const beforeProject = await projectManagementCommandSnapshot();
  const targetValue = typeof value === "string" && value.trim() ? value.trim() : null;
  if (selectTestId) {
    const select = page.locator('[data-testid="' + cssEscape(selectTestId) + '"]');
    if (await visibleLocator(select)) {
      const selected = await selectHtmlOptionByValueOrLabel(select, targetValue || "");
      const afterProjectRaw = await waitForProjectCommandSelection({ type, selected, targetValue: targetValue || "" });
      const selectionMatched = projectSnapshotMatchesCommandSelection(type, afterProjectRaw, selected, targetValue || "");
      const afterProject = sanitizeProjectCommandSnapshot(afterProjectRaw);
      if (targetValue && (selected.ok !== true || !selectionMatched)) {
        const error = new Error(type + " did not select requested target");
        error.details = {
          beforeUrl,
          afterUrl: currentPageUrl(),
          type,
          attr,
          selectTestId,
          mode: selected.mode,
          targetHash: sha256Text(targetValue),
          targetPreview: truncate(targetValue, 120),
          selected,
          beforeProject,
          afterProject,
          pageId,
          valuesRedacted: true
        };
        throw error;
      }
      return {
        beforeUrl,
        afterUrl: currentPageUrl(),
        type,
        attr,
        mode: selected.mode,
        selected: opaqueIdSummary(selected.selectedValue || targetValue),
        beforeProject,
        afterProject,
        pageId,
        valuesRedacted: true
      };
    }
  }
  const selector = targetValue ? "[" + attr + "=\"" + cssEscape(targetValue) + "\"]" : fallbackSelector;
  const locator = page.locator(selector).first();
  await locator.waitFor({ state: "visible", timeout: 15000 });
  const clickedValue = await locator.evaluate((element, name) => element.getAttribute(name), attr).catch(() => targetValue);
  await locator.click();
  await page.waitForTimeout(700);
  const afterProject = await projectManagementCommandSnapshot();
  return {
    beforeUrl,
    afterUrl: currentPageUrl(),
    type,
    attr,
    selected: opaqueIdSummary(clickedValue || targetValue),
    beforeProject,
    afterProject,
    pageId,
    valuesRedacted: true
  };
}

async function selectProjectSource(command) {
  return clickProjectItemByAttr({
    type: "selectProjectSource",
    attr: "data-source-id",
    value: command.sourceId || command.value || command.text || "",
    fallbackSelector: '[data-testid="mdtodo-source-list"] [data-source-id], [data-source-id]',
    selectTestId: "mdtodo-source-select"
  });
}

async function selectMdtodoSource(command) {
  return clickProjectItemByAttr({
    type: "selectMdtodoSource",
    attr: "data-source-id",
    value: command.sourceId || command.value || command.text || "",
    fallbackSelector: '[data-testid="mdtodo-source-list"] [data-source-id], [data-source-id]',
    selectTestId: "mdtodo-source-select"
  });
}

async function selectMdtodoFile(command) {
  return clickProjectItemByAttr({
    type: "selectMdtodoFile",
    attr: "data-file-ref",
    value: command.fileRef || command.filename || command.value || command.text || "",
    fallbackSelector: '[data-testid="mdtodo-file-list"] [data-file-ref], [data-file-ref]',
    selectTestId: "mdtodo-file-select"
  });
}

async function mdtodoTaskLocator(command, options = {}) {
  const taskRef = commandValue(command, ["taskRef"]);
  const taskId = commandValue(command, ["taskId", "task", "value", "text"]);
  const selectors = [];
  if (taskRef) selectors.push('[data-task-ref="' + cssEscape(taskRef) + '"]');
  if (taskId) {
    selectors.push('[data-task-id="' + cssEscape(taskId) + '"]');
    selectors.push('[data-rxx-id="' + cssEscape(taskId) + '"]');
  }
  for (const selector of selectors) {
    const locator = page.locator(selector).first();
    if (await visibleLocator(locator)) return { locator, taskRef, taskId, selector };
  }
  if (taskId) {
    const textLocator = page.locator('[data-testid="mdtodo-task-tree"] [data-task-ref], [data-task-ref]').filter({ hasText: taskId }).first();
    if (await visibleLocator(textLocator)) return { locator: textLocator, taskRef, taskId, selector: "text:" + taskId };
  }
  if ((taskRef || taskId) && options.allowFallback !== true) {
    return { locator: null, taskRef, taskId, selector: "target-task-not-visible", targetMissing: true };
  }
  const fallback = page.locator('[data-testid="mdtodo-task-tree"] [data-task-ref], [data-task-ref]').first();
  return { locator: fallback, taskRef, taskId, selector: "first-visible-task" };
}

async function waitForMdtodoTaskLocator(command, timeoutMs = 30000) {
  const started = Date.now();
  let lastProject = null;
  let lastTarget = null;
  do {
    lastTarget = await mdtodoTaskLocator(command, { allowFallback: false });
    if (lastTarget.locator && await visibleLocator(lastTarget.locator)) return { target: lastTarget, project: lastProject };
    lastProject = await projectManagementCommandSnapshot();
    await page.waitForTimeout(500);
  } while (Date.now() - started < timeoutMs);
  return { target: lastTarget || await mdtodoTaskLocator(command, { allowFallback: false }), project: lastProject || await projectManagementCommandSnapshot() };
}

async function selectMdtodoTask(command) {
  ensureProjectManagementCommand("selectMdtodoTask");
  const beforeUrl = currentPageUrl();
  const beforeProject = await projectManagementCommandSnapshot();
  const hasExplicitTarget = Boolean(commandValue(command, ["taskRef"]) || commandValue(command, ["taskId", "task", "value", "text"]));
  const waited = hasExplicitTarget ? await waitForMdtodoTaskLocator(command) : { target: await mdtodoTaskLocator(command, { allowFallback: true }), project: null };
  const target = waited.target;
  if (!target.locator) {
    const error = new Error("selectMdtodoTask did not find requested task");
    error.details = {
      beforeUrl,
      afterUrl: currentPageUrl(),
      selector: target.selector,
      requestedTaskRef: target.taskRef ? opaqueIdSummary(target.taskRef) : null,
      requestedTaskId: target.taskId || null,
      beforeProject,
      afterProject: waited.project,
      pageId,
      valuesRedacted: true
    };
    throw error;
  }
  await target.locator.waitFor({ state: "visible", timeout: 15000 });
  const clicked = await target.locator.evaluate((element) => ({
    taskRef: element.getAttribute("data-task-ref") || null,
    taskId: element.getAttribute("data-task-id") || element.getAttribute("data-rxx-id") || null,
    status: element.getAttribute("data-task-status") || element.querySelector("[data-state]")?.getAttribute("data-state") || null,
    selected: element.getAttribute("data-selected") === "true" || element.getAttribute("aria-selected") === "true"
  })).catch(() => ({ taskRef: target.taskRef || null, taskId: target.taskId || null, status: null }));
  if (!clicked.selected) {
    await target.locator.scrollIntoViewIfNeeded().catch(() => null);
    await target.locator.click();
    await page.waitForTimeout(700);
  }
  const afterProject = await projectManagementCommandSnapshot();
  return {
    beforeUrl,
    afterUrl: currentPageUrl(),
    type: "selectMdtodoTask",
    selector: target.selector,
    selectedTask: opaqueIdSummary(clicked.taskRef || target.taskRef),
    selectedTaskId: clicked.taskId || target.taskId || null,
    selectedTaskStatus: clicked.status || null,
    alreadySelected: clicked.selected === true,
    beforeProject,
    afterProject,
    pageId,
    valuesRedacted: true
  };
}

async function expandMdtodoTask(command) {
  ensureProjectManagementCommand("expandMdtodoTask");
  const beforeUrl = currentPageUrl();
  const beforeProject = await projectManagementCommandSnapshot();
  const target = await mdtodoTaskLocator(command);
  await target.locator.waitFor({ state: "visible", timeout: 15000 });
  const toggle = target.locator.locator('[data-testid="mdtodo-task-toggle"], [data-testid="mdtodo-task-expand"], [data-action="toggle-task"], button[aria-expanded]').first();
  const toggleVisible = await visibleLocator(toggle);
  if (toggleVisible) await toggle.click();
  else await target.locator.click();
  await page.waitForTimeout(700);
  return {
    beforeUrl,
    afterUrl: currentPageUrl(),
    type: "expandMdtodoTask",
    selector: target.selector,
    toggleVisible,
    beforeProject,
    afterProject: await projectManagementCommandSnapshot(),
    pageId,
    valuesRedacted: true
  };
}

async function openMdtodoSourceConfig(command) {
  ensureProjectManagementCommand("openMdtodoSourceConfig");
  const beforeUrl = currentPageUrl();
  const beforeProject = await projectManagementCommandSnapshot();
  const existingDialog = page.locator('[data-testid="mdtodo-source-form-hwpod"], [data-testid="mdtodo-source-config-dialog"], [role="dialog"]').first();
  if (await visibleLocator(existingDialog)) {
    return {
      beforeUrl,
      afterUrl: currentPageUrl(),
      type: "openMdtodoSourceConfig",
      alreadyOpen: true,
      beforeProject,
      afterProject: await projectManagementCommandSnapshot(),
      pageId,
      valuesRedacted: true
    };
  }
  const button = page.locator('[data-testid="mdtodo-source-config-open"]').first();
  await button.waitFor({ state: "visible", timeout: 15000 });
  await button.click();
  await page.locator('[data-testid="mdtodo-source-form-hwpod"], [data-testid="mdtodo-source-config-dialog"], [role="dialog"]').first().waitFor({ state: "visible", timeout: 10000 }).catch(() => null);
  return {
    beforeUrl,
    afterUrl: currentPageUrl(),
    type: "openMdtodoSourceConfig",
    beforeProject,
    afterProject: await projectManagementCommandSnapshot(),
    pageId,
    valuesRedacted: true
  };
}

async function ensureMdtodoSourceConfigOpen() {
  const form = page.locator('[data-testid="mdtodo-source-form-hwpod"], [data-testid="mdtodo-source-form-node"], [data-testid="mdtodo-source-form-root"]').first();
  if (await visibleLocator(form)) return { opened: false };
  await openMdtodoSourceConfig({ type: "openMdtodoSourceConfig" });
  return { opened: true };
}

async function closeMdtodoSourceConfig(command) {
  ensureProjectManagementCommand("closeMdtodoSourceConfig");
  const beforeUrl = currentPageUrl();
  const beforeProject = await projectManagementCommandSnapshot();
  const close = await closeMdtodoSourceConfigIfOpen({ required: true });
  return {
    beforeUrl,
    afterUrl: currentPageUrl(),
    type: "closeMdtodoSourceConfig",
    close,
    beforeProject,
    afterProject: await projectManagementCommandSnapshot(),
    pageId,
    valuesRedacted: true
  };
}

async function closeMdtodoSourceConfigIfOpen(options = {}) {
  const dialog = page.locator('[data-testid="mdtodo-source-config-dialog"], [role="dialog"]').filter({
    has: page.locator('[data-testid="mdtodo-source-form-hwpod"], [data-testid="mdtodo-source-form-node"], [data-testid="mdtodo-source-form-root"], [data-testid="mdtodo-source-reindex-dialog"]')
  }).first();
  if (!await visibleLocator(dialog)) return { wasOpen: false, stillVisible: false };
  const closeButton = dialog.locator('[data-testid="mdtodo-source-config-close"], [aria-label="关闭配置"], [aria-label*="关闭"], [aria-label="Close"], button:has-text("关闭"), button:has-text("Cancel"), button:has-text("取消")').first();
  let closeClick = null;
  try {
    await closeButton.waitFor({ state: "visible", timeout: 5000 });
    await closeButton.click({ timeout: 5000 });
    closeClick = { attempted: true, ok: true };
  } catch (error) {
    closeClick = { attempted: true, ok: false, error: errorSummary(error) };
    await page.keyboard.press("Escape").catch(() => null);
  }
  await page.waitForTimeout(400);
  const stillVisible = await visibleLocator(dialog);
  const result = { wasOpen: true, closeClick, stillVisible, valuesRedacted: true };
  if (options.required && stillVisible) {
    const error = new Error("closeMdtodoSourceConfig dialog remained visible after close attempt");
    error.details = result;
    throw error;
  }
  return result;
}

function mdtodoTestIdCandidates(value) {
  return (Array.isArray(value) ? value : [value]).filter((item) => typeof item === "string" && item.length > 0);
}

function mdtodoTestIdLocator(value) {
  const candidates = mdtodoTestIdCandidates(value);
  return page.locator(candidates.map((testId) => '[data-testid="' + cssEscape(testId) + '"]').join(", ")).first();
}

async function fillMdtodoField(testId, value) {
  if (typeof value !== "string" || !value.trim()) return { testId, filled: false };
  const locator = mdtodoTestIdLocator(testId);
  await locator.waitFor({ state: "visible", timeout: 10000 });
  await locator.fill(value);
  const resolvedTestId = await locator.getAttribute("data-testid").catch(() => null);
  return { testId: resolvedTestId || mdtodoTestIdCandidates(testId)[0] || null, filled: true, value: opaqueIdSummary(value), valuesRedacted: true };
}

async function clickProjectButtonAndMaybeWait(testId, pathPattern) {
  const button = page.locator('[data-testid="' + cssEscape(testId) + '"]').first();
  await button.waitFor({ state: "visible", timeout: 15000 });
  const responsePromise = pathPattern ? page.waitForResponse((response) => {
    try {
      const pathname = new URL(response.url()).pathname;
      return pathPattern.test(pathname);
    } catch {
      return false;
    }
  }, { timeout: 15000 }).then((response) => ({ observed: true, status: response.status(), path: new URL(response.url()).pathname })).catch((error) => ({ observed: false, waitError: errorSummary(error) })) : Promise.resolve(null);
  const buttonState = await button.evaluate((element) => ({ disabled: Boolean(element.disabled) || element.getAttribute("aria-disabled") === "true", testId: element.getAttribute("data-testid") || null })).catch((error) => ({ disabled: null, error: errorSummary(error) }));
  if (buttonState.disabled === true) {
    const error = new Error(testId + " button is disabled");
    error.details = { buttonState, valuesRedacted: true };
    throw error;
  }
  await button.click();
  const response = await responsePromise;
  await page.waitForTimeout(900);
  return { buttonState, response, valuesRedacted: true };
}

async function configureMdtodoHwpodSource(command) {
  ensureProjectManagementCommand("configureMdtodoHwpodSource");
  const beforeUrl = currentPageUrl();
  const beforeProject = await projectManagementCommandSnapshot();
  const dialog = await ensureMdtodoSourceConfigOpen();
  const fields = [
    await fillMdtodoField("mdtodo-source-form-hwpod", commandValue(command, ["hwpodId", "hwpod", "value"])),
    await fillMdtodoField("mdtodo-source-form-node", commandValue(command, ["nodeId", "node"])),
    await fillMdtodoField("mdtodo-source-form-workspace", commandValue(command, ["workspaceRoot", "workspaceRootRef", "workspace"])),
    await fillMdtodoField("mdtodo-source-form-root", commandValue(command, ["root", "path"])),
  ];
  const save = await clickProjectButtonAndMaybeWait("mdtodo-source-save", /^\/v1\/project-management\/mdtodo\/sources/u);
  const close = await closeMdtodoSourceConfigIfOpen();
  return {
    beforeUrl,
    afterUrl: currentPageUrl(),
    type: "configureMdtodoHwpodSource",
    dialog,
    fields,
    save,
    close,
    beforeProject,
    afterProject: await projectManagementCommandSnapshot(),
    pageId,
    valuesRedacted: true
  };
}

async function probeMdtodoSource(command) {
  ensureProjectManagementCommand("probeMdtodoSource");
  const beforeUrl = currentPageUrl();
  const beforeProject = await projectManagementCommandSnapshot();
  await ensureMdtodoSourceConfigOpen();
  const probe = await clickProjectButtonAndMaybeWait("mdtodo-source-probe", /^\/v1\/project-management\/mdtodo\/sources/u);
  const close = await closeMdtodoSourceConfigIfOpen();
  return { beforeUrl, afterUrl: currentPageUrl(), type: "probeMdtodoSource", probe, close, beforeProject, afterProject: await projectManagementCommandSnapshot(), pageId, valuesRedacted: true };
}

async function reindexMdtodoSource(command) {
  ensureProjectManagementCommand("reindexMdtodoSource");
  const beforeUrl = currentPageUrl();
  const beforeProject = await projectManagementCommandSnapshot();
  await ensureMdtodoSourceConfigOpen();
  const dialogButton = page.locator('[data-testid="mdtodo-source-reindex-dialog"]').first();
  const buttonTestId = await visibleLocator(dialogButton) ? "mdtodo-source-reindex-dialog" : "mdtodo-source-reindex";
  const reindex = await clickProjectButtonAndMaybeWait(buttonTestId, /^\/v1\/project-management\/mdtodo\/sources/u);
  const close = await closeMdtodoSourceConfigIfOpen();
  return { beforeUrl, afterUrl: currentPageUrl(), type: "reindexMdtodoSource", buttonTestId, reindex, close, beforeProject, afterProject: await projectManagementCommandSnapshot(), pageId, valuesRedacted: true };
}

async function selectTaskIfCommandTargetsOne(command) {
  if (commandValue(command, ["taskRef", "taskId", "task"]).length === 0) return null;
  return selectMdtodoTask(command);
}

async function selectMdtodoProviderProfileForLaunch(command) {
  const provider = commandValue(command, ["provider", "providerProfile"]);
  if (!provider) return null;
  const select = page.locator('[data-testid="mdtodo-provider-profile"]').first();
  if (!(await visibleLocator(select))) {
    return { provider, visible: false, selected: null, valuesRedacted: true };
  }
  const selected = await selectHtmlOptionByValueOrLabel(select, provider);
  await page.waitForTimeout(300);
  return { provider, visible: true, ...selected, valuesRedacted: true };
}

async function saveMdtodoTaskWithButton(command, type, testId, fields) {
  ensureProjectManagementCommand(type);
  const beforeUrl = currentPageUrl();
  const beforeProject = await projectManagementCommandSnapshot();
  const selection = await selectTaskIfCommandTargetsOne(command);
  const inlineEditors = [];
  for (const field of fields) {
    if (field.openByDblClickTestId) inlineEditors.push(await ensureMdtodoInlineEditor(field.testId, field.openByDblClickTestId));
    if (field.kind === "fill") await fillMdtodoField(field.testId, field.value);
    if (field.kind === "select") {
      const locator = mdtodoTestIdLocator(field.testId);
      await locator.waitFor({ state: "visible", timeout: 10000 });
      await selectHtmlOptionByValueOrLabel(locator, field.value);
    }
  }
  const save = await clickProjectButtonAndMaybeWaitAny(Array.isArray(testId) ? testId : [testId], /^\/v1\/project-management\/mdtodo\/tasks/u);
  return { beforeUrl, afterUrl: currentPageUrl(), type, selection, inlineEditors, save, beforeProject, afterProject: await projectManagementCommandSnapshot(), pageId, valuesRedacted: true };
}

async function clickProjectButtonAndMaybeWaitAny(testIds, pathPattern) {
  const candidates = (testIds || []).filter(Boolean);
  for (const testId of candidates) {
    const button = page.locator('[data-testid="' + cssEscape(testId) + '"]').first();
    if (await visibleLocator(button)) return clickProjectButtonAndMaybeWait(testId, pathPattern);
  }
  if (candidates.length === 0) throw new Error("clickProjectButtonAndMaybeWaitAny requires at least one data-testid");
  return clickProjectButtonAndMaybeWait(candidates[0], pathPattern);
}

async function ensureMdtodoInlineEditor(editorTestId, readTestId) {
  const editor = mdtodoTestIdLocator(editorTestId);
  if (await visibleLocator(editor)) return { editorTestId, readTestId, opened: false };
  const read = mdtodoTestIdLocator(readTestId);
  await read.waitFor({ state: "visible", timeout: 10000 });
  await read.dblclick();
  await editor.waitFor({ state: "visible", timeout: 10000 });
  return { editorTestId, readTestId, opened: true };
}

async function editMdtodoTaskTitle(command) {
  const title = commandValue(command, ["title", "text", "value"]);
  if (!title) throw new Error("editMdtodoTaskTitle requires --title or --text");
  return saveMdtodoTaskWithButton(command, "editMdtodoTaskTitle", "mdtodo-edit-save", [{ kind: "fill", testId: "mdtodo-edit-title", value: title, openByDblClickTestId: "mdtodo-title-read" }]);
}

async function editMdtodoTaskBody(command) {
  const body = commandValue(command, ["text", "body", "value"]);
  if (!body) throw new Error("editMdtodoTaskBody requires --text or --text-stdin");
  return saveMdtodoTaskWithButton(command, "editMdtodoTaskBody", ["mdtodo-body-save", "mdtodo-edit-body-save"], [{ kind: "fill", testId: ["mdtodo-body-editor", "mdtodo-edit-body"], value: body, openByDblClickTestId: ["mdtodo-body-read", "mdtodo-body-rendered"] }]);
}

async function toggleMdtodoTaskStatus(command) {
  const status = commandValue(command, ["status", "value", "text"]);
  if (!status) throw new Error("toggleMdtodoTaskStatus requires --status");
  return saveMdtodoTaskWithButton(command, "toggleMdtodoTaskStatus", ["mdtodo-status-save", "mdtodo-edit-save"], [{ kind: "select", testId: "mdtodo-edit-status", value: status }]);
}

async function editMdtodoTaskInline(command) {
  const field = commandValue(command, ["field", "value"]).toLowerCase();
  if (field === "title") {
    const title = commandValue(command, ["title", "text"]);
    if (!title) throw new Error("editMdtodoTaskInline --field title requires --title or --text");
    return saveMdtodoTaskWithButton(command, "editMdtodoTaskInline", "mdtodo-edit-save", [{ kind: "fill", testId: "mdtodo-edit-title", value: title, openByDblClickTestId: "mdtodo-title-read" }]);
  }
  if (field === "body" || field === "content") {
    const body = commandValue(command, ["body", "text"]);
    if (!body) throw new Error("editMdtodoTaskInline --field body requires --body, --text, or --text-stdin");
    return saveMdtodoTaskWithButton(command, "editMdtodoTaskInline", ["mdtodo-body-save", "mdtodo-edit-body-save"], [{ kind: "fill", testId: ["mdtodo-body-editor", "mdtodo-edit-body"], value: body, openByDblClickTestId: ["mdtodo-body-read", "mdtodo-body-rendered"] }]);
  }
  if (field === "status") {
    const status = commandValue(command, ["status", "text"]);
    if (!status) throw new Error("editMdtodoTaskInline --field status requires --status or --text");
    return saveMdtodoTaskWithButton(command, "editMdtodoTaskInline", ["mdtodo-status-save", "mdtodo-edit-save"], [{ kind: "select", testId: "mdtodo-edit-status", value: status }]);
  }
  throw new Error("editMdtodoTaskInline requires --field title, body, or status");
}

async function openMdtodoReportPreview(command) {
  ensureProjectManagementCommand("openMdtodoReportPreview");
  const beforeUrl = currentPageUrl();
  const beforeProject = await projectManagementCommandSnapshot();
  const selection = await selectTaskIfCommandTargetsOne(command);
  const linkText = commandValue(command, ["link", "value", "text"]);
  const links = page.locator('[data-testid="mdtodo-report-link"]');
  await links.first().waitFor({ state: "visible", timeout: 15000 });
  const count = await links.count();
  let index = 0;
  if (linkText) {
    for (let i = 0; i < count; i += 1) {
      const item = links.nth(i);
      const text = await item.textContent().catch(() => "");
      if (String(text || "").includes(linkText)) {
        index = i;
        break;
      }
    }
  }
  const link = links.nth(index);
  const linkStateRaw = await link.evaluate((element, selectedIndex) => ({
    index: selectedIndex,
    disabled: Boolean(element.disabled) || element.getAttribute("aria-disabled") === "true",
    text: String(element.textContent || "").replace(/\s+/gu, " ").trim().slice(0, 240),
    valuesRedacted: true
  }), index).catch((error) => ({ index, error: errorSummary(error), valuesRedacted: true }));
  const linkState = {
    ...linkStateRaw,
    textHash: linkStateRaw.text ? sha256Text(linkStateRaw.text) : null,
    textPreview: linkStateRaw.text ? truncate(linkStateRaw.text, 120) : null,
    text: undefined,
    valuesRedacted: true
  };
  if (linkState.disabled === true) {
    const error = new Error("openMdtodoReportPreview selected report link is disabled");
    error.details = { beforeUrl, linkState, beforeProject, valuesRedacted: true };
    throw error;
  }
  const attempts = [];
  let response = null;
  let afterProject = null;
  for (let attempt = 1; attempt <= 3; attempt += 1) {
    const responsePromise = page.waitForResponse((item) => {
      try {
        const request = item.request();
        return request.method().toUpperCase() === "GET" && new URL(item.url()).pathname === "/v1/project-management/mdtodo/report-preview";
      } catch {
        return false;
      }
    }, { timeout: 10000 }).then((item) => ({ observed: true, status: item.status(), path: new URL(item.url()).pathname })).catch((error) => ({ observed: false, waitError: errorSummary(error) }));
    const clickMode = attempt === 1 ? "normal" : "force";
    let click = { ok: true, mode: clickMode, valuesRedacted: true };
    await link.scrollIntoViewIfNeeded().catch(() => null);
    try {
      if (attempt === 1) await link.click();
      else await link.click({ force: true, timeout: 5000 });
    } catch (error) {
      click = { ok: false, mode: clickMode, error: errorSummary(error), valuesRedacted: true };
      await link.evaluate((element) => element.click()).catch(() => null);
    }
    await page.locator('[data-testid="mdtodo-report-preview"], [data-testid="mdtodo-report-error"]').first().waitFor({ state: "visible", timeout: 5000 }).catch(() => null);
    afterProject = await projectManagementCommandSnapshot();
    response = afterProject?.reportPreviewVisible === true
      ? { observed: null, skippedAfterPreviewVisible: true, valuesRedacted: true }
      : await responsePromise;
    attempts.push({
      attempt,
      click,
      response,
      afterUrl: currentPageUrl(),
      reportPreviewVisible: afterProject?.reportPreviewVisible === true,
      reportErrorVisible: afterProject?.reportErrorVisible === true,
      reportFullscreenVisible: afterProject?.reportFullscreenVisible === true,
      valuesRedacted: true
    });
    if (afterProject?.reportPreviewVisible === true) break;
    await page.waitForTimeout(700).catch(() => null);
  }
  afterProject = afterProject ?? await projectManagementCommandSnapshot();
  if (afterProject?.reportPreviewVisible !== true) {
    const error = new Error("openMdtodoReportPreview did not show markdown report preview");
    error.details = { beforeUrl, afterUrl: currentPageUrl(), selection, linkState, response, attempts, beforeProject, afterProject, pageId, valuesRedacted: true };
    throw error;
  }
  return { beforeUrl, afterUrl: currentPageUrl(), type: "openMdtodoReportPreview", selection, linkState, response, attempts, beforeProject, afterProject, pageId, valuesRedacted: true };
}

async function toggleMdtodoReportFullscreen(command) {
  ensureProjectManagementCommand("toggleMdtodoReportFullscreen");
  const beforeUrl = currentPageUrl();
  const beforeProject = await projectManagementCommandSnapshot();
  const desired = commandValue(command, ["value", "text"]).toLowerCase();
  const dialog = page.locator('[data-testid="mdtodo-report-fullscreen-dialog"]').first();
  const initiallyOpen = await visibleLocator(dialog);
  let action = "open";
  if (desired === "close" || desired === "off") action = "close";
  else if (desired === "toggle") action = initiallyOpen ? "close" : "open";
  if (action === "close") {
    const closeButton = page.locator('[data-testid="mdtodo-report-fullscreen-dialog"] [aria-label="关闭报告"], [aria-label="关闭报告"]').first();
    await closeButton.waitFor({ state: "visible", timeout: 10000 });
    await closeButton.click();
  } else if (!initiallyOpen) {
    const button = page.locator('[data-testid="mdtodo-report-fullscreen"]').first();
    await button.waitFor({ state: "visible", timeout: 10000 });
    await button.click();
  }
  await page.waitForTimeout(500);
  return { beforeUrl, afterUrl: currentPageUrl(), type: "toggleMdtodoReportFullscreen", action, initiallyOpen, fullscreenOpen: await visibleLocator(dialog), beforeProject, afterProject: await projectManagementCommandSnapshot(), pageId, valuesRedacted: true };
}

async function fillNewTaskDraft(command) {
  const title = commandValue(command, ["title", "text", "value"]);
  if (!title) throw new Error(command.type + " requires --title or --text");
  const fields = [await fillMdtodoField("mdtodo-new-title", title)];
  const body = commandValue(command, ["body"]) || (command.title ? commandValue(command, ["text"]) : "");
  if (body) fields.push(await fillMdtodoField("mdtodo-new-body", body));
  return fields;
}

async function addMdtodoTaskWithButton(command, type, testId) {
  ensureProjectManagementCommand(type);
  const beforeUrl = currentPageUrl();
  const beforeProject = await projectManagementCommandSnapshot();
  const selection = type === "addMdtodoRootTask" ? null : await selectTaskIfCommandTargetsOne(command);
  const fields = await fillNewTaskDraft(command);
  const save = await clickProjectButtonAndMaybeWait(testId, /^\/v1\/project-management\/mdtodo\/tasks/u);
  return { beforeUrl, afterUrl: currentPageUrl(), type, selection, fields, save, beforeProject, afterProject: await projectManagementCommandSnapshot(), pageId, valuesRedacted: true };
}

async function addMdtodoRootTask(command) {
  return addMdtodoTaskWithButton(command, "addMdtodoRootTask", "mdtodo-add-root");
}

async function addMdtodoSubTask(command) {
  return addMdtodoTaskWithButton(command, "addMdtodoSubTask", "mdtodo-add-subtask");
}

async function continueMdtodoTask(command) {
  return addMdtodoTaskWithButton(command, "continueMdtodoTask", "mdtodo-continue-task");
}

async function deleteMdtodoTask(command) {
  ensureProjectManagementCommand("deleteMdtodoTask");
  const beforeUrl = currentPageUrl();
  const beforeProject = await projectManagementCommandSnapshot();
  const selection = await selectTaskIfCommandTargetsOne(command);
  const firstClick = await clickProjectButtonAndMaybeWait("mdtodo-delete-task", null);
  let confirmClick = null;
  const deleteButton = page.locator('[data-testid="mdtodo-delete-task"]').first();
  const confirmVisible = await visibleLocator(page.locator('[data-testid="mdtodo-delete-cancel"]').first())
    || await deleteButton.evaluate((element) => /确认|confirm/iu.test(String(element.textContent || ""))).catch(() => false);
  if (confirmVisible) confirmClick = await clickProjectButtonAndMaybeWait("mdtodo-delete-task", /^\/v1\/project-management\/mdtodo\/tasks/u);
  return { beforeUrl, afterUrl: currentPageUrl(), type: "deleteMdtodoTask", selection, firstClick, confirmClick, beforeProject, afterProject: await projectManagementCommandSnapshot(), pageId, valuesRedacted: true };
}

async function launchWorkbenchFromTask(command) {
  const commandType = command.type === "launchWorkbenchFromMdtodo" ? "launchWorkbenchFromMdtodo" : "launchWorkbenchFromTask";
  ensureProjectManagementCommand(commandType);
  const beforeUrl = currentPageUrl();
  const initialProject = await projectManagementCommandSnapshot({ includeRaw: true });
  const requestedSourceId = commandValue(command, ["sourceId", "mdtodoSourceId"]);
  const requestedFileRef = commandValue(command, ["fileRef"]);
  const requestedFilename = commandValue(command, ["filename", "fileName"]);
  const requestedTaskRef = typeof command.taskRef === "string" && command.taskRef.trim() ? command.taskRef.trim() : null;
  const requestedTaskId = typeof command.taskId === "string" && command.taskId.trim() ? command.taskId.trim() : null;
  const needsMdtodoSelection = Boolean(requestedFileRef || requestedFilename || requestedTaskRef || requestedTaskId);
  const sourceSelection = requestedSourceId && initialProject.selectedSourceIdRaw !== requestedSourceId
    ? await selectMdtodoSource({ ...command, type: "selectMdtodoSource", sourceId: requestedSourceId })
    : !requestedSourceId && needsMdtodoSelection && !initialProject.selectedSourceIdRaw
      ? await selectMdtodoSource({ ...command, type: "selectMdtodoSource", sourceId: "" })
    : null;
  const fileProject = sourceSelection ? await projectManagementCommandSnapshot({ includeRaw: true }) : initialProject;
  const fileSelection = (requestedFileRef && fileProject.selectedFileRefRaw !== requestedFileRef) || requestedFilename
    ? await selectMdtodoFile({ ...command, type: "selectMdtodoFile", fileRef: requestedFileRef || command.fileRef, filename: requestedFilename || command.filename })
    : null;
  const taskProject = fileSelection ? await projectManagementCommandSnapshot({ includeRaw: true }) : fileProject;
  const taskSelection = (requestedTaskRef && taskProject.selectedTaskRefRaw !== requestedTaskRef) || requestedTaskId
    ? await selectMdtodoTask({ ...command, type: "selectMdtodoTask", taskRef: requestedTaskRef || command.taskRef, taskId: requestedTaskId || command.taskId })
    : null;
  const providerSelection = await selectMdtodoProviderProfileForLaunch(command);
  const projectBeforeClick = await projectManagementCommandSnapshot({ includeRaw: true });
  const button = page.locator('[data-testid="mdtodo-workbench-launch"], [data-testid="mdtodo-launch-workbench"], [data-action="launch-workbench"]').first();
  await button.waitFor({ state: "visible", timeout: 15000 });
  const buttonState = await button.evaluate((element) => ({
    disabled: Boolean(element.disabled) || element.getAttribute("aria-disabled") === "true",
    textHash: element.textContent ? null : null,
    testId: element.getAttribute("data-testid") || null,
    action: element.getAttribute("data-action") || null,
    valuesRedacted: true
  })).catch((error) => ({ disabled: null, error: errorSummary(error), valuesRedacted: true }));
  if (buttonState.disabled === true) {
    const error = new Error("launchWorkbenchFromTask button is disabled");
    error.details = { beforeUrl, project: sanitizeProjectCommandSnapshot(projectBeforeClick), buttonState, valuesRedacted: true };
    throw error;
  }
  const launchPath = projectManagement.launchRoute;
  const launchResponsePromise = page.waitForResponse((response) => {
    const request = response.request();
    if (request.method().toUpperCase() !== "POST") return false;
    try {
      return new URL(response.url()).pathname === launchPath;
    } catch {
      return false;
    }
  }, { timeout: 45000 }).catch((error) => ({ waitError: errorSummary(error) }));
  const chatResponsePromise = page.waitForResponse((response) => {
    const request = response.request();
    if (request.method().toUpperCase() !== "POST") return false;
    try {
      return new URL(response.url()).pathname === "/v1/agent/chat";
    } catch {
      return false;
    }
  }, { timeout: 30000 }).then(async (response) => {
    let chatPayload = null;
    let chatPayloadError = null;
    const headers = response.headers();
    const chatBody = await boundedResponseJson(response);
    chatPayload = chatBody.value;
    chatPayloadError = chatBody.error;
    return {
      observed: true,
      status: response.status(),
      statusText: response.statusText(),
      path: new URL(response.url()).pathname,
      sessionId: sessionIdFromAgentSessionPayload(chatPayload),
      traceId: traceIdFromAgentChatPayload(chatPayload),
      otelTraceId: typeof headers["x-hwlab-otel-trace-id"] === "string" ? headers["x-hwlab-otel-trace-id"] : null,
      responseParsed: chatPayload !== null,
      responseParseError: chatPayloadError,
      valuesRedacted: true
    };
  }).catch((error) => ({ observed: false, waitError: errorSummary(error), valuesRedacted: true }));
  await button.click();
  const launchResponse = await launchResponsePromise;
  if (launchResponse?.waitError) {
    const error = new Error("launchWorkbenchFromTask did not observe POST " + launchPath + " response after button click");
    error.details = { beforeUrl, afterUrl: currentPageUrl(), launchPath, project: sanitizeProjectCommandSnapshot(projectBeforeClick), waitError: launchResponse.waitError, valuesRedacted: true };
    throw error;
  }
  const launchStatus = launchResponse.status();
  const headers = launchResponse.headers();
  const launchTraceHeader = typeof headers["x-hwlab-otel-trace-id"] === "string" ? headers["x-hwlab-otel-trace-id"] : null;
  let payload = null;
  let responseParseError = null;
  const launchBody = await boundedResponseJson(launchResponse);
  payload = launchBody.value;
  responseParseError = launchBody.error;
  const sessionId = sessionIdFromAgentSessionPayload(payload);
  const workbenchUrl = safeUrlPath(payload?.workbenchUrl || payload?.url || "");
  const contractVersion = typeof payload?.contractVersion === "string" ? payload.contractVersion : null;
  if (launchStatus < 200 || launchStatus >= 300 || !sessionId) {
    const error = new Error("launchWorkbenchFromTask did not receive a successful authoritative Workbench session");
    error.details = { beforeUrl, afterUrl: currentPageUrl(), launchStatus, statusText: launchResponse.statusText(), contractVersion, responseParsed: payload !== null, responseParseError, sessionId, workbenchUrl, otelTraceId: launchTraceHeader, valuesRedacted: true };
    throw error;
  }
  if (workbenchUrl) {
    await page.waitForFunction((expectedPath) => window.location.pathname === expectedPath, workbenchUrl, { timeout: 20000 }).catch(() => null);
  }
  const chat = await chatResponsePromise;
  await page.waitForTimeout(1500);
  const workbenchSnapshot = await workbenchSessionSnapshot();
  return {
    beforeUrl,
    afterUrl: currentPageUrl(),
    launchPath,
    launchStatus,
    statusText: launchResponse.statusText(),
    contractVersion,
    sessionId,
    workbenchUrl,
    otelTraceId: launchTraceHeader,
    chatObserved: chat?.observed === true,
    chatStatus: chat?.status ?? null,
    chatSessionId: chat?.sessionId ?? null,
    chatTraceId: chat?.traceId ?? null,
    chatOtelTraceId: chat?.otelTraceId ?? null,
    chat,
    workbenchSnapshot,
    selectedTask: opaqueIdSummary(projectBeforeClick.selectedTaskRefRaw),
    selection: {
      source: sourceSelection,
      file: fileSelection,
      task: taskSelection,
      provider: providerSelection,
      valuesRedacted: true
    },
    initialProject: sanitizeProjectCommandSnapshot(initialProject),
    projectBeforeClick: sanitizeProjectCommandSnapshot(projectBeforeClick),
    buttonState,
    responseParsed: payload !== null,
    responseParseError,
    pageId,
    valuesRedacted: true
  };
}

async function launchWorkbenchFromMdtodo(command) {
  return launchWorkbenchFromTask({ ...command, type: "launchWorkbenchFromMdtodo" });
}

async function projectManagementCommandSnapshot(options = {}) {
  const raw = await page.evaluate(() => {
    const visible = (element) => {
      if (!element) return false;
      const rect = element.getBoundingClientRect();
      const style = window.getComputedStyle(element);
      return rect.width > 0 && rect.height > 0 && style.visibility !== "hidden" && style.display !== "none";
    };
    const text = (element) => String(element?.textContent || "").replace(/\s+/gu, " ").trim().slice(0, 240);
    const selectedTask = document.querySelector('[data-task-ref][data-selected="true"], [data-task-ref][aria-selected="true"], [data-task-ref].selected, [data-task-ref].is-selected');
    const selectedSource = document.querySelector('[data-source-id][data-selected="true"], [data-source-id][aria-selected="true"], [data-source-id].selected, [data-source-id].is-selected');
    const selectedFile = document.querySelector('[data-file-ref][data-selected="true"], [data-file-ref][aria-selected="true"], [data-file-ref].selected, [data-file-ref].is-selected');
    const sourceSelect = document.querySelector('[data-testid="mdtodo-source-select"]');
    const fileSelect = document.querySelector('[data-testid="mdtodo-file-select"]');
    const sourceOptionCount = sourceSelect ? Array.from(sourceSelect.options || []).filter((option) => option.value).length : 0;
    const fileOptionCount = fileSelect ? Array.from(fileSelect.options || []).filter((option) => option.value).length : 0;
    const fileOptions = fileSelect ? Array.from(fileSelect.options || []).filter((option) => option.value).map((option) => ({
      value: option.value,
      label: text(option),
      selected: option.selected === true
    })) : [];
    const selectedFileOption = fileOptions.find((option) => option.selected) || null;
    const launch = document.querySelector('[data-testid="mdtodo-workbench-launch"], [data-testid="mdtodo-launch-workbench"], [data-action="launch-workbench"]');
    const bodyRendered = document.querySelector('[data-testid="mdtodo-body-rendered"], [data-testid="mdtodo-body-read"]');
    const reportPreview = document.querySelector('[data-testid="mdtodo-report-preview"]');
    const reportError = document.querySelector('[data-testid="mdtodo-report-error"]');
    const reportFullscreen = document.querySelector('[data-testid="mdtodo-report-fullscreen-dialog"]');
    const reportLinks = Array.from(document.querySelectorAll('[data-testid="mdtodo-report-link"]')).filter(visible);
    return {
      path: window.location.pathname,
      pageKind: visible(document.querySelector('[data-testid="project-management-mdtodo"]')) ? "project-management-mdtodo" : visible(document.querySelector('[data-testid="project-management-root"]')) ? "project-management-root" : null,
      sourceCount: Math.max(Array.from(document.querySelectorAll('[data-source-id]')).filter(visible).length, sourceOptionCount),
      fileCount: Math.max(Array.from(document.querySelectorAll('[data-file-ref]')).filter(visible).length, fileOptionCount),
      taskCount: Array.from(document.querySelectorAll('[data-task-ref]')).filter(visible).length,
      selectedSourceIdRaw: selectedSource?.getAttribute("data-source-id") || sourceSelect?.value || null,
      selectedFileRefRaw: selectedFile?.getAttribute("data-file-ref") || fileSelect?.value || null,
      selectedFileLabel: selectedFile ? text(selectedFile) : selectedFileOption?.label || null,
      fileOptionLabels: fileOptions.map((option) => option.label).filter(Boolean).slice(0, 20),
      selectedTaskRefRaw: selectedTask?.getAttribute("data-task-ref") || null,
      selectedTaskId: selectedTask?.getAttribute("data-task-id") || selectedTask?.getAttribute("data-rxx-id") || null,
      selectedTaskStatus: selectedTask?.getAttribute("data-task-status") || selectedTask?.querySelector("[data-state]")?.getAttribute("data-state") || null,
      sourceSelectVisible: visible(sourceSelect),
      fileSelectVisible: visible(fileSelect),
      sourceConfigVisible: visible(document.querySelector('[data-testid="mdtodo-source-form-hwpod"], [data-testid="mdtodo-source-config-dialog"], [role="dialog"]')),
      taskEditorVisible: visible(document.querySelector('[data-testid="mdtodo-edit-title"], [data-testid="mdtodo-edit-body"], [data-testid="mdtodo-body-editor"]')),
      taskBodyVisible: visible(bodyRendered),
      taskBodyText: visible(bodyRendered) ? text(bodyRendered) : "",
      newTaskDraftVisible: visible(document.querySelector('[data-testid="mdtodo-new-title"], [data-testid="mdtodo-new-body"]')),
      reportLinkCount: reportLinks.length,
      reportPreviewVisible: visible(reportPreview),
      reportPreviewText: visible(reportPreview) ? text(reportPreview) : "",
      reportErrorVisible: visible(reportError),
      reportErrorText: visible(reportError) ? text(reportError) : "",
      reportFullscreenVisible: visible(reportFullscreen),
      launchButtonVisible: visible(launch),
      launchButtonEnabled: visible(launch) && !launch.disabled && launch.getAttribute("aria-disabled") !== "true",
      launchButtonText: text(launch),
      blockerTexts: Array.from(document.querySelectorAll('[data-testid="mdtodo-workbench-launch-blocker"], [data-testid="mdtodo-workbench-launch-error"], [data-testid="mdtodo-create-blocker"], .launch-blocker, [role="alert"]')).filter(visible).map(text).filter(Boolean).slice(0, 8),
      workbenchLinkCount: Array.from(document.querySelectorAll('[data-testid="mdtodo-workbench-link-summary"] li, a[href*="/workbench/sessions/"]')).filter(visible).length,
      valuesRedacted: true
    };
  }).catch((error) => ({ error: errorSummary(error), valuesRedacted: true }));
  if (options.includeRaw === true) return raw;
  return sanitizeProjectCommandSnapshot(raw);
}

function sanitizeProjectCommandSnapshot(value) {
  if (!value || typeof value !== "object") return value;
  const textDigest = (raw, limit = 160) => {
    const text = String(raw || "");
    return { textHash: sha256Text(text), textPreview: truncate(text, limit), textBytes: Buffer.byteLength(text), valuesRedacted: true };
  };
  const fileLabelLooksDirect = (label) => /^[^/\\]+\.md$/iu.test(String(label || "").trim());
  const suspiciousFileLabel = (label) => {
    const text = String(label || "").trim();
    return Boolean(text && (!fileLabelLooksDirect(text) || /(?:details\/|_Task_Report|_log_|\/)/iu.test(text)));
  };
  const fileLabels = Array.isArray(value.fileOptionLabels) ? value.fileOptionLabels.map((item) => String(item || "")).filter(Boolean) : [];
  return {
    ...value,
    selectedSourceId: opaqueIdSummary(value.selectedSourceIdRaw),
    selectedFileRef: opaqueIdSummary(value.selectedFileRefRaw),
    selectedTaskRef: opaqueIdSummary(value.selectedTaskRefRaw),
    selectedSourceIdRaw: undefined,
    selectedFileRefRaw: undefined,
    selectedTaskRefRaw: undefined,
    selectedFileLabel: value.selectedFileLabel ? textDigest(value.selectedFileLabel, 120) : null,
    selectedFileLabelLooksDirect: value.selectedFileLabel ? fileLabelLooksDirect(value.selectedFileLabel) : null,
    fileOptionLabelSamples: fileLabels.slice(0, 8).map((item) => textDigest(item, 120)),
    fileOptionSuspiciousLabelCount: fileLabels.filter(suspiciousFileLabel).length,
    fileOptionLabels: undefined,
    taskBodyText: undefined,
    taskBody: value.taskBodyText ? textDigest(value.taskBodyText, 200) : null,
    reportPreviewText: undefined,
    reportPreview: value.reportPreviewText ? textDigest(value.reportPreviewText, 200) : null,
    reportErrorText: undefined,
    reportError: value.reportErrorText ? textDigest(value.reportErrorText, 200) : null,
    launchButtonTextHash: value.launchButtonText ? sha256Text(value.launchButtonText) : null,
    launchButtonTextPreview: value.launchButtonText ? truncate(value.launchButtonText, 80) : null,
    launchButtonText: undefined,
    blockerTexts: Array.isArray(value.blockerTexts) ? value.blockerTexts.map((item) => ({ textHash: sha256Text(item), textPreview: truncate(item, 160), textBytes: Buffer.byteLength(String(item || "")) })) : [],
    valuesRedacted: true
  };
}

function opaqueIdSummary(value) {
  const text = String(value || "");
  if (!text) return null;
  return {
    hash: sha256Text(text),
    preview: text.length <= 18 ? text : text.slice(0, 10) + "..." + text.slice(-5),
    bytes: Buffer.byteLength(text),
    valuesRedacted: true
  };
}
`;
}
