// SPEC: PJ2026-01040111 长程观测 draft-2026-06-20-p0-passive-web-probe-observer.
// SPEC: PJ2026-01060508 Web哨兵 draft-2026-06-26-p9-multi-web-probe-sentinel.
// Responsibility: Source string for the pure-client HWLAB web-probe observer runner.
import { nodeWebObserveRunnerCommandActionsSource } from "./hwlab-node-web-observe-runner-actions-source";

export function nodeWebObserveRunnerSource(): string {
  return String.raw`#!/usr/bin/env node
import { createHash, randomBytes } from "node:crypto";
import { appendFile, mkdir, readFile, readdir, rename, stat, unlink, writeFile } from "node:fs/promises";
import path from "node:path";
import { pathToFileURL } from "node:url";

const specRef = "PJ2026-01040111 长程观测 draft-2026-06-20-p0-passive-web-probe-observer";
const startedAtMs = Date.now();
const startedAt = new Date(startedAtMs).toISOString();
const baseUrl = normalizeBaseUrl(process.env.HWLAB_WEB_BASE_URL);
const username = process.env.HWLAB_WEB_USER || "admin";
const password = process.env.HWLAB_WEB_PASS || "";
const stateDir = path.resolve(process.env.UNIDESK_WEB_OBSERVE_STATE_DIR || ".state/web-observe/manual");
const jobId = safeId(process.env.UNIDESK_WEB_OBSERVE_JOB_ID || "webobs-" + Date.now().toString(36) + "-" + randomBytes(3).toString("hex"));
const targetPath = process.env.UNIDESK_WEB_OBSERVE_TARGET_PATH || "/workbench";
const sampleIntervalMs = positiveInteger(process.env.UNIDESK_WEB_OBSERVE_SAMPLE_INTERVAL_MS, 5000);
const screenshotIntervalMs = positiveInteger(process.env.UNIDESK_WEB_OBSERVE_SCREENSHOT_INTERVAL_MS, 300000);
const maxSamples = positiveInteger(process.env.UNIDESK_WEB_OBSERVE_MAX_SAMPLES, 0);
const observerRefreshIntervalMs = positiveInteger(process.env.UNIDESK_WEB_OBSERVE_OBSERVER_REFRESH_INTERVAL_MS, 180000);
const viewport = parseViewport(process.env.UNIDESK_WEB_OBSERVE_VIEWPORT || "1440x900");
const browserProxyMode = parseBrowserProxyMode(process.env.UNIDESK_WEB_OBSERVE_BROWSER_PROXY_MODE || "auto");
const authLoginMaxAttempts = boundedInteger(process.env.UNIDESK_WEB_OBSERVE_AUTH_LOGIN_MAX_ATTEMPTS, 6, 1, 20);
const authLoginRequestTimeoutMs = boundedInteger(process.env.UNIDESK_WEB_OBSERVE_AUTH_LOGIN_REQUEST_TIMEOUT_MS, 30000, 1000, 120000);
const authLoginInitialDelayMs = boundedInteger(process.env.UNIDESK_WEB_OBSERVE_AUTH_LOGIN_INITIAL_DELAY_MS, 500, 0, 60000);
const authLoginMaxDelayMs = boundedInteger(process.env.UNIDESK_WEB_OBSERVE_AUTH_LOGIN_MAX_DELAY_MS, 10000, 0, 120000);
const navigationMaxAttempts = boundedInteger(process.env.UNIDESK_WEB_OBSERVE_NAVIGATION_MAX_ATTEMPTS, 4, 1, 10);
const alertThresholds = parseAlertThresholds(process.env.UNIDESK_WEB_OBSERVE_ALERT_THRESHOLDS_JSON);
const projectManagement = parseProjectManagementConfig(process.env.UNIDESK_WEB_OBSERVE_PROJECT_MANAGEMENT_JSON);
const playwrightProxy = proxyConfigFromEnv(baseUrl);
const chromiumLaunchOptions = chromiumLaunchOptionsForProxy(playwrightProxy);
const pageId = "control-" + randomBytes(4).toString("hex");
const observerPageId = "observer-" + randomBytes(4).toString("hex");
const dirs = {
  commandsPending: path.join(stateDir, "commands", "pending"),
  commandsProcessing: path.join(stateDir, "commands", "processing"),
  commandsDone: path.join(stateDir, "commands", "done"),
  commandsFailed: path.join(stateDir, "commands", "failed"),
  screenshots: path.join(stateDir, "screenshots"),
  analysis: path.join(stateDir, "analysis"),
  archive: path.join(stateDir, "archive"),
};
const files = {
  manifest: path.join(stateDir, "manifest.json"),
  heartbeat: path.join(stateDir, "heartbeat.json"),
  control: path.join(stateDir, "control.jsonl"),
  samples: path.join(stateDir, "samples.jsonl"),
  network: path.join(stateDir, "network.jsonl"),
  console: path.join(stateDir, "console.jsonl"),
  errors: path.join(stateDir, "errors.jsonl"),
  artifacts: path.join(stateDir, "artifacts.jsonl"),
};

let browser;
let context;
let page;
let observerPage;
let lastObserverRefreshAtMs = Date.now();
let sampleSeq = 0;
let commandSeq = 0;
let artifactSeq = 0;
let activeCommandId = null;
let stopping = false;
let terminalStatus = "starting";
let lastScreenshotAtMs = 0;
let auth = null;
let pageLoadSeq = 0;
let controlPageEpoch = 0;
let observerPageEpoch = 0;
let currentPageProvenance = null;
let heartbeatPulseTimer = null;
const jsonlRotation = { stamp: compactFileTimestamp(startedAt), files: [] };

try {
  if (!password) throw new Error("missing HWLAB_WEB_PASS");
  await prepareDirs();
  await rotateExistingJsonlArtifacts();
  await writeManifest({ status: "starting" });
  await writeHeartbeat({ status: "starting" });
  heartbeatPulseTimer = startHeartbeatPulse();
  if (jsonlRotation.files.length > 0) await appendJsonl(files.control, eventRecord("jsonl-rotated", { stamp: jsonlRotation.stamp, archiveDir: path.relative(stateDir, dirs.archive), files: jsonlRotation.files, valuesRedacted: true }));
  const launcher = await import(pathToFileURL(path.resolve("scripts/src/browser-launcher.mjs")).href);
  const { chromium } = await launcher.importPlaywright();
  browser = await launcher.launchChromium(chromium, chromiumLaunchOptions);
  context = await browser.newContext({ viewport, ...(playwrightProxy === null ? {} : { proxy: playwrightProxy }) });
  page = await context.newPage();
  attachPassiveListeners(page, "control", pageId);
  auth = await runControlCommand({ id: "startup-login", type: "login", createdAt: startedAt, source: "startup" }, async () => authenticate(context));
  await runControlCommand({ id: "startup-goto", type: "goto", path: targetPath, createdAt: new Date().toISOString(), source: "startup" }, async () => gotoTarget(targetPath));
  observerPage = await context.newPage();
  attachPassiveListeners(observerPage, "observer", observerPageId);
    await runControlCommand({ id: "startup-observer-goto", type: "observerGoto", path: targetPath, createdAt: new Date().toISOString(), source: "startup" }, async () => {
      const result = await syncObserverPageToControlSession("startup");
      if (!result?.ok) {
        await appendJsonl(files.control, eventRecord("observer-startup-degraded", {
          reason: result?.failureKind || result?.reason || "observer-not-ready",
          result: sanitize(result),
          valuesRedacted: true,
        }));
      }
      return result ?? { ok: false, reason: "observer-not-ready", pageRole: "observer", pageId: observerPageId, valuesRedacted: true };
    });
  terminalStatus = "running";
  await writeManifest({ status: "running", auth: publicAuth(auth) });
  await writeHeartbeat({ status: "running" });
  while (!stopping) {
    await drainOneCommand();
    await samplePage("interval");
    if (maxSamples > 0 && sampleSeq >= maxSamples) {
      await appendJsonl(files.control, controlRecord({ id: "max-samples", type: "stop", source: "sampler" }, "completed", { reason: "max-samples", maxSamples }));
      break;
    }
    await sleep(sampleIntervalMs);
  }
  terminalStatus = "completed";
  await writeHeartbeat({ status: "completed" });
  await writeManifest({ status: "completed", completedAt: new Date().toISOString() });
  process.exitCode = 0;
} catch (error) {
  terminalStatus = "failed";
  await appendJsonl(files.errors, eventRecord("runner-error", { error: errorSummary(error) })).catch(() => {});
  await writeHeartbeat({ status: "failed", error: errorSummary(error) }).catch(() => {});
  await writeManifest({ status: "failed", error: errorSummary(error) }).catch(() => {});
  process.exitCode = 2;
} finally {
  if (heartbeatPulseTimer) clearInterval(heartbeatPulseTimer);
  if (browser) await browser.close().catch(() => {});
}

async function prepareDirs() {
  await mkdir(stateDir, { recursive: true, mode: 0o700 });
  await Promise.all(Object.values(dirs).map((dir) => mkdir(dir, { recursive: true, mode: 0o700 })));
}

async function rotateExistingJsonlArtifacts() {
  for (const [key, file] of Object.entries(files)) {
    if (!file.endsWith(".jsonl")) continue;
    let meta = null;
    try {
      meta = await stat(file);
    } catch (error) {
      if (error?.code === "ENOENT") continue;
      throw error;
    }
    if (!meta?.isFile()) continue;
    const archiveFile = await uniqueArchiveFile(jsonlRotation.stamp + "-" + path.basename(file));
    await rename(file, archiveFile);
    jsonlRotation.files.push({ key, from: path.relative(stateDir, file), to: path.relative(stateDir, archiveFile), byteCount: meta.size });
  }
}

async function uniqueArchiveFile(name) {
  let candidate = path.join(dirs.archive, name);
  const parsed = path.parse(name);
  for (let index = 1; ; index += 1) {
    try {
      await stat(candidate);
      candidate = path.join(dirs.archive, parsed.name + "-" + index + parsed.ext);
    } catch (error) {
      if (error?.code === "ENOENT") return candidate;
      throw error;
    }
  }
}

function compactFileTimestamp(value) {
  return new Date(value).toISOString().replace(/[-:]/gu, "").replace(/[.]\d{3}Z$/u, "Z");
}

async function writeManifest(extra = {}) {
  const manifest = {
    ok: extra.status !== "failed",
    command: "web-probe-observe",
    specRef,
    jobId,
    pid: process.pid,
    stateDir,
    baseUrl,
    targetPath,
    network: publicNetwork(playwrightProxy),
    navigation: { maxAttempts: navigationMaxAttempts, valuesRedacted: true },
    pageAuthority: { browser: "chromium", context: "shared-auth", pageMode: "dual-control-observer", controlPageId: pageId, observerPageId, continuityBreaksRecorded: true },
    pageProvenance: compactPageProvenance(currentPageProvenance),
    sampling: { mode: "passive", sampleIntervalMs, screenshotIntervalMs, maxSamples, observerRefreshIntervalMs, observerInitiatedDefault: false, responseBodyReadDefault: false },
    alertThresholds,
    projectManagement,
    jsonlRotation,
    commandDirs: dirs,
    artifacts: files,
    safety: { pureClient: true, inboundApi: false, database: false, queueConsumer: false, k8sWorkload: false, valuesRedacted: true, secretValuesPrinted: false },
    startedAt,
    ...extra,
  };
  await writeFile(files.manifest, JSON.stringify(manifest, null, 2) + "\n", { mode: 0o600 });
}

async function writeHeartbeat(extra = {}) {
  const heartbeat = {
    ok: terminalStatus !== "failed",
    jobId,
    pid: process.pid,
    stateDir,
    status: terminalStatus,
    pageId,
    observerPageId,
    baseUrl,
    currentUrl: currentPageUrl(),
    observerUrl: pageUrl(observerPage),
    observerRefreshIntervalMs,
    lastObserverRefreshAt: Number.isFinite(lastObserverRefreshAtMs) ? new Date(lastObserverRefreshAtMs).toISOString() : null,
    pageProvenance: compactPageProvenance(currentPageProvenance),
    sampleSeq,
    commandSeq,
    activeCommandId,
    updatedAt: new Date().toISOString(),
    uptimeMs: Date.now() - startedAtMs,
    ...extra,
  };
  await writeFile(files.heartbeat, JSON.stringify(heartbeat, null, 2) + "\n", { mode: 0o600 });
}

function startHeartbeatPulse() {
  const timer = setInterval(() => {
    if (stopping) return;
    void writeHeartbeat({ status: terminalStatus, heartbeatPulse: true })
      .catch((error) => appendJsonl(files.errors, eventRecord("heartbeat-pulse-error", { error: errorSummary(error) })));
  }, 5000);
  if (timer && typeof timer.unref === "function") timer.unref();
  return timer;
}

function attachPassiveListeners(targetPage, pageRole = "control", targetPageId = pageId) {
  targetPage.on("request", (request) => {
    void appendJsonl(files.network, eventRecord("request", {
      pageRole,
      pageId: targetPageId,
      observerInitiated: false,
      commandId: activeCommandId,
      method: request.method(),
      url: safeUrl(request.url()),
      resourceType: request.resourceType(),
      frameUrl: safeFrameUrl(request.frame()),
    }));
  });
  targetPage.on("response", (response) => {
    const request = response.request();
    const base = {
      pageRole,
      pageId: targetPageId,
      sampleSeq,
      observerInitiated: false,
      commandId: activeCommandId,
      method: request.method(),
      url: safeUrl(response.url()),
      resourceType: request.resourceType(),
      status: response.status(),
      statusText: response.statusText(),
      fromServiceWorker: response.fromServiceWorker(),
    };
    void (async () => {
      const bodyFields = await summarizeWorkbenchResponseBody(response, request);
      await appendJsonl(files.network, eventRecord("response", { ...base, ...bodyFields }));
    })().catch((error) => appendJsonl(files.errors, eventRecord("response-body-summary-error", {
      pageRole,
      pageId: targetPageId,
      sampleSeq,
      commandId: activeCommandId,
      method: request.method(),
      url: safeUrl(response.url()),
      error: errorSummary(error),
      valuesRedacted: true
    })));
  });
  targetPage.on("requestfailed", (request) => {
    void appendJsonl(files.network, eventRecord("requestfailed", {
      pageRole,
      pageId: targetPageId,
      observerInitiated: false,
      commandId: activeCommandId,
      method: request.method(),
      url: safeUrl(request.url()),
      resourceType: request.resourceType(),
      failure: request.failure()?.errorText ?? null,
    }));
  });
  targetPage.on("console", (message) => {
    void appendJsonl(files.console, eventRecord("console", { pageRole, pageId: targetPageId, type: message.type(), text: truncate(message.text(), 1000), location: message.location() }));
  });
  targetPage.on("pageerror", (error) => {
    void appendJsonl(files.errors, eventRecord("pageerror", { pageRole, pageId: targetPageId, error: errorSummary(error) }));
  });
  targetPage.on("crash", () => {
    void appendJsonl(files.errors, eventRecord("page-crash", { pageRole, pageId: targetPageId }));
  });
  targetPage.on("close", () => {
    void appendJsonl(files.control, eventRecord("continuity-break", { pageRole, pageId: targetPageId, reason: "page-closed" }));
  });
}

async function drainOneCommand() {
  const entries = (await readdir(dirs.commandsPending).catch(() => [])).filter((name) => name.endsWith(".json")).sort();
  const name = entries[0];
  if (!name) return;
  const pending = path.join(dirs.commandsPending, name);
  const processing = path.join(dirs.commandsProcessing, name);
  await rename(pending, processing).catch(() => null);
  const raw = await readFile(processing, "utf8");
  const command = JSON.parse(raw);
  const id = safeId(command.id || name.replace(/[.]json$/u, ""));
  command.id = id;
  const stopCommandSampler = startCommandActiveSampler(command);
  try {
    const result = await processCommand(command);
    const done = { ok: true, commandId: id, type: command.type, completedAt: new Date().toISOString(), result: sanitize(result) };
    await writeFile(path.join(dirs.commandsDone, id + ".json"), JSON.stringify(done, null, 2) + "\n", { mode: 0o600 });
    await appendJsonl(files.control, controlRecord(command, "completed", done.result));
    await unlink(processing).catch(() => {});
  } catch (error) {
    const failureSample = await samplePage("command-failed", { refreshObserver: false, screenshot: false })
      .then(() => ({ ok: true, sampleSeq, valuesRedacted: true }))
      .catch((sampleError) => ({ ok: false, error: errorSummary(sampleError), valuesRedacted: true }));
    const failed = { ok: false, commandId: id, type: command.type, failedAt: new Date().toISOString(), error: errorSummary(error), failureSample };
    await writeFile(path.join(dirs.commandsFailed, id + ".json"), JSON.stringify(failed, null, 2) + "\n", { mode: 0o600 });
    await appendJsonl(files.control, controlRecord(command, "failed", { error: failed.error, failureSample }));
    await unlink(processing).catch(() => {});
  } finally {
    stopCommandSampler();
    activeCommandId = null;
    await writeHeartbeat({ status: terminalStatus });
  }
}

function startCommandActiveSampler(command) {
  const intervalMs = Math.max(1000, Number(sampleIntervalMs) || 5000);
  const heartbeatIntervalMs = Math.min(5000, intervalMs);
  let stopped = false;
  let timer = null;
  let heartbeatTimer = null;
  let inFlight = false;
  const heartbeat = () => {
    if (stopped) return;
    void writeHeartbeat({ status: terminalStatus, activeCommandId: command.id, activeCommandType: command.type, commandActive: true })
      .catch((error) => appendJsonl(files.errors, eventRecord("command-active-heartbeat-error", { commandId: command.id, commandType: command.type, error: errorSummary(error) })));
  };
  const schedule = () => {
    if (stopped) return;
    timer = setTimeout(tick, intervalMs);
    if (timer && typeof timer.unref === "function") timer.unref();
  };
  const tick = () => {
    if (stopped) return;
    if (inFlight) {
      schedule();
      return;
    }
    inFlight = true;
    samplePage("command-active", { refreshObserver: false, screenshot: false })
      .catch((error) => appendJsonl(files.errors, eventRecord("command-active-sample-error", { commandId: command.id, commandType: command.type, error: errorSummary(error) })))
      .finally(() => {
        inFlight = false;
        schedule();
      });
  };
  heartbeatTimer = setInterval(heartbeat, heartbeatIntervalMs);
  if (heartbeatTimer && typeof heartbeatTimer.unref === "function") heartbeatTimer.unref();
  schedule();
  return () => {
    stopped = true;
    if (timer) clearTimeout(timer);
    if (heartbeatTimer) clearInterval(heartbeatTimer);
  };
}

async function processCommand(command) {
  commandSeq += 1;
  activeCommandId = command.id;
  await writeHeartbeat({ status: "running", activeCommandId });
  await appendJsonl(files.control, controlRecord(command, "started", commandInputSummary(command)));
  switch (command.type) {
    case "login": return authenticate(context);
    case "loginAccount": return withObserverSync(await loginAccount(command), "loginAccount");
    case "logout": return withObserverSync(await logoutAccount(command), "logout");
    case "listSessions": return withObserverSync(await listSessions(command), "listSessions");
    case "switchSessions": return withObserverSync(await switchSessions(command), "switchSessions");
    case "preflight": return preflightSummary();
    case "goto": return withObserverSync(await gotoTarget(command.path || command.url || targetPath), "goto");
    case "newSession": return withObserverSync(await createSessionFromUi(), "newSession");
    case "sendPrompt": return withObserverSync(await sendPrompt(String(command.text || ""), {
      expectedAction: "turn",
      responsePath: "/v1/agent/chat",
      alternateResponsePaths: ["/v1/agent/chat/steer"],
      noActiveReason: "send-no-turn-composer",
      throwOnActionMismatch: true,
      expectedActionWaitMs: command.expectedActionWaitMs,
    }), "sendPrompt");
    case "steer": return withObserverSync(await sendPrompt(String(command.text || ""), { expectedAction: "steer", responsePath: "/v1/agent/chat/steer", noActiveReason: "steer-no-active-turn", expectedActionWaitMs: command.expectedActionWaitMs }), "steer");
    case "cancel": return withObserverSync(await cancelRunningTurn(), "cancel");
    case "selectProvider": return withObserverSync(await selectProvider(String(command.provider || command.value || command.text || "")), "selectProvider");
    case "clickSession": return withObserverSync(await clickSession(String(command.sessionId || command.value || "")), "clickSession");
    case "refreshCurrentSession": return withObserverSync(await refreshCurrentSession(command), "refreshCurrentSession");
    case "switchAwayAndBack": return withObserverSync(await switchAwayAndBack(command), "switchAwayAndBack");
    case "assertSessionInvariant": return withObserverSync(await assertSessionInvariant(command), "assertSessionInvariant");
    case "gotoProjectMdtodo": return withObserverSync(await gotoProjectMdtodo(), "gotoProjectMdtodo");
    case "openMdtodoSourceConfig": return openMdtodoSourceConfig(command);
    case "closeMdtodoSourceConfig": return closeMdtodoSourceConfig(command);
    case "configureMdtodoHwpodSource": return configureMdtodoHwpodSource(command);
    case "probeMdtodoSource": return probeMdtodoSource(command);
    case "reindexMdtodoSource": return reindexMdtodoSource(command);
    case "selectProjectSource": return selectProjectSource(command);
    case "selectMdtodoSource": return selectMdtodoSource(command);
    case "selectMdtodoFile": return selectMdtodoFile(command);
    case "selectMdtodoTask": return selectMdtodoTask(command);
    case "expandMdtodoTask": return expandMdtodoTask(command);
    case "openMdtodoReportPreview": return openMdtodoReportPreview(command);
    case "toggleMdtodoReportFullscreen": return toggleMdtodoReportFullscreen(command);
    case "editMdtodoTaskInline": return editMdtodoTaskInline(command);
    case "editMdtodoTaskTitle": return editMdtodoTaskTitle(command);
    case "editMdtodoTaskBody": return editMdtodoTaskBody(command);
    case "toggleMdtodoTaskStatus": return toggleMdtodoTaskStatus(command);
    case "addMdtodoRootTask": return addMdtodoRootTask(command);
    case "addMdtodoSubTask": return addMdtodoSubTask(command);
    case "continueMdtodoTask": return continueMdtodoTask(command);
    case "deleteMdtodoTask": return deleteMdtodoTask(command);
    case "launchWorkbenchFromTask": return withObserverSync(await launchWorkbenchFromTask(command), "launchWorkbenchFromTask");
    case "launchWorkbenchFromMdtodo": return withObserverSync(await launchWorkbenchFromMdtodo(command), "launchWorkbenchFromMdtodo");
    case "screenshot": return captureCommandScreenshot(command);
    case "mark": return { mark: truncate(command.label || command.text || "mark", 200), currentUrl: currentPageUrl(), pageId };
    case "stop": stopping = true; return { stopping: true, currentUrl: currentPageUrl(), pageId };
    default: throw new Error("unsupported observer command type: " + command.type);
  }
}

async function withObserverSync(result, reason) {
  return { ...result, observer: await syncObserverPageToControlSession(reason, result?.sessionId ?? null) };
}

async function syncObserverPageToControlSession(reason, explicitSessionId = null, options = {}) {
  if (!observerPage || observerPage.isClosed()) return { ok: false, reason, pageRole: "observer", pageId: observerPageId, failureKind: "observer-page-unavailable" };
  const forceRefresh = options?.forceRefresh === true;
  const navigationTimeoutMs = Number.isFinite(Number(options.navigationTimeoutMs)) ? Math.max(1, Number(options.navigationTimeoutMs)) : 45000;
  const readinessTimeoutMs = Number.isFinite(Number(options.readinessTimeoutMs)) ? Math.max(1, Number(options.readinessTimeoutMs)) : 15000;
  const hydrationTimeoutMs = Number.isFinite(Number(options.hydrationTimeoutMs)) ? Math.max(1, Number(options.hydrationTimeoutMs)) : 15000;
  const shortCircuitReadinessTimeoutMs = Number.isFinite(Number(options.shortCircuitReadinessTimeoutMs)) ? Math.max(1, Number(options.shortCircuitReadinessTimeoutMs)) : 1000;
  const shortCircuitHydrationTimeoutMs = Number.isFinite(Number(options.shortCircuitHydrationTimeoutMs)) ? Math.max(1, Number(options.shortCircuitHydrationTimeoutMs)) : 1000;
  const snapshot = await workbenchSessionSnapshot();
  const sessionId = explicitSessionId || snapshot?.activeSessionId || snapshot?.routeSessionId || routeSessionIdFromUrl(currentPageUrl());
  const target = sessionId ? "/workbench/sessions/" + encodeURIComponent(sessionId) : targetPath;
  const targetUrl = new URL(target, baseUrl).toString();
  const beforeUrl = pageUrl(observerPage);
  const beforeSessionId = routeSessionIdFromUrl(beforeUrl);
  const attempts = [];
  if (sessionId && beforeSessionId === sessionId && !forceRefresh) {
    const current = await observerSessionReadiness(targetUrl, sessionId, { readinessTimeoutMs: shortCircuitReadinessTimeoutMs, hydrationTimeoutMs: shortCircuitHydrationTimeoutMs });
    if (current.ok) return { ok: true, reason, changed: false, observerRoundTrip: false, sessionId, beforeUrl, afterUrl: beforeUrl, pageRole: "observer", pageId: observerPageId, pageEpoch: observerPageEpoch, readiness: current.readiness, hydration: current.hydration, valuesRedacted: true };
    attempts.push({ attempt: 0, ok: false, shortCircuitRejected: true, failureKind: current.failureKind, readiness: current.readiness, hydration: current.hydration, beforeUrl, afterUrl: pageUrl(observerPage), valuesRedacted: true });
  }
  const maxAttempts = Number.isFinite(Number(options?.maxAttempts)) ? Math.max(1, Number(options.maxAttempts)) : 2;
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
    const attemptBeforeUrl = pageUrl(observerPage);
    observerPageEpoch += 1;
    let status = null;
    let statusText = null;
    const response = await observerPage.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: navigationTimeoutMs }).catch((error) => ({ observerGotoError: errorSummary(error) }));
    if (response?.observerGotoError) {
      attempts.push({ attempt, ok: false, failureKind: navigationFailureKind(response.observerGotoError?.message || response.observerGotoError?.name || "observer-navigation-error"), beforeUrl: attemptBeforeUrl, afterUrl: pageUrl(observerPage), error: response.observerGotoError, valuesRedacted: true });
      if (attempt < maxAttempts && isRetryableNavigationError(response.observerGotoError?.message || response.observerGotoError?.name || "")) {
        await recreateObserverPageForNavigation("observer-goto-error", attempt).catch((resetError) => appendJsonl(files.errors, eventRecord("observer-page-reset-error", { commandId: activeCommandId, attempt, error: errorSummary(resetError) })));
        continue;
      }
      lastObserverRefreshAtMs = Date.now();
      return { ok: false, reason, changed: false, observerRoundTrip: forceRefresh, sessionId: sessionId ?? null, targetPath: target, beforeUrl, afterUrl: pageUrl(observerPage), pageRole: "observer", pageId: observerPageId, pageEpoch: observerPageEpoch, error: response.observerGotoError, attempts, valuesRedacted: true };
    }
    status = typeof response?.status === "function" ? response.status() : null;
    statusText = typeof response?.statusText === "function" ? response.statusText() : null;
    const readiness = await waitForTargetPageReady(observerPage, targetUrl, { timeoutMs: readinessTimeoutMs });
    if (!readiness.ok) {
      attempts.push({ attempt, ok: false, failureKind: readiness.reason || "observer-target-not-ready", beforeUrl: attemptBeforeUrl, afterUrl: pageUrl(observerPage), httpStatus: status, statusText, readiness, valuesRedacted: true });
      if (attempt < maxAttempts && observerReadinessRetryable(readiness)) {
        await recreateObserverPageForNavigation(readiness.reason || "observer-target-not-ready", attempt).catch((resetError) => appendJsonl(files.errors, eventRecord("observer-page-reset-error", { commandId: activeCommandId, attempt, error: errorSummary(resetError) })));
        continue;
      }
      lastObserverRefreshAtMs = Date.now();
      return { ok: false, reason, changed: true, observerRoundTrip: forceRefresh, sessionId: sessionId ?? null, targetPath: target, beforeUrl, afterUrl: pageUrl(observerPage), pageRole: "observer", pageId: observerPageId, pageEpoch: observerPageEpoch, httpStatus: status, statusText, readiness, hydration: null, attempts, failureKind: readiness.reason || "observer-target-not-ready", valuesRedacted: true };
    }
    if (!isWorkbenchPathname(safeUrlPath(targetUrl) || "")) {
      lastObserverRefreshAtMs = Date.now();
      attempts.push({ attempt, ok: true, beforeUrl: attemptBeforeUrl, afterUrl: pageUrl(observerPage), httpStatus: status, statusText, readiness, valuesRedacted: true });
      return { ok: true, reason, changed: true, observerRoundTrip: forceRefresh, sessionId: null, targetPath: target, beforeUrl, afterUrl: pageUrl(observerPage), pageRole: "observer", pageId: observerPageId, pageEpoch: observerPageEpoch, httpStatus: status, statusText, readiness, hydration: null, attempts, valuesRedacted: true };
    }
    const hydration = await waitForWorkbenchSessionHydrated(observerPage, sessionId, { timeoutMs: hydrationTimeoutMs });
    attempts.push({ attempt, ok: hydration.ok === true, failureKind: hydration.ok === true ? null : hydration.reason || "observer-session-hydration-failed", beforeUrl: attemptBeforeUrl, afterUrl: pageUrl(observerPage), httpStatus: status, statusText, readiness, hydration, valuesRedacted: true });
    if (hydration.ok === true) {
      lastObserverRefreshAtMs = Date.now();
      return { ok: true, reason, changed: true, observerRoundTrip: forceRefresh, sessionId: sessionId ?? null, targetPath: target, beforeUrl, afterUrl: pageUrl(observerPage), pageRole: "observer", pageId: observerPageId, pageEpoch: observerPageEpoch, httpStatus: status, statusText, readiness, hydration, attempts, failureKind: null, valuesRedacted: true };
    }
    if (attempt < maxAttempts && observerHydrationRetryable(hydration)) {
      await recreateObserverPageForNavigation(hydration.reason || "observer-session-hydration-failed", attempt).catch((resetError) => appendJsonl(files.errors, eventRecord("observer-page-reset-error", { commandId: activeCommandId, attempt, error: errorSummary(resetError) })));
      continue;
    }
    lastObserverRefreshAtMs = Date.now();
    return { ok: false, reason, changed: true, observerRoundTrip: forceRefresh, sessionId: sessionId ?? null, targetPath: target, beforeUrl, afterUrl: pageUrl(observerPage), pageRole: "observer", pageId: observerPageId, pageEpoch: observerPageEpoch, httpStatus: status, statusText, readiness, hydration, attempts, failureKind: hydration.reason || "observer-session-hydration-failed", valuesRedacted: true };
  }
  lastObserverRefreshAtMs = Date.now();
  return { ok: false, reason, changed: true, observerRoundTrip: forceRefresh, sessionId: sessionId ?? null, targetPath: target, beforeUrl, afterUrl: pageUrl(observerPage), pageRole: "observer", pageId: observerPageId, pageEpoch: observerPageEpoch, attempts, failureKind: "observer-sync-retry-exhausted", valuesRedacted: true };
}

async function observerSessionReadiness(targetUrl, sessionId, options = {}) {
  const readiness = await waitForTargetPageReady(observerPage, targetUrl, { timeoutMs: options.readinessTimeoutMs ?? 1000 });
  if (!readiness.ok) return { ok: false, failureKind: readiness.reason || "observer-target-not-ready", readiness, hydration: null, valuesRedacted: true };
  if (!isWorkbenchPathname(safeUrlPath(targetUrl) || "")) return { ok: true, readiness, hydration: null, valuesRedacted: true };
  const hydration = await waitForWorkbenchSessionHydrated(observerPage, sessionId, { timeoutMs: options.hydrationTimeoutMs ?? 1000 });
  return { ok: hydration.ok === true, failureKind: hydration.ok === true ? null : hydration.reason || "observer-session-hydration-failed", readiness, hydration, valuesRedacted: true };
}

function observerReadinessRetryable(readiness) {
  const reason = String(readiness?.reason || "");
  const snapshot = readiness?.snapshot || {};
  return /workbench-app-not-ready|observer-target-not-ready/iu.test(reason)
    || snapshot.workbenchShellVisible === false
    || snapshot.sessionRailPresent === false && snapshot.commandInputPresent === false;
}

function observerHydrationRetryable(hydration) {
  const reason = String(hydration?.reason || "");
  return /observer-session-hydration-timeout|observer-session-hydration-failed|active-session-not-hydrated|route-session-not-hydrated/iu.test(reason);
}

async function recreateObserverPageForNavigation(reason, attempt) {
  const before = pageUrl(observerPage);
  if (observerPage && !observerPage.isClosed()) await observerPage.close().catch(() => {});
  observerPage = await context.newPage();
  attachPassiveListeners(observerPage, "observer", observerPageId);
  await appendJsonl(files.control, eventRecord("observer-page-recreated", { reason, attempt, beforeUrl: before, afterUrl: pageUrl(observerPage), pageRole: "observer", pageId: observerPageId, pageEpoch: observerPageEpoch, valuesRedacted: true }));
}

async function waitForWorkbenchSessionHydrated(targetPage, sessionId, options = {}) {
  const timeoutMs = Number.isFinite(Number(options.timeoutMs)) ? Number(options.timeoutMs) : 15000;
  const started = Date.now();
  const deadline = started + Math.max(1, timeoutMs);
  let last = null;
  while (Date.now() <= deadline) {
    last = await workbenchSessionSnapshot(targetPage);
    const expected = String(sessionId || "").trim();
    const observedPath = safeUrlPath(last?.url || pageUrl(targetPage));
    const routeOk = expected ? last?.routeSessionId === expected : isWorkbenchPathname(observedPath || "");
    const activeOk = expected ? last?.activeSessionId === expected : isWorkbenchPathname(observedPath || "");
    if (routeOk && activeOk) return { ok: true, durationMs: Date.now() - started, snapshot: last, valuesRedacted: true };
    await targetPage.waitForTimeout(250).catch(() => {});
  }
  return { ok: false, durationMs: Date.now() - started, snapshot: last, reason: "observer-session-hydration-timeout", expectedSessionId: sessionId || null, valuesRedacted: true };
}

async function maybeRefreshObserverPage(reason) {
  if (!observerPage || observerPage.isClosed()) return null;
  if (!observerRefreshIntervalMs || observerRefreshIntervalMs <= 0) return null;
  if (Date.now() - lastObserverRefreshAtMs < observerRefreshIntervalMs) return null;
  const result = await syncObserverPageToControlSession("observer-periodic-refresh", null, { forceRefresh: true });
  await appendJsonl(files.control, eventRecord("observer-periodic-refresh", {
    pageRole: "observer",
    pageId: observerPageId,
    reason,
    intervalMs: observerRefreshIntervalMs,
    result,
    valuesRedacted: true
  }));
  return result;
}

async function runControlCommand(command, fn) {
  activeCommandId = command.id;
  commandSeq += 1;
  const beforeUrl = currentPageUrl();
  const started = Date.now();
  await appendJsonl(files.control, controlRecord(command, "started", { beforeUrl, input: commandInputSummary(command) }));
  try {
    const result = await fn();
    await appendJsonl(files.control, controlRecord(command, "completed", { beforeUrl, afterUrl: currentPageUrl(), durationMs: Date.now() - started, result: sanitize(result) }));
    return result;
  } catch (error) {
    let failurePageProvenance = null;
    let failureScreenshot = null;
    try {
      failurePageProvenance = compactPageProvenance(await refreshPageProvenance("command-failed", null));
    } catch (captureError) {
      await appendJsonl(files.errors, eventRecord("failure-provenance-error", { commandId: command.id, pageRole: "control", pageId, error: errorSummary(captureError) }));
    }
    try {
      failureScreenshot = await captureScreenshot("command-failed", "jpeg");
    } catch (captureError) {
      await appendJsonl(files.errors, eventRecord("failure-screenshot-error", { commandId: command.id, pageRole: "control", pageId, error: errorSummary(captureError) }));
    }
    await appendJsonl(files.control, controlRecord(command, "failed", { beforeUrl, afterUrl: currentPageUrl(), durationMs: Date.now() - started, error: errorSummary(error), failurePageProvenance, failureScreenshot }));
    throw error;
  } finally {
    activeCommandId = null;
  }
}

async function authenticate(browserContext) {
  const loginUrl = new URL("/auth/login", baseUrl).toString();
  const attempts = [];
  const maxAttempts = authLoginMaxAttempts;
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
    const retryDelayMs = authRetryDelayMs(attempt, maxAttempts);
    const retryLabel = attempt + "/" + maxAttempts;
    await writeHeartbeat({ status: terminalStatus, auth: { phase: "api-login", retryAttempt: attempt, retryMaxAttempts: maxAttempts, lastRetryLabel: retryLabel, retryDelayMs: 0, retryExhausted: false, valuesRedacted: true } }).catch(() => {});
    try {
      const response = await pageAuthLogin(browserContext, loginUrl);
      const cookieState = await readAuthCookieState(browserContext);
      const retryable = isRetryableAuthStatus(response.status);
      const item = {
        attempt,
        retryAttempt: attempt,
        retryMaxAttempts: maxAttempts,
        retryLabel,
        retryDelayMs: retryable && attempt < maxAttempts ? retryDelayMs : 0,
        requestTimeoutMs: authLoginRequestTimeoutMs,
        method: "api",
        status: response.status,
        statusText: response.statusText,
        retryable,
        cookiePresent: cookieState.cookiePresent,
        cookieNames: cookieState.cookieNames,
        valuesRedacted: true,
      };
      attempts.push(item);
      await writeHeartbeat({ status: terminalStatus, auth: { phase: "api-login", lastRetryLabel: item.retryLabel, retryAttempt: item.retryAttempt, retryMaxAttempts: item.retryMaxAttempts, retryDelayMs: item.retryDelayMs, lastStatus: item.status, lastStatusText: item.statusText, retryable: item.retryable, cookiePresent: item.cookiePresent, retryExhausted: false, valuesRedacted: true } }).catch(() => {});
      if (response.ok && cookieState.cookiePresent) {
        return {
          ok: true,
          method: "api",
          loginPath: new URL(loginUrl).pathname,
          status: response.status,
          statusText: response.statusText,
          cookiePresent: true,
          cookieNames: cookieState.cookieNames,
          attempts,
          retryCount: attempt - 1,
          retryMaxAttempts: maxAttempts,
          lastRetryLabel: attempt + "/" + maxAttempts,
          retryExhausted: false,
          retryable: false,
          valuesRedacted: true,
        };
      }
      if (!retryable) break;
    } catch (error) {
      const retryable = isRetryableAuthError(error);
      attempts.push({
        attempt,
        retryAttempt: attempt,
        retryMaxAttempts: maxAttempts,
        retryLabel,
        retryDelayMs: retryable && attempt < maxAttempts ? retryDelayMs : 0,
        requestTimeoutMs: authLoginRequestTimeoutMs,
        method: "api",
        status: 0,
        statusText: "request-error",
        retryable,
        error: error && error.message ? truncate(error.message, 500) : truncate(String(error), 500),
        cookiePresent: false,
        cookieNames: [],
        valuesRedacted: true,
      });
      const item = attempts[attempts.length - 1] || null;
      await writeHeartbeat({ status: terminalStatus, auth: { phase: "api-login", lastRetryLabel: item?.retryLabel || retryLabel, retryAttempt: attempt, retryMaxAttempts: maxAttempts, retryDelayMs: item?.retryDelayMs ?? 0, lastStatus: item?.status ?? 0, lastStatusText: item?.statusText ?? "request-error", retryable, cookiePresent: false, retryExhausted: false, lastError: item?.error || null, valuesRedacted: true } }).catch(() => {});
      if (!retryable) break;
    }
    if (attempt < maxAttempts && attempts[attempts.length - 1]?.retryable === true) await sleep(retryDelayMs);
  }
  const cookieState = await readAuthCookieState(browserContext);
  const last = attempts[attempts.length - 1] || null;
  const retryable = attempts.some((attempt) => attempt && attempt.retryable === true);
  const failure = {
    ok: false,
    method: "api",
    loginPath: new URL(loginUrl).pathname,
    status: typeof last?.status === "number" ? last.status : 0,
    statusText: typeof last?.statusText === "string" ? last.statusText : "api-login-failed",
    cookiePresent: cookieState.cookiePresent,
    cookieNames: cookieState.cookieNames,
    attempts,
    retryCount: Math.max(0, attempts.length - 1),
    retryMaxAttempts: maxAttempts,
    lastRetryLabel: last?.retryLabel || null,
    retryExhausted: retryable && attempts.length >= maxAttempts,
    retryable,
    lastError: last?.error || null,
    valuesRedacted: true,
  };
  await writeHeartbeat({ status: terminalStatus, auth: { phase: "api-login", lastRetryLabel: failure.lastRetryLabel, retryAttempt: attempts.length, retryMaxAttempts: maxAttempts, retryDelayMs: 0, lastStatus: failure.status, lastStatusText: failure.statusText, retryable: failure.retryable, cookiePresent: failure.cookiePresent, retryExhausted: failure.retryExhausted, lastError: failure.lastError, valuesRedacted: true } }).catch(() => {});
  const error = new Error(authFailureMessage(failure));
  error.webProbeAuth = failure;
  throw error;
}

async function pageAuthLogin(browserContext, loginUrl, credential = { username, password }) {
  if (!browserContext?.request) throw new Error("auth browser context request is not ready");
  const response = await browserContext.request.post(loginUrl, {
    data: { username: credential.username, password: credential.password },
    headers: { accept: "application/json", "content-type": "application/json" },
    timeout: authLoginRequestTimeoutMs,
  });
  await response.text().catch(() => "");
  return {
    ok: response.ok(),
    status: response.status(),
    statusText: response.statusText() || "",
  };
}

async function loginAccount(command) {
  const accountId = requiredAccountId(command, ["accountId", "account", "value", "text"]);
  const credential = credentialForAccount(accountId);
  const loginUrl = new URL("/auth/login", baseUrl).toString();
  const before = await accountSessionSnapshot();
  const attempts = [];
  let response = null;
  let cookieState = null;
  const maxAttempts = authLoginMaxAttempts;
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
    const retryDelayMs = authRetryDelayMs(attempt, maxAttempts);
    const retryLabel = attempt + "/" + maxAttempts;
    try {
      response = await pageAuthLogin(context, loginUrl, credential);
      cookieState = await readAuthCookieState(context);
      const retryable = isRetryableAuthStatus(response.status);
      attempts.push({
        attempt,
        retryAttempt: attempt,
        retryMaxAttempts: maxAttempts,
        retryLabel,
        retryDelayMs: retryable && attempt < maxAttempts ? retryDelayMs : 0,
        requestTimeoutMs: authLoginRequestTimeoutMs,
        method: "api",
        status: response.status,
        statusText: response.statusText,
        retryable,
        cookiePresent: cookieState.cookiePresent,
        cookieNames: cookieState.cookieNames,
        credentialSource: credential.source,
        valuesRedacted: true,
      });
      if (response.ok && cookieState.cookiePresent) break;
      if (!retryable) break;
    } catch (error) {
      const retryable = isRetryableAuthError(error);
      attempts.push({
        attempt,
        retryAttempt: attempt,
        retryMaxAttempts: maxAttempts,
        retryLabel,
        retryDelayMs: retryable && attempt < maxAttempts ? retryDelayMs : 0,
        requestTimeoutMs: authLoginRequestTimeoutMs,
        method: "api",
        status: 0,
        statusText: "request-error",
        retryable,
        error: error && error.message ? truncate(error.message, 500) : truncate(String(error), 500),
        cookiePresent: false,
        cookieNames: [],
        credentialSource: credential.source,
        valuesRedacted: true,
      });
      response = { ok: false, status: 0, statusText: "request-error" };
      cookieState = await readAuthCookieState(context).catch(() => ({ cookiePresent: false, cookieNames: [] }));
      if (!retryable) break;
    }
    if (attempt < maxAttempts && attempts[attempts.length - 1]?.retryable === true) await sleep(retryDelayMs);
  }
  response = response ?? { ok: false, status: 0, statusText: "api-login-failed" };
  cookieState = cookieState ?? await readAuthCookieState(context);
  if (!response.ok || !cookieState.cookiePresent) {
    const error = new Error("loginAccount failed for accountId=" + accountId + " status=" + response.status + " " + (response.statusText || ""));
    const retryable = attempts.some((item) => item && item.retryable === true);
    error.details = {
      accountId,
      status: response.status,
      statusText: response.statusText,
      cookiePresent: cookieState.cookiePresent,
      credentialSource: credential.source,
      attempts,
      retryCount: Math.max(0, attempts.length - 1),
      retryMaxAttempts: maxAttempts,
      lastRetryLabel: attempts[attempts.length - 1]?.retryLabel || null,
      retryExhausted: retryable && attempts.length >= maxAttempts,
      retryable,
      valuesRedacted: true,
    };
    throw error;
  }
  const target = isWorkbenchPathname(safeUrlPath(currentPageUrl()) || "") ? safeUrlPath(currentPageUrl()) : targetPath;
  const navigation = await gotoTarget(target || targetPath);
  const after = await accountSessionSnapshot();
  return { ok: true, type: "loginAccount", accountId, credentialSource: credential.source, before, after, navigation, cookiePresent: cookieState.cookiePresent, cookieNames: cookieState.cookieNames, valuesRedacted: true };
}

async function logoutAccount(command = {}) {
  const accountId = commandValue(command, ["accountId", "account", "value", "text"]) || null;
  const before = await accountSessionSnapshot();
  const logoutUrl = new URL("/logout", baseUrl).toString();
  const response = await page.evaluate(async (input) => {
    const res = await fetch(input.logoutUrl, { method: "POST", headers: { accept: "application/json" }, credentials: "include" });
    await res.text().catch(() => "");
    return { ok: res.ok, status: res.status, statusText: res.statusText || "" };
  }, { logoutUrl });
  await context.clearCookies().catch(() => {});
  const cookieState = await readAuthCookieState(context);
  const afterUrl = await page.goto(new URL("/auth/login", baseUrl).toString(), { waitUntil: "domcontentloaded", timeout: 15000 }).then(() => currentPageUrl()).catch(() => currentPageUrl());
  const result = { ok: response.ok || response.status === 401 || !cookieState.cookiePresent, type: "logout", accountId, status: response.status, statusText: response.statusText, before, after: { url: afterUrl, cookiePresent: cookieState.cookiePresent, cookieNames: cookieState.cookieNames, valuesRedacted: true }, valuesRedacted: true };
  if (!result.ok) {
    const error = new Error("logout failed status=" + response.status + " " + (response.statusText || ""));
    error.details = result;
    throw error;
  }
  return result;
}

async function listSessions(command = {}) {
  const accountId = commandValue(command, ["accountId", "account", "value", "text"]) || null;
  if (!isWorkbenchPathname(safeUrlPath(currentPageUrl()) || "")) await gotoTarget(targetPath);
  const snapshot = await workbenchSessionSnapshot();
  const sessions = await page.evaluate(() => {
    const seen = new Set();
    const rows = [];
    for (const element of Array.from(document.querySelectorAll("[data-session-id], .session-tab, a[href*='/workbench/sessions/']"))) {
      const sessionId = element.getAttribute("data-session-id") || (element.getAttribute("href") || "").match(/\/workbench\/sessions\/([^/?#]+)/)?.[1] || "";
      if (!sessionId || seen.has(sessionId)) continue;
      seen.add(sessionId);
      rows.push({
        sessionId,
        active: element.getAttribute("data-active") === "true" || element.getAttribute("aria-selected") === "true",
        status: element.getAttribute("data-status") || null,
        conversationId: element.getAttribute("data-conversation-id") || null,
      });
    }
    return rows.slice(0, 50);
  }).catch(() => []);
  return { ok: true, type: "listSessions", accountId, sessionCount: sessions.length, activeSessionId: snapshot?.activeSessionId || snapshot?.routeSessionId || null, sessions, snapshot, valuesRedacted: true };
}

async function switchSessions(command) {
  const fromAccountId = commandValue(command, ["fromAccountId", "fromAccount", "accountId"]);
  const toAccountId = requiredAccountId(command, ["toAccountId", "toAccount", "value", "text"]);
  const before = await accountSessionSnapshot();
  if (fromAccountId) {
    const beforeAccount = before.accountId || null;
    await appendJsonl(files.control, eventRecord("switchSessions-from-account", { fromAccountId, observedAccountId: beforeAccount, valuesRedacted: true }));
  }
  const logout = await logoutAccount({ ...command, accountId: fromAccountId || null });
  const login = await loginAccount({ ...command, accountId: toAccountId });
  const sessions = await listSessions({ ...command, accountId: toAccountId });
  return { ok: login.ok === true && sessions.ok === true, type: "switchSessions", fromAccountId: fromAccountId || null, toAccountId, before, logout, login, sessions, valuesRedacted: true };
}

async function accountSessionSnapshot() {
  const cookieState = await readAuthCookieState(context);
  const workbench = await workbenchSessionSnapshot().catch(() => null);
  return {
    url: currentPageUrl(),
    path: safeUrlPath(currentPageUrl()),
    cookiePresent: cookieState.cookiePresent,
    cookieNames: cookieState.cookieNames,
    activeSessionId: workbench?.activeSessionId || null,
    routeSessionId: workbench?.routeSessionId || null,
    tabCount: workbench?.tabCount ?? null,
    messageCount: workbench?.messageCount ?? null,
    valuesRedacted: true,
  };
}

function requiredAccountId(command, keys) {
  const accountId = commandValue(command, keys);
  if (!isSafeAccountId(accountId)) throw new Error(command.type + " requires --account-id using lowercase account id");
  return accountId;
}

function credentialForAccount(accountId) {
  if (accountId === "bootstrap-admin" || accountId === "admin") {
    if (!password) throw new Error("loginAccount accountId=" + accountId + " missing HWLAB_WEB_PASS");
    return { username, password, source: "HWLAB_WEB_USER/HWLAB_WEB_PASS", valuesRedacted: true };
  }
  const env = accountCredentialEnvCandidates(accountId);
  for (const jsonKey of env.jsonKeys) {
    const raw = process.env[jsonKey];
    if (!raw) continue;
    const parsed = parseCredentialJson(raw);
    if (parsed !== null) return { ...parsed, source: jsonKey, valuesRedacted: true };
  }
  for (const pair of env.pairs) {
    const user = process.env[pair.userKey];
    const pass = process.env[pair.passKey];
    if (user && pass) return { username: user, password: pass, source: pair.userKey + "/" + pair.passKey, valuesRedacted: true };
  }
  throw new Error("loginAccount missing credential material for accountId=" + accountId + "; expected one of " + [...env.jsonKeys, ...env.pairs.flatMap((item) => [item.userKey, item.passKey])].join(","));
}

function accountCredentialEnvCandidates(accountId) {
  const segment = accountId.toUpperCase().replace(/[^A-Z0-9]+/gu, "_").replace(/^_+|_+$/gu, "");
  return {
    jsonKeys: [
      "HWLAB_WEB_" + segment + "_JSON",
      "HWLAB_WEB_ACCOUNT_" + segment + "_JSON",
    ],
    pairs: [
      { userKey: "HWLAB_WEB_" + segment + "_USER", passKey: "HWLAB_WEB_" + segment + "_PASS" },
      { userKey: "HWLAB_WEB_ACCOUNT_" + segment + "_USER", passKey: "HWLAB_WEB_ACCOUNT_" + segment + "_PASS" },
    ],
  };
}

function parseCredentialJson(raw) {
  try {
    const parsed = JSON.parse(raw);
    if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
    const user = typeof parsed.username === "string" ? parsed.username : typeof parsed.user === "string" ? parsed.user : typeof parsed.email === "string" ? parsed.email : "";
    const pass = typeof parsed.password === "string" ? parsed.password : typeof parsed.pass === "string" ? parsed.pass : "";
    if (!user || !pass) return null;
    return { username: user, password: pass, valuesRedacted: true };
  } catch {
    return null;
  }
}

function isSafeAccountId(value) {
  return /^[a-z0-9][a-z0-9-]{1,80}$/u.test(String(value || ""));
}

function publicAuth(value) {
  if (!value) return null;
  return {
    ok: value.ok === true,
    method: value.method,
    status: value.status,
    cookiePresent: value.cookiePresent === true,
    cookieNames: value.cookieNames || [],
    retryCount: value.retryCount ?? null,
    retryMaxAttempts: value.retryMaxAttempts ?? null,
    lastRetryLabel: value.lastRetryLabel ?? null,
    retryExhausted: value.retryExhausted === true,
    valuesRedacted: true
  };
}

async function readAuthCookieState(browserContext) {
  const cookies = await browserContext.cookies(baseUrl);
  const cookieNames = cookies.map((cookie) => cookie.name).sort();
  return {
    cookiePresent: cookieNames.includes("hwlab_session") || cookieNames.some((name) => /session|auth|token/iu.test(name)),
    cookieNames: cookieNames.filter((name) => /session|auth|token/iu.test(name)),
  };
}

function isRetryableAuthStatus(status) {
  return status === 0 || status === 408 || status === 409 || status === 425 || status === 429 || status >= 500;
}

function isRetryableAuthError(error) {
  const message = error && error.message ? String(error.message) : String(error || "");
  return /AbortError|EAI_AGAIN|ETIMEDOUT|ECONNRESET|ECONNREFUSED|ECONNABORTED|socket hang up|ERR_NETWORK_CHANGED|fetch failed|failed to fetch|network|timeout|aborted/iu.test(message);
}

function authRetryDelayMs(attempt, maxAttempts) {
  return attempt < maxAttempts ? Math.min(authLoginMaxDelayMs, authLoginInitialDelayMs * (2 ** (attempt - 1))) : 0;
}

function authFailureMessage(failure) {
  const last = Array.isArray(failure.attempts) && failure.attempts.length > 0 ? failure.attempts[failure.attempts.length - 1] : null;
  const retry = failure.lastRetryLabel ? " retry=" + failure.lastRetryLabel : "";
  const exhausted = failure.retryExhausted ? " exhausted=true" : "";
  const status = last ? " status=" + (last.status ?? "-") + " " + (last.statusText ?? "") : "";
  const error = last?.error ? " error=" + truncate(last.error, 160) : "";
  return ("auth login failed:" + retry + exhausted + status + error).trim();
}

function proxyConfigFromEnv(targetBaseUrl) {
  if (browserProxyMode === "direct") return null;
  let target;
  try {
    target = new URL(targetBaseUrl);
  } catch {
    return null;
  }
  const noProxy = process.env.NO_PROXY || process.env.no_proxy || "";
  if (noProxyMatches(target.hostname, noProxy)) return null;
  const raw = target.protocol === "https:"
    ? process.env.HTTPS_PROXY || process.env.https_proxy || process.env.ALL_PROXY || process.env.all_proxy || process.env.HTTP_PROXY || process.env.http_proxy || ""
    : process.env.HTTP_PROXY || process.env.http_proxy || process.env.ALL_PROXY || process.env.all_proxy || process.env.HTTPS_PROXY || process.env.https_proxy || "";
  if (!raw) return null;
  return { server: raw };
}

function chromiumLaunchOptionsForProxy(proxy) {
  const base = { env: browserProcessEnvWithoutProxy() };
  if (proxy === null) return { ...base, args: ["--no-proxy-server"] };
  return { ...base, proxy };
}

function browserProcessEnvWithoutProxy() {
  const blocked = new Set(["HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY", "NO_PROXY", "http_proxy", "https_proxy", "all_proxy", "no_proxy"]);
  const env = {};
  for (const [key, value] of Object.entries(process.env)) {
    if (!blocked.has(key) && value !== undefined) env[key] = value;
  }
  return env;
}

function noProxyMatches(hostname, rawList) {
  const host = String(hostname || "").toLowerCase();
  if (!host) return false;
  return String(rawList || "").split(",").some((raw) => {
    let item = raw.trim().toLowerCase();
    if (!item) return false;
    if (item === "*") return true;
    item = item.replace(/^\*\./u, ".");
    const portIndex = item.lastIndexOf(":");
    if (portIndex > -1 && !item.includes("]")) item = item.slice(0, portIndex);
    if (item.startsWith(".")) return host === item.slice(1) || host.endsWith(item);
    return host === item;
  });
}

function publicNetwork(proxy) {
  return {
    proxy: proxy === null ? { enabled: false, source: "env", valuesRedacted: true } : {
      enabled: true,
      source: "env",
      server: publicProxyServer(proxy.server),
      valuesRedacted: true,
    },
    browser: {
      proxyMode: proxy === null ? "direct-no-proxy-server" : "explicit-playwright-proxy",
      requestedProxyMode: browserProxyMode,
      proxyEnvCleared: true,
      valuesRedacted: true,
    },
    valuesRedacted: true,
  };
}

function parseBrowserProxyMode(raw) {
  if (raw === "auto" || raw === "direct") return raw;
  return "auto";
}

function publicProxyServer(raw) {
  try {
    const parsed = new URL(String(raw || ""));
    parsed.username = "";
    parsed.password = "";
    const value = parsed.toString();
    if (parsed.pathname === "/" && parsed.search === "" && parsed.hash === "") return value.replace(/\/$/u, "");
    return value;
  } catch {
    return String(raw || "").replace(/\/\/[^/@]+@/u, "//[redacted]@");
  }
}

async function gotoTarget(rawTarget, options = {}) {
  const target = new URL(String(rawTarget || targetPath), baseUrl).toString();
  const beforeUrl = currentPageUrl();
  const attempts = [];
  const maxAttempts = Number.isFinite(Number(options.maxAttempts)) ? Math.max(1, Number(options.maxAttempts)) : navigationMaxAttempts;
  const navigationTimeoutMs = Number.isFinite(Number(options.navigationTimeoutMs)) ? Math.max(1, Number(options.navigationTimeoutMs)) : 45000;
  const readinessTimeoutMs = Number.isFinite(Number(options.readinessTimeoutMs)) ? Math.max(1, Number(options.readinessTimeoutMs)) : 15000;
  const settleMs = Number.isFinite(Number(options.settleMs)) ? Math.max(0, Number(options.settleMs)) : 1000;
  const lateReadinessTimeoutMs = Number.isFinite(Number(options.lateReadinessTimeoutMs)) ? Math.max(0, Number(options.lateReadinessTimeoutMs)) : 5000;
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
    try {
      const response = await page.goto(target, { waitUntil: "domcontentloaded", timeout: navigationTimeoutMs });
      if (settleMs > 0) await page.waitForTimeout(settleMs).catch(() => {});
      const httpStatus = response ? response.status() : null;
      const readiness = await waitForTargetPageReady(page, target, { timeoutMs: readinessTimeoutMs });
      if (!readiness.ok) {
        const pageProvenance = await refreshPageProvenance("goto-degraded", httpStatus).catch(() => null);
        attempts.push({ attempt, ok: false, degraded: true, httpStatus, readiness, failureKind: readiness.reason || "workbench-app-not-ready" });
        return { beforeUrl, afterUrl: currentPageUrl(), httpStatus, pageId, degraded: true, degradedReason: readiness.reason || "workbench-app-not-ready", pageProvenance: compactPageProvenance(pageProvenance), readiness, attempts };
      }
      const pageProvenance = await refreshPageProvenance("goto", httpStatus);
      attempts.push({ attempt, ok: true, httpStatus, readiness });
      return { beforeUrl, afterUrl: currentPageUrl(), httpStatus, pageId, pageProvenance: compactPageProvenance(pageProvenance), readiness, attempts };
    } catch (error) {
      const message = error instanceof Error ? error.message : String(error);
      attempts.push({ attempt, ok: false, failureKind: navigationFailureKind(message), message: redactErrorMessage(message), readiness: error?.navigationReadiness ?? null });
      if (lateReadinessTimeoutMs > 0 && /workbench-app-not-ready|navigation timeout|page\.goto:\s*timeout|timeout\s+\d+ms\s+exceeded/iu.test(message)) {
        const lateReadiness = await waitForTargetPageReady(page, target, { timeoutMs: lateReadinessTimeoutMs }).catch(() => null);
        if (lateReadiness?.ok) {
          const pageProvenance = await refreshPageProvenance("goto-late-ready", null);
          attempts.push({ attempt, ok: true, lateReady: true, httpStatus: null, readiness: lateReadiness });
          return { beforeUrl, afterUrl: currentPageUrl(), httpStatus: null, pageId, pageProvenance: compactPageProvenance(pageProvenance), readiness: lateReadiness, attempts };
        }
      }
      if (attempt >= maxAttempts || !isRetryableNavigationError(message)) {
        throw Object.assign(new Error(message), { attempts, target });
      }
      if (!observerPage) {
        await recreateAuthenticatedContextForNavigation("retryable-navigation-" + navigationFailureKind(message), attempt).catch((resetError) => appendJsonl(files.errors, eventRecord("navigation-context-reset-error", { commandId: activeCommandId, attempt, error: errorSummary(resetError) })));
      } else {
        await recreateControlPageForNavigation("retryable-navigation-" + navigationFailureKind(message), attempt).catch((resetError) => appendJsonl(files.errors, eventRecord("navigation-page-reset-error", { commandId: activeCommandId, attempt, error: errorSummary(resetError) })));
      }
      await page.waitForTimeout(1500 * attempt).catch(() => {});
    }
  }
  return { beforeUrl, afterUrl: currentPageUrl(), httpStatus: null, pageId, attempts };
}

async function recreateControlPageForNavigation(reason, attempt) {
  const before = currentPageUrl();
  if (page && !page.isClosed()) await withHardTimeout(page.close(), 3000, "control page close exceeded 3000ms").catch((error) => appendJsonl(files.errors, eventRecord("control-page-close-timeout", { reason, attempt, error: errorSummary(error), pageRole: "control", pageId, pageEpoch: controlPageEpoch })));
  controlPageEpoch += 1;
  page = await context.newPage();
  attachPassiveListeners(page, "control", pageId);
  currentPageProvenance = null;
  await appendJsonl(files.control, eventRecord("page-recreated", { reason, attempt, beforeUrl: before, afterUrl: currentPageUrl(), pageRole: "control", pageId, pageEpoch: controlPageEpoch, valuesRedacted: true }));
}

async function recreateAuthenticatedContextForNavigation(reason, attempt) {
  const before = currentPageUrl();
  if (page && !page.isClosed()) await page.close().catch(() => {});
  if (observerPage && !observerPage.isClosed()) await observerPage.close().catch(() => {});
  observerPage = null;
  if (context) await context.close().catch(() => {});
  context = await browser.newContext({ viewport, ...(playwrightProxy === null ? {} : { proxy: playwrightProxy }) });
  auth = await authenticate(context);
  page = await context.newPage();
  attachPassiveListeners(page, "control", pageId);
  currentPageProvenance = null;
  await appendJsonl(files.control, eventRecord("context-recreated", { reason, attempt, beforeUrl: before, afterUrl: currentPageUrl(), pageRole: "control", pageId, auth: publicAuth(auth), valuesRedacted: true }));
}

async function refreshPageProvenance(reason, httpStatus = null) {
  if (!page || page.isClosed()) return currentPageProvenance;
  const observed = await page.evaluate(() => {
    const assetPath = (raw) => {
      if (!raw) return null;
      try {
        const url = new URL(raw, location.href);
        const keys = Array.from(url.searchParams.keys()).sort();
        return url.pathname + (keys.length > 0 ? "?keys=" + keys.join(",") : "");
      } catch {
        return null;
      }
    };
    const meta = Array.from(document.querySelectorAll("meta[name], meta[property]")).map((element) => ({
      key: String(element.getAttribute("name") || element.getAttribute("property") || "").slice(0, 120),
      content: String(element.getAttribute("content") || "").slice(0, 200),
    })).filter((item) => item.key).sort((a, b) => a.key.localeCompare(b.key));
    const navigation = performance.getEntriesByType("navigation")[0] || null;
    return {
      url: location.href,
      path: location.pathname,
      title: document.title,
      readyState: document.readyState,
      timeOrigin: Math.round(performance.timeOrigin || 0),
      navigationStartTime: navigation ? Math.round(navigation.startTime) : null,
      scripts: Array.from(document.scripts).map((element) => assetPath(element.src)).filter(Boolean).sort(),
      stylesheets: Array.from(document.querySelectorAll('link[rel~="stylesheet"][href]')).map((element) => assetPath(element.href)).filter(Boolean).sort(),
      meta,
    };
  }).catch((error) => ({ error: errorSummary(error), url: currentPageUrl(), path: null, scripts: [], stylesheets: [], meta: [] }));
  pageLoadSeq += 1;
  currentPageProvenance = normalizePageProvenance(observed, { reason, httpStatus, pageLoadSeq });
  await appendJsonl(files.control, eventRecord("page-provenance", { reason, httpStatus, pageProvenance: compactPageProvenance(currentPageProvenance) }));
  return currentPageProvenance;
}

function normalizePageProvenance(value, options = {}) {
  const scripts = Array.isArray(value?.scripts) ? value.scripts.map(String).filter(Boolean) : [];
  const stylesheets = Array.isArray(value?.stylesheets) ? value.stylesheets.map(String).filter(Boolean) : [];
  const meta = Array.isArray(value?.meta) ? value.meta.map((item) => ({
    key: String(item?.key || "").slice(0, 120),
    contentHash: sha256Text(String(item?.content || "")),
  })).filter((item) => item.key) : [];
  const fingerprintInput = JSON.stringify({ scripts, stylesheets, meta });
  return {
    pageLoadSeq: options.pageLoadSeq ?? pageLoadSeq,
    reason: options.reason || "sample",
    observedAt: new Date().toISOString(),
    urlPath: safeUrlPath(value?.url || currentPageUrl()),
    documentPath: value?.path || null,
    titleHash: sha256Text(String(value?.title || "")),
    documentReadyState: value?.readyState || null,
    timeOrigin: Number.isFinite(Number(value?.timeOrigin)) ? Number(value.timeOrigin) : null,
    navigationStartTime: Number.isFinite(Number(value?.navigationStartTime)) ? Number(value.navigationStartTime) : null,
    httpStatus: options.httpStatus ?? null,
    assetFingerprint: sha256Text(fingerprintInput),
    scriptCount: scripts.length,
    stylesheetCount: stylesheets.length,
    metaCount: meta.length,
    scripts: scripts.slice(0, 30),
    stylesheets: stylesheets.slice(0, 30),
    meta: meta.slice(0, 30),
    error: value?.error || null,
    valuesRedacted: true,
  };
}

function compactPageProvenance(value) {
  if (!value) return null;
  return {
    pageLoadSeq: value.pageLoadSeq ?? null,
    reason: value.reason || null,
    observedAt: value.observedAt || null,
    urlPath: value.urlPath || null,
    documentReadyState: value.documentReadyState || null,
    timeOrigin: value.timeOrigin ?? null,
    httpStatus: value.httpStatus ?? null,
    assetFingerprint: value.assetFingerprint || null,
    scriptCount: value.scriptCount ?? 0,
    stylesheetCount: value.stylesheetCount ?? 0,
    metaCount: value.metaCount ?? 0,
    scripts: Array.isArray(value.scripts) ? value.scripts.slice(0, 12) : [],
    stylesheets: Array.isArray(value.stylesheets) ? value.stylesheets.slice(0, 12) : [],
    error: value.error || null,
    valuesRedacted: true,
  };
}

function isRetryableNavigationError(message) {
  return /net::ERR_NETWORK_CHANGED|net::ERR_ABORTED|net::ERR_CONNECTION_RESET|net::ERR_NAME_NOT_RESOLVED|Navigation timeout|page\.goto:\s*timeout|timeout\s+\d+ms\s+exceeded|workbench-app-not-ready/iu.test(String(message || ""));
}

function navigationFailureKind(message) {
  const text = String(message || "");
  if (/net::ERR_NETWORK_CHANGED/iu.test(text)) return "net::ERR_NETWORK_CHANGED";
  if (/net::ERR_ABORTED/iu.test(text)) return "net::ERR_ABORTED";
  if (/net::ERR_CONNECTION_RESET/iu.test(text)) return "net::ERR_CONNECTION_RESET";
  if (/net::ERR_NAME_NOT_RESOLVED/iu.test(text)) return "net::ERR_NAME_NOT_RESOLVED";
  if (/Navigation timeout|page\.goto:\s*timeout|timeout\s+\d+ms\s+exceeded/iu.test(text)) return "navigation-timeout";
  if (/workbench-app-not-ready/iu.test(text)) return "workbench-app-not-ready";
  return "navigation-error";
}

function redactErrorMessage(message) {
  return String(message || "")
    .replace(/([?&](?:token|key|password|secret|authorization)=)[^&\s]+/giu, "$1[redacted]")
    .replace(/(Bearer\s+)[A-Za-z0-9._~+/=-]+/gu, "$1[redacted]");
}

async function waitForTargetPageReady(targetPage, targetUrl, options = {}) {
  const timeoutMs = Number.isFinite(Number(options.timeoutMs)) ? Math.max(1, Number(options.timeoutMs)) : 15000;
  const targetPathname = safeUrlPath(targetUrl) || "";
  if (isProjectManagementPathname(targetPathname)) {
    const started = Date.now();
    const selectors = projectManagement.readinessSelectors;
    await targetPage.waitForFunction((input) => {
      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";
      };
      return input.selectors.some((selector) => {
        try { return visible(document.querySelector(selector)); } catch { return false; }
      });
    }, { selectors }, { timeout: timeoutMs }).catch(() => null);
    const snapshot = await projectManagementReadinessSnapshot(targetPage);
    const ok = snapshot.projectManagementVisible === true || snapshot.mdtodoVisible === true;
    return {
      ok,
      reason: ok ? "project-management-ready" : snapshot.loginVisible ? "login-visible" : "project-management-not-ready",
      durationMs: Date.now() - started,
      snapshot,
      valuesRedacted: true
    };
  }
  if (!isWorkbenchPathname(targetPathname)) return { ok: true, reason: "not-workbench-route", valuesRedacted: true };
  const started = Date.now();
  await targetPage.waitForFunction(() => {
    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 workspace = document.querySelector("#workspace, .workbench-route");
    const login = document.querySelector("form.login-card, .login-card, [data-testid='login']");
    return Boolean(visible(workspace) || visible(login));
  }, null, { timeout: timeoutMs }).catch(() => null);
  const snapshot = await workbenchReadinessSnapshot(targetPage);
  const ok = snapshot.workbenchShellVisible === true;
  return {
    ok,
    reason: ok ? "workbench-ready" : snapshot.loginVisible ? "login-visible" : "workbench-app-not-ready",
    durationMs: Date.now() - started,
    snapshot,
    valuesRedacted: true
  };
}

async function workbenchReadinessSnapshot(targetPage) {
  const snapshot = await targetPage.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 sessionCreate = document.querySelector("#session-create");
    const sessionRail = document.querySelector("#session-sidebar");
    const sessionCollapseToggle = document.querySelector("#session-collapse-toggle");
    return {
      url: window.location.href,
      path: window.location.pathname,
      readyState: document.readyState,
      workbenchShellVisible: visible(document.querySelector("#workspace, .workbench-route")),
      sessionCreatePresent: Boolean(sessionCreate),
      sessionCreateVisible: visible(sessionCreate),
      sessionRailPresent: Boolean(sessionRail),
      sessionRailCollapsed: sessionRail ? sessionRail.getAttribute("data-collapsed") === "true" || sessionRail.classList.contains("is-collapsed") : null,
      sessionCollapseTogglePresent: Boolean(sessionCollapseToggle),
      sessionCollapseToggleVisible: visible(sessionCollapseToggle),
      sessionCollapseToggleExpanded: sessionCollapseToggle ? sessionCollapseToggle.getAttribute("aria-expanded") : null,
      commandInputPresent: visible(document.querySelector("#command-input")),
      activeTabPresent: visible(document.querySelector(".session-tab[data-active='true'], .session-tab[aria-selected='true']")),
      warningPresent: visible(document.querySelector(".composer-warning")),
      loginVisible: visible(document.querySelector("form.login-card, .login-card, [data-testid='login']")),
      bodyTextPreview: String(document.body?.innerText || "").slice(0, 2000),
      valuesRedacted: true
    };
  }).catch((error) => ({ error: errorSummary(error), valuesRedacted: true }));
  if (snapshot && typeof snapshot.bodyTextPreview === "string") {
    snapshot.bodyTextHash = sha256Text(snapshot.bodyTextPreview);
    delete snapshot.bodyTextPreview;
  }
  return snapshot;
}

async function projectManagementReadinessSnapshot(targetPage) {
  const selectors = projectManagement.readinessSelectors;
  return targetPage.evaluate((input) => {
    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 selectorStates = input.selectors.map((selector) => {
      let matched = false;
      let visibleMatched = false;
      try {
        const element = document.querySelector(selector);
        matched = Boolean(element);
        visibleMatched = visible(element);
      } catch {}
      return { selector, matched, visible: visibleMatched };
    });
    return {
      url: window.location.href,
      path: window.location.pathname,
      readyState: document.readyState,
      projectManagementVisible: visible(document.querySelector('[data-testid="project-management-root"]')),
      mdtodoVisible: visible(document.querySelector('[data-testid="project-management-mdtodo"]')),
      loginVisible: visible(document.querySelector("form.login-card, .login-card, [data-testid='login']")),
      selectorStates,
      valuesRedacted: true
    };
  }, { selectors }).catch((error) => ({ error: errorSummary(error), valuesRedacted: true }));
}

async function waitForProjectManagementCommandReady(options = {}) {
  const timeoutMs = Number.isFinite(Number(options.timeoutMs)) ? Math.max(1, Number(options.timeoutMs)) : 15000;
  const started = Date.now();
  const deadline = started + timeoutMs;
  let last = null;
  while (Date.now() <= deadline) {
    last = await projectManagementCommandSnapshot();
    const path = String(last?.path || safeUrlPath(currentPageUrl()) || "");
    const baseReady = last?.pageKind === "project-management-mdtodo"
      && Number(last?.sourceCount || 0) > 0
      && Number(last?.fileCount || 0) > 0
      && Number(last?.taskCount || 0) > 0;
    const needsTask = /\/tasks\//u.test(path);
    const taskReady = !needsTask || Boolean(last?.selectedTaskId || last?.selectedTaskRef?.hash || last?.taskBodyVisible === true || last?.launchButtonVisible === true);
    const needsReport = /\/reports\//u.test(path);
    const reportReady = !needsReport || last?.reportPreviewVisible === true || last?.reportFullscreenVisible === true;
    if (baseReady && taskReady && reportReady) return { ok: true, reason: "project-management-command-ready", durationMs: Date.now() - started, snapshot: last, valuesRedacted: true };
    await page.waitForTimeout(250).catch(() => {});
  }
  return { ok: false, reason: "project-management-command-not-ready", durationMs: Date.now() - started, snapshot: last, valuesRedacted: true };
}

function isWorkbenchPathname(value) {
  const pathname = String(value || "");
  return pathname === "/workbench" || pathname === "/workspace" || pathname.startsWith("/workbench/") || pathname.startsWith("/workspace/");
}

function isProjectManagementPathname(value) {
  if (projectManagement.enabled !== true) return false;
  const pathname = String(value || "");
  return projectManagement.targetPaths.some((target) => pathname === target || pathname.startsWith(target + "/"));
}

function isAgentSessionCreateRequest(requestOrUrl) {
  const method = typeof requestOrUrl?.method === "function" ? requestOrUrl.method().toUpperCase() : "";
  if (method && method !== "POST") return false;
  const url = typeof requestOrUrl === "string" ? requestOrUrl : typeof requestOrUrl?.url === "function" ? requestOrUrl.url() : "";
  try {
    return new URL(url).pathname === "/v1/agent/sessions";
  } catch {
    return false;
  }
}

function requestFailureSummary(request) {
  let failure = null;
  try {
    failure = request.failure();
  } catch {}
  let urlPath = null;
  try {
    urlPath = new URL(request.url()).pathname;
  } catch {}
  return {
    method: typeof request.method === "function" ? request.method().toUpperCase() : null,
    urlPath,
    failureText: failure?.errorText || null,
    valuesRedacted: true
  };
}

async function ensureSessionRailExpanded() {
  const before = await workbenchReadinessSnapshot(page);
  if (before?.sessionCreateVisible === true) {
    return { ok: true, action: "already-visible", before, after: before, valuesRedacted: true };
  }
  const toggle = page.locator("#session-collapse-toggle").first();
  const toggleVisible = await toggle.isVisible({ timeout: 2000 }).catch(() => false);
  if (before?.sessionRailCollapsed !== true || !toggleVisible) {
    return { ok: false, action: "not-expanded", reason: before?.sessionRailCollapsed === true ? "collapse-toggle-not-visible" : "session-rail-not-collapsed", before, after: before, valuesRedacted: true };
  }
  await toggle.click();
  await page.waitForFunction(() => {
    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 rail = document.querySelector("#session-sidebar");
    return Boolean(visible(document.querySelector("#session-create")) || (rail && rail.getAttribute("data-collapsed") === "false"));
  }, null, { timeout: 5000 }).catch(() => null);
  const after = await workbenchReadinessSnapshot(page);
  return {
    ok: after?.sessionCreateVisible === true,
    action: "expanded-session-rail",
    before,
    after,
    valuesRedacted: true
  };
}

async function clickAndWaitForAgentSessionCreate(create) {
  let removeRequestFailedListener = () => {};
  const requestFailedPromise = new Promise((resolve) => {
    let timeout = null;
    const handler = (request) => {
      if (!isAgentSessionCreateRequest(request)) return;
      removeRequestFailedListener();
      resolve({ kind: "requestfailed", requestFailure: requestFailureSummary(request) });
    };
    removeRequestFailedListener = () => {
      page.off("requestfailed", handler);
      if (timeout !== null) clearTimeout(timeout);
      timeout = null;
    };
    page.on("requestfailed", handler);
    timeout = setTimeout(() => {
      removeRequestFailedListener();
      resolve(null);
    }, 45000);
  });
  const createResponsePromise = page.waitForResponse((response) => {
    const request = response.request();
    return isAgentSessionCreateRequest(request) || isAgentSessionCreateRequest(response.url());
  }, { timeout: 45000 }).then((response) => ({ kind: "response", response })).catch((error) => ({ kind: "wait-error", waitError: errorSummary(error) }));
  await create.click();
  const outcome = await Promise.race([createResponsePromise, requestFailedPromise]);
  removeRequestFailedListener();
  return outcome ?? await createResponsePromise;
}

async function createSessionFromUi() {
  const beforeUrl = currentPageUrl();
  const before = await workbenchSessionSnapshot();
  const attempts = [];
  let createResponse = null;
  for (let attempt = 1; attempt <= 2; attempt += 1) {
    const railExpansion = await ensureSessionRailExpanded();
    const readinessBeforeClick = railExpansion.after || await workbenchReadinessSnapshot(page);
    const create = page.locator("#session-create").first();
    try {
      await create.waitFor({ state: "visible", timeout: 15000 });
    } catch (error) {
      const readinessAfterWait = await workbenchReadinessSnapshot(page);
      const createError = new Error("newSession session create button is not visible");
      createError.details = { beforeUrl, afterUrl: currentPageUrl(), before, attempts, attempt, readinessBeforeClick, readinessAfterWait, railExpansion, waitError: errorSummary(error), pageId, valuesRedacted: true };
      throw createError;
    }
    const createButtonState = await create.evaluate((element) => {
      const rect = element.getBoundingClientRect();
      const style = window.getComputedStyle(element);
      return {
        tag: element.tagName.toLowerCase(),
        id: element.id || null,
        disabled: Boolean(element.disabled),
        ariaDisabled: element.getAttribute("aria-disabled") || null,
        visible: rect.width > 0 && rect.height > 0 && style.visibility !== "hidden" && style.display !== "none",
        rect: { width: Math.round(rect.width), height: Math.round(rect.height) },
        valuesRedacted: true
      };
    }).catch((error) => ({ error: errorSummary(error), valuesRedacted: true }));
    const outcome = await clickAndWaitForAgentSessionCreate(create);
    if (outcome?.kind === "response") {
      createResponse = outcome.response;
      attempts.push({ attempt, outcome: "response", readinessBeforeClick, railExpansion, createButtonState, valuesRedacted: true });
      break;
    }
    const afterAttempt = await workbenchSessionSnapshot();
    attempts.push({
      attempt,
      outcome: outcome?.kind || "unknown",
      readinessBeforeClick,
      railExpansion,
      createButtonState,
      waitError: outcome?.waitError || null,
      requestFailure: outcome?.requestFailure || null,
      after: afterAttempt,
      valuesRedacted: true
    });
    if (attempt < 2 && outcome?.kind === "requestfailed") {
      await page.waitForTimeout(1500);
      continue;
    }
    const waitError = outcome?.waitError || outcome?.requestFailure || { name: outcome?.kind || "unknown", valuesRedacted: true };
    const error = new Error("newSession did not observe POST /v1/agent/sessions response after click: " + (waitError.message || waitError.failureText || waitError.name || "timeout"));
    error.details = { beforeUrl, afterUrl: currentPageUrl(), before, attempts, pageId, valuesRedacted: true };
    throw error;
  }
  if (createResponse === null) throw new Error("newSession did not produce an authoritative session create response");
  const createStatus = createResponse.status();
  let createPayload = null;
  let createPayloadError = null;
  try {
    createPayload = await createResponse.json();
  } catch (error) {
    createPayloadError = errorSummary(error);
  }
  const createdSessionId = sessionIdFromAgentSessionPayload(createPayload);
  if (createStatus < 200 || createStatus >= 300 || !createdSessionId) {
    const error = new Error("newSession did not receive an authoritative session id from POST /v1/agent/sessions");
    error.details = { status: createStatus, statusText: createResponse.statusText(), responseParsed: createPayload !== null, responseParseError: createPayloadError, createdSessionId, valuesRedacted: true };
    throw error;
  }
  await page.waitForFunction((expectedSessionId) => {
    const activeTab = document.querySelector(".session-tab[data-active='true'], .session-tab[aria-selected='true']");
    const sessionId = activeTab?.getAttribute("data-session-id") || "";
    const routeMatch = window.location.pathname.match(/\/workbench\/sessions\/([^/]+)/u) || window.location.pathname.match(/\/workspace\/sessions\/([^/]+)/u);
    const routeSessionId = routeMatch ? decodeURIComponent(routeMatch[1] || "") : "";
    const warning = document.querySelector(".composer-warning")?.textContent?.trim() || "";
    const input = document.querySelector("#command-input");
    return Boolean(activeTab && sessionId === expectedSessionId && routeSessionId === expectedSessionId && input && !input.disabled && !warning);
  }, createdSessionId, { timeout: 45000 }).catch(() => null);
  const after = await workbenchSessionSnapshot();
  const afterSessionId = after?.activeSessionId || after?.routeSessionId || "";
  const ok = Boolean(afterSessionId === createdSessionId && after?.routeSessionId === createdSessionId && after?.composerReady);
  if (!ok) {
    const error = new Error("newSession did not select the authoritative newly created workbench session");
    error.details = { beforeUrl, afterUrl: currentPageUrl(), before, after, createdSessionId, attempts, pageId, valuesRedacted: true };
    throw error;
  }
  return {
    beforeUrl,
    afterUrl: currentPageUrl(),
    ok,
    before,
    after,
    attempts,
    sessionId: createdSessionId,
    createSession: { status: createStatus, statusText: createResponse.statusText(), responseParsed: createPayload !== null, responseParseError: createPayloadError, createdSessionId, valuesRedacted: true },
    pageId
  };
}

function sessionIdFromAgentSessionPayload(payload) {
  const direct = payload?.sessionId ?? payload?.id ?? payload?.session?.sessionId ?? payload?.session?.id ?? payload?.data?.sessionId ?? payload?.data?.id ?? payload?.data?.session?.sessionId ?? payload?.data?.session?.id;
  const directText = String(direct || "").trim();
  if (/^ses_[A-Za-z0-9_-]+$/u.test(directText)) return directText;
  const match = JSON.stringify(payload ?? "").match(/\bses_[A-Za-z0-9_-]+\b/u);
  return match ? match[0] : null;
}

function traceIdFromAgentChatPayload(payload) {
  const direct = payload?.traceId ?? payload?.turn?.traceId ?? payload?.message?.traceId ?? payload?.data?.traceId ?? payload?.data?.turn?.traceId ?? payload?.data?.message?.traceId;
  const directText = String(direct || "").trim();
  if (/^(?:trc_[A-Za-z0-9_-]+|[a-f0-9]{16,64})$/u.test(directText)) return directText;
  const match = JSON.stringify(payload ?? "").match(/\b(?:trc_[A-Za-z0-9_-]+|[a-f0-9]{16,64})\b/u);
  return match ? match[0] : null;
}

async function workbenchSessionSnapshot(targetPage = page) {
  return targetPage.evaluate(() => {
    const activeTab = document.querySelector(".session-tab[data-active='true'], .session-tab[aria-selected='true']");
    const routeMatch = window.location.pathname.match(/\/workbench\/sessions\/([^/]+)/u) || window.location.pathname.match(/\/workspace\/sessions\/([^/]+)/u);
    const input = document.querySelector("#command-input");
    const warning = document.querySelector(".composer-warning")?.textContent?.trim() || null;
    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";
    };
    return {
      url: window.location.href,
      routeSessionId: routeMatch ? decodeURIComponent(routeMatch[1] || "") : null,
      activeSessionId: activeTab?.getAttribute("data-session-id") || null,
      activeConversationId: activeTab?.getAttribute("data-conversation-id") || null,
      activeStatus: activeTab?.getAttribute("data-status") || null,
      tabCount: document.querySelectorAll(".session-tab").length,
      messageCount: Array.from(document.querySelectorAll('article.message-card, .message-card[data-message-id], article[data-message-id]')).filter(visible).length,
      traceRowCount: Array.from(document.querySelectorAll('[data-testid*="trace" i], [class*="trace" i], [data-trace-id], [data-testid*="event" i]')).filter(visible).length,
      loadingCount: Array.from(document.querySelectorAll('[aria-busy="true"], [data-loading="true"], [class*="loading" i], [data-testid*="loading" i]')).filter(visible).length,
      composerReady: Boolean(activeTab && input && !input.disabled && !warning),
      warning
    };
  }).catch(() => null);
}

function controlPageRecoveryTarget(snapshot, beforeUrl) {
  const sessionId = snapshot?.routeSessionId || snapshot?.activeSessionId || routeSessionIdFromUrl(beforeUrl);
  if (sessionId) return { sessionId, targetPath: "/workbench/sessions/" + encodeURIComponent(sessionId), valuesRedacted: true };
  const path = safeUrlPath(beforeUrl);
  if (isWorkbenchPathname(path || "")) return { sessionId: null, targetPath: path, valuesRedacted: true };
  return { sessionId: null, targetPath, valuesRedacted: true };
}

function controlPageProjectionMissingForCommand(snapshot, beforeUrl) {
  const path = safeUrlPath(snapshot?.url || beforeUrl);
  if (!isWorkbenchPathname(path || "")) return false;
  const routeSessionId = snapshot?.routeSessionId || routeSessionIdFromUrl(snapshot?.url || beforeUrl);
  if (!routeSessionId) return false;
  return snapshot?.activeSessionId !== routeSessionId
    && Number(snapshot?.tabCount || 0) === 0
    && Number(snapshot?.messageCount || 0) === 0
    && Number(snapshot?.traceRowCount || 0) === 0
    && snapshot?.composerReady !== true;
}

async function controlPageLivenessSnapshot(reason, timeoutMs = 1500) {
  const started = Date.now();
  return withHardTimeout(workbenchSessionSnapshot(page), timeoutMs, "control page liveness snapshot exceeded " + timeoutMs + "ms")
    .then((snapshot) => ({
      ok: snapshot !== null,
      reason,
      durationMs: Date.now() - started,
      snapshot,
      pageRole: "control",
      pageId,
      pageEpoch: controlPageEpoch,
      valuesRedacted: true
    }))
    .catch((error) => ({
      ok: false,
      reason,
      durationMs: Date.now() - started,
      error: errorSummary(error),
      pageRole: "control",
      pageId,
      pageEpoch: controlPageEpoch,
      valuesRedacted: true
    }));
}

async function recoverControlPageToTarget(reason, beforeUrl, target, liveness = null, options = {}) {
  let navigation = null;
  let hydration = null;
  let afterLiveness = null;
  const attempts = [];
  let ok = false;
  const maxAttempts = Number.isFinite(Number(options.maxAttempts)) ? Math.max(1, Number(options.maxAttempts)) : 2;
  const hydrationTimeoutMs = Number.isFinite(Number(options.hydrationTimeoutMs)) ? Math.max(1, Number(options.hydrationTimeoutMs)) : 12000;
  const hydrationHardTimeoutMs = Number.isFinite(Number(options.hydrationHardTimeoutMs)) ? Math.max(hydrationTimeoutMs, Number(options.hydrationHardTimeoutMs)) : hydrationTimeoutMs + 2000;
  const navigationOptions = options.navigation || {};
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
    await recreateControlPageForNavigation(reason + "-control-page-recovery", attempt);
    try {
      navigation = await gotoTarget(target.targetPath, navigationOptions);
    } catch (error) {
      navigation = { ok: false, targetPath: target.targetPath, error: errorSummary(error), valuesRedacted: true };
    }
    if (!navigation?.error && target.sessionId) {
      hydration = await withHardTimeout(
        waitForWorkbenchSessionHydrated(page, target.sessionId, { timeoutMs: hydrationTimeoutMs }),
        hydrationHardTimeoutMs,
        "control page recovery hydration exceeded " + hydrationHardTimeoutMs + "ms"
      ).catch((error) => ({ ok: false, error: errorSummary(error), valuesRedacted: true }));
    } else {
      hydration = null;
    }
    afterLiveness = await controlPageLivenessSnapshot(reason + "-post-recovery", 3000);
    ok = !navigation?.error && afterLiveness.ok === true && (!target.sessionId || hydration?.ok === true);
    attempts.push({ attempt, ok, navigation, hydration, afterLiveness, pageId, pageEpoch: controlPageEpoch, valuesRedacted: true });
    if (ok) break;
  }
  return {
    ok,
    recovered: ok,
    reason,
    beforeUrl,
    afterUrl: currentPageUrl(),
    target,
    liveness,
    navigation,
    hydration,
    afterLiveness,
    attempts,
    pageRole: "control",
    pageId,
    pageEpoch: controlPageEpoch,
    valuesRedacted: true
  };
}

async function promoteObserverPageToControlForCommand(reason, target, liveness = null) {
  const beforeUrl = currentPageUrl();
  const beforeObserverUrl = pageUrl(observerPage);
  if (!observerPage || observerPage.isClosed()) {
    return { ok: false, promoted: false, reason, failureKind: "observer-page-unavailable", beforeUrl, observerUrl: beforeObserverUrl, target, liveness, valuesRedacted: true };
  }
  const sessionId = target?.sessionId || routeSessionIdFromUrl(beforeUrl);
  if (!sessionId) {
    return { ok: false, promoted: false, reason, failureKind: "observer-promotion-needs-session", beforeUrl, observerUrl: beforeObserverUrl, target, liveness, valuesRedacted: true };
  }
  const observerSessionId = routeSessionIdFromUrl(beforeObserverUrl);
  if (observerSessionId !== sessionId) {
    return { ok: false, promoted: false, reason, failureKind: "observer-session-mismatch", beforeUrl, observerUrl: beforeObserverUrl, observerSessionId, sessionId, target, liveness, valuesRedacted: true };
  }
  const targetUrl = new URL(target?.targetPath || ("/workbench/sessions/" + encodeURIComponent(sessionId)), baseUrl).toString();
  const readiness = await observerSessionReadiness(targetUrl, sessionId, { readinessTimeoutMs: 1000, hydrationTimeoutMs: 2000 });
  const observerComposerReady = readiness?.hydration?.snapshot?.composerReady === true;
  if (readiness.ok !== true || observerComposerReady !== true) {
    return { ok: false, promoted: false, reason, failureKind: readiness.failureKind || (observerComposerReady ? "observer-not-ready" : "observer-composer-not-ready"), beforeUrl, observerUrl: beforeObserverUrl, sessionId, target, liveness, readiness, valuesRedacted: true };
  }
  const oldControlPage = page;
  observerPageEpoch += 1;
  controlPageEpoch += 1;
  page = observerPage;
  observerPage = null;
  attachPassiveListeners(page, "control", pageId);
  currentPageProvenance = null;
  if (oldControlPage && !oldControlPage.isClosed() && oldControlPage !== page) {
    await withHardTimeout(oldControlPage.close(), 2000, "old control page close exceeded 2000ms")
      .catch((error) => appendJsonl(files.errors, eventRecord("old-control-page-close-timeout", { reason, error: errorSummary(error), pageRole: "control", pageId, pageEpoch: controlPageEpoch })));
  }
  observerPage = await context.newPage();
  attachPassiveListeners(observerPage, "observer", observerPageId);
  const observerSync = await syncObserverPageToControlSession(reason + "-observer-recreated-after-promotion", sessionId, {
    maxAttempts: 1,
    navigationTimeoutMs: 8000,
    readinessTimeoutMs: 3000,
    hydrationTimeoutMs: 3000,
    shortCircuitReadinessTimeoutMs: 1000,
    shortCircuitHydrationTimeoutMs: 1000,
  });
  return {
    ok: true,
    promoted: true,
    reason,
    beforeUrl,
    beforeObserverUrl,
    afterUrl: currentPageUrl(),
    sessionId,
    target,
    liveness,
    readiness,
    observerSync,
    pageRole: "control",
    pageId,
    pageEpoch: controlPageEpoch,
    valuesRedacted: true
  };
}

async function ensureControlPageResponsiveForCommand(reason) {
  const beforeUrl = currentPageUrl();
  const liveness = await controlPageLivenessSnapshot(reason + "-preflight", 3000);
  const projectionMissing = liveness.ok === true && controlPageProjectionMissingForCommand(liveness.snapshot, beforeUrl);
  if (liveness.ok && !projectionMissing) return { ok: true, recovered: false, reason, beforeUrl, afterUrl: currentPageUrl(), liveness, pageRole: "control", pageId, pageEpoch: controlPageEpoch, valuesRedacted: true };
  const target = controlPageRecoveryTarget(liveness.snapshot, beforeUrl);
  await appendJsonl(files.control, eventRecord(projectionMissing ? "control-page-projection-missing-before-command" : "control-page-unresponsive-before-command", {
    reason,
    beforeUrl,
    target,
    liveness,
    pageRole: "control",
    pageId,
    pageEpoch: controlPageEpoch,
    valuesRedacted: true
  }));
  const promotion = await promoteObserverPageToControlForCommand(reason + "-observer-promotion", target, liveness);
  await appendJsonl(files.control, eventRecord(promotion.ok ? "control-page-promoted-from-observer-before-command" : "control-page-observer-promotion-skipped-before-command", promotion));
  if (promotion.ok === true) return promotion;
  const recovery = await recoverControlPageToTarget(reason, beforeUrl, target, liveness, {
    maxAttempts: 1,
    navigation: { maxAttempts: 1, navigationTimeoutMs: 8000, readinessTimeoutMs: 4000, settleMs: 250, lateReadinessTimeoutMs: 1000 },
    hydrationTimeoutMs: 4000,
    hydrationHardTimeoutMs: 5000,
  });
  await appendJsonl(files.control, eventRecord(recovery.ok ? "control-page-recovered-before-command" : "control-page-recovery-failed-before-command", recovery));
  if (!recovery.ok) {
    const error = new Error("control page recovery failed before " + reason);
    error.details = recovery;
    throw error;
  }
  return recovery;
}

async function forceRecoverControlPageForCommand(reason) {
  const beforeUrl = currentPageUrl();
  const liveness = await controlPageLivenessSnapshot(reason + "-snapshot", 3000);
  const target = controlPageRecoveryTarget(liveness.snapshot, beforeUrl);
  await appendJsonl(files.control, eventRecord("control-page-forced-recovery-before-command", {
    reason,
    beforeUrl,
    target,
    liveness,
    pageRole: "control",
    pageId,
    pageEpoch: controlPageEpoch,
    valuesRedacted: true
  }));
  const promotion = await promoteObserverPageToControlForCommand(reason + "-observer-promotion", target, liveness);
  await appendJsonl(files.control, eventRecord(promotion.ok ? "control-page-forced-promoted-from-observer-before-command" : "control-page-forced-observer-promotion-skipped-before-command", promotion));
  if (promotion.ok === true) return promotion;
  const recovery = await recoverControlPageToTarget(reason, beforeUrl, target, liveness, {
    maxAttempts: 1,
    navigation: { maxAttempts: 1, navigationTimeoutMs: 8000, readinessTimeoutMs: 4000, settleMs: 250, lateReadinessTimeoutMs: 1000 },
    hydrationTimeoutMs: 4000,
    hydrationHardTimeoutMs: 5000,
  });
  await appendJsonl(files.control, eventRecord(recovery.ok ? "control-page-forced-recovered-before-command" : "control-page-forced-recovery-failed-before-command", recovery));
  return recovery;
}

async function sendPrompt(text, options = {}) {
  if (text.trim().length === 0) throw new Error("sendPrompt requires non-empty text");
  const responsePath = options.responsePath || "/v1/agent/chat";
  const controlRecovery = await ensureControlPageResponsiveForCommand("sendPrompt");
  const beforeUrl = currentPageUrl();
  const beforeEvidence = await promptSideEffectSnapshot();
  let editor = null;
  let composerRecovery = null;
  let editorWaitError = null;
  for (let attempt = 1; attempt <= 2; attempt += 1) {
    const primaryEditor = page.locator("#command-input").last();
    const candidate = await primaryEditor.isVisible().catch(() => false)
      ? primaryEditor
      : page.locator('textarea, [role="textbox"], [contenteditable="true"], input[type="text"]').last();
    try {
      await withHardTimeout(candidate.waitFor({ state: "visible", timeout: 8000 }), 10000, "sendPrompt composer editor did not become visible within 10s");
      editor = candidate;
      break;
    } catch (error) {
      editorWaitError = error;
      if (attempt >= 2) break;
      composerRecovery = await forceRecoverControlPageForCommand("sendPrompt-composer-editor-missing");
      if (composerRecovery.ok !== true) break;
    }
  }
  if (!editor) {
    const snapshot = await controlPageLivenessSnapshot("sendPrompt-composer-editor-missing-final", 3000);
    const error = new Error("sendPrompt composer editor did not become visible");
    error.details = { beforeUrl, afterUrl: currentPageUrl(), controlRecovery, composerRecovery, snapshot, editorWaitError: errorSummary(editorWaitError), pageId, pageEpoch: controlPageEpoch, valuesRedacted: true };
    throw error;
  }
  await fillComposerEditor(editor, text);
  const primarySubmitSelector = '#command-send, #command-submit, [data-testid="command-submit"], [data-testid="composer-submit"], [data-testid="send-command"]';
  const primarySubmit = page.locator(primarySubmitSelector).last();
  const submit = await primarySubmit.isVisible().catch(() => false)
    ? primarySubmit
    : page.locator([
    'button[type="submit"]',
    'button:has-text("发送")',
    'button:has-text("Send")',
    '[data-testid*="send" i]',
    '[aria-label*="send" i]',
    '[aria-label*="发送"]'
  ].join(", ")).last();
  await withHardTimeout(submit.waitFor({ state: "visible", timeout: 15000 }), 20000, "sendPrompt submit button did not become visible within 20s");
  if (options.expectedAction) {
    const configuredActionWaitMs = options.expectedActionWaitMs === null || options.expectedActionWaitMs === undefined || options.expectedActionWaitMs === ""
      ? null
      : Number(options.expectedActionWaitMs);
    const actionWaitMs = Number.isFinite(configuredActionWaitMs)
      ? Math.max(1000, Math.trunc(configuredActionWaitMs))
      : options.expectedAction === "turn" ? 180000 : 1000;
    const actionDeadline = Date.now() + actionWaitMs;
    let composer = null;
    while (Date.now() <= actionDeadline) {
      composer = await composerButtonState(submit);
      if (composer.action === options.expectedAction && composer.disabled !== true) break;
      await page.waitForTimeout(250);
    }
    composer = composer || await composerButtonState(submit);
    if (composer.action !== options.expectedAction || composer.disabled === true) {
      await clearComposerEditor(editor).catch(() => {});
      const sideEffect = await waitForPromptSideEffect(beforeEvidence, 200).catch(() => promptSideEffectSnapshot());
      const blocked = {
        beforeUrl,
        afterUrl: currentPageUrl(),
        textHash: sha256Text(text),
        textBytes: Buffer.byteLength(text),
        submitted: false,
        blocked: true,
        degradedReason: options.noActiveReason || "composer-action-mismatch",
        composer,
        chatSubmit: {
          status: null,
          statusText: null,
          urlPath: responsePath,
          responseObserved: false,
          sideEffectObserved: sideEffectHasAuthoritativePromptSubmission(sideEffect),
          sideEffect,
          actionWaitMs,
          expectedAction: options.expectedAction,
          actualAction: composer.action,
          valuesRedacted: true
        },
        controlRecovery,
        pageId,
        pageEpoch: controlPageEpoch,
        valuesRedacted: true
      };
      if (options.throwOnActionMismatch === true) {
        const error = new Error("sendPrompt composer action mismatch: expected " + options.expectedAction + " actual " + (composer.action || "null"));
        error.details = blocked;
        throw error;
      }
      return blocked;
    }
  }
  const acceptedResponsePaths = [responsePath, ...(Array.isArray(options.alternateResponsePaths) ? options.alternateResponsePaths : [])];
  const chatResponsePromise = page.waitForResponse((response) => {
    const request = response.request();
    if (request.method().toUpperCase() !== "POST") return false;
    try {
      return acceptedResponsePaths.includes(new URL(response.url()).pathname);
    } catch {
      return false;
    }
  }, { timeout: 45000 }).catch((error) => ({ waitError: errorSummary(error) }));
  await submit.click();
  const chatResponse = await chatResponsePromise;
  await page.waitForTimeout(500);
  if (chatResponse?.waitError) {
    const sideEffect = await waitForPromptSideEffect(beforeEvidence, 5000);
    if (sideEffectHasAuthoritativePromptSubmission(sideEffect)) {
      return {
        beforeUrl,
        afterUrl: currentPageUrl(),
        textHash: sha256Text(text),
        textBytes: Buffer.byteLength(text),
        chatSubmit: { status: null, statusText: null, urlPath: responsePath, waitError: chatResponse.waitError, sideEffectObserved: true, sideEffect },
        controlRecovery,
        pageId,
        pageEpoch: controlPageEpoch
      };
    }
    const error = new Error("sendPrompt did not observe POST " + responsePath + " response or an authoritative new turn after submit: " + (chatResponse.waitError.message || chatResponse.waitError.name || "timeout"));
    error.details = {
      beforeUrl,
      afterUrl: currentPageUrl(),
      textHash: sha256Text(text),
      textBytes: Buffer.byteLength(text),
      chatSubmit: { status: null, statusText: null, urlPath: responsePath, waitError: chatResponse.waitError, sideEffectObserved: false, sideEffect },
      controlRecovery,
      pageId,
      pageEpoch: controlPageEpoch,
      valuesRedacted: true
    };
    throw error;
  }
  const chatStatus = chatResponse.status();
  let chatUrlPath = responsePath;
  try {
    chatUrlPath = new URL(chatResponse.url()).pathname;
  } catch {
    chatUrlPath = responsePath;
  }
  if (chatStatus < 200 || chatStatus >= 300) {
    throw new Error("sendPrompt observed POST " + chatUrlPath + " HTTP " + chatStatus + " " + chatResponse.statusText());
  }
  let chatPayload = null;
  let chatPayloadError = null;
  try {
    chatPayload = await chatResponse.json();
  } catch (error) {
    chatPayloadError = errorSummary(error);
  }
  const payloadText = chatPayload ? JSON.stringify(chatPayload) : "";
  const traceId = payloadText.match(/\btrc_[A-Za-z0-9_-]+\b/u)?.[0] || null;
  const otelTraceId = typeof chatPayload?.otelTrace?.traceId === "string" && /^[0-9a-f]{32}$/u.test(chatPayload.otelTrace.traceId)
    ? chatPayload.otelTrace.traceId
    : null;
  return {
    beforeUrl,
    afterUrl: currentPageUrl(),
    textHash: sha256Text(text),
    textBytes: Buffer.byteLength(text),
    chatSubmit: {
      status: chatStatus,
      statusText: chatResponse.statusText(),
      urlPath: chatUrlPath,
      traceId,
      otelTraceId,
      resultUrl: safeUrlPath(chatPayload?.resultUrl),
      turnUrl: safeUrlPath(chatPayload?.turnUrl),
      streamUrl: safeUrlPath(chatPayload?.streamUrl),
      responseParsed: chatPayload !== null,
      responseParseError: chatPayloadError,
      valuesRedacted: true
    },
    controlRecovery,
    pageId,
    pageEpoch: controlPageEpoch
  };
}

async function fillComposerEditor(editor, text) {
  const tag = await editor.evaluate((element) => element.tagName.toLowerCase()).catch(() => "");
  const editable = await editor.evaluate((element) => element.getAttribute("contenteditable") === "true").catch(() => false);
  if (tag === "textarea" || tag === "input") await editor.fill(text);
  else if (editable) {
    await editor.click();
    await page.keyboard.insertText(text);
  } else {
    await editor.click();
    await page.keyboard.insertText(text);
  }
}

async function clearComposerEditor(editor) {
  const tag = await editor.evaluate((element) => element.tagName.toLowerCase()).catch(() => "");
  const editable = await editor.evaluate((element) => element.getAttribute("contenteditable") === "true").catch(() => false);
  if (tag === "textarea" || tag === "input") {
    await editor.fill("");
    return;
  }
  if (editable) {
    await editor.click();
    await page.keyboard.press(process.platform === "darwin" ? "Meta+A" : "Control+A").catch(() => {});
    await page.keyboard.press("Backspace").catch(() => {});
  }
}

async function composerButtonState(button) {
  return button.evaluate((element) => ({
    action: element.getAttribute("data-action") || null,
    disabled: element.hasAttribute("disabled") || element.getAttribute("aria-disabled") === "true",
    text: (element.textContent || "").trim().slice(0, 80),
    title: element.getAttribute("title") || null,
    ariaLabel: element.getAttribute("aria-label") || null,
    testId: element.getAttribute("data-testid") || null,
  })).catch((error) => ({ action: null, disabled: null, error: errorSummary(error), valuesRedacted: true }));
}

function sideEffectHasAuthoritativePromptSubmission(sideEffect) {
  return Boolean(
    (Array.isArray(sideEffect?.newRunIds) && sideEffect.newRunIds.length > 0)
    || (Array.isArray(sideEffect?.newTraceIds) && sideEffect.newTraceIds.length > 0)
    || Number(sideEffect?.messageCountDelta || 0) > 0
  );
}

async function waitForPromptSideEffect(beforeEvidence, timeoutMs) {
  await page.waitForFunction((before) => {
    const current = (() => {
      const text = document.body?.innerText || "";
      const runIds = Array.from(new Set(text.match(/run_[A-Za-z0-9_:-]+/gu) || [])).slice(-20);
      const traceIds = Array.from(new Set(text.match(/trc_[A-Za-z0-9_:-]+/gu) || [])).slice(-20);
      const running = /Trace running|最近\s*\d+\s*(?:秒|分钟|分|小时)前|Code Agent\s*耗时/iu.test(text);
      const executionError = /AgentRun error|agentrun:error:|provider-stream-disconnected|provider-unavailable/iu.test(text);
      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 messageCount = Array.from(document.querySelectorAll('article.message-card, .message-card[data-message-id], article[data-message-id]')).filter(visible).length;
      const textBytes = new TextEncoder().encode(text).length;
      return { runIds, traceIds, running, executionError, messageCount, textBytes };
    })();
    const beforeRuns = Array.isArray(before?.runIds) ? before.runIds : [];
    const beforeTraces = Array.isArray(before?.traceIds) ? before.traceIds : [];
    const newRun = current.runIds.some((id) => !beforeRuns.includes(id));
    const newTrace = current.traceIds.some((id) => !beforeTraces.includes(id));
    const newMessage = current.messageCount > Number(before?.messageCount || 0);
    return newRun || newTrace || newMessage;
  }, beforeEvidence || {}, { timeout: timeoutMs }).catch(() => null);
  const after = await promptSideEffectSnapshot();
  const beforeRuns = Array.isArray(beforeEvidence?.runIds) ? beforeEvidence.runIds : [];
  const beforeTraces = Array.isArray(beforeEvidence?.traceIds) ? beforeEvidence.traceIds : [];
  const newRunIds = after.runIds.filter((id) => !beforeRuns.includes(id));
  const newTraceIds = after.traceIds.filter((id) => !beforeTraces.includes(id));
  const messageCountDelta = Math.max(0, Number(after.messageCount || 0) - Number(beforeEvidence?.messageCount || 0));
  return { ...after, newRunIds, newTraceIds, messageCountDelta, submitted: newRunIds.length > 0 || newTraceIds.length > 0 || messageCountDelta > 0, valuesRedacted: true };
}

async function promptSideEffectSnapshot() {
  return withHardTimeout(page.evaluate(() => {
    const text = document.body?.innerText || "";
    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";
    };
    return {
      runIds: Array.from(new Set(text.match(/run_[A-Za-z0-9_:-]+/gu) || [])).slice(-20),
      traceIds: Array.from(new Set(text.match(/trc_[A-Za-z0-9_:-]+/gu) || [])).slice(-20),
      running: /Trace running|最近\s*\d+\s*(?:秒|分钟|分|小时)前|Code Agent\s*耗时/iu.test(text),
      executionError: /AgentRun error|agentrun:error:|provider-stream-disconnected|provider-unavailable/iu.test(text),
      messageCount: Array.from(document.querySelectorAll('article.message-card, .message-card[data-message-id], article[data-message-id]')).filter(visible).length,
      textBytes: new TextEncoder().encode(text).length,
      valuesRedacted: true
    };
  }), 3000, "prompt side-effect snapshot exceeded 3000ms")
    .catch(() => ({ runIds: [], traceIds: [], running: false, executionError: false, messageCount: 0, textBytes: 0, valuesRedacted: true }));
}

${nodeWebObserveRunnerCommandActionsSource()}

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);
}

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 { 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 { mode: "select-label", selectedValue: byLabel.selected[0] || 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 { 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 || "");
      await page.waitForTimeout(700);
      const afterProject = await projectManagementCommandSnapshot();
      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) {
  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 };
  }
  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 selectMdtodoTask(command) {
  ensureProjectManagementCommand("selectMdtodoTask");
  const beforeUrl = currentPageUrl();
  const beforeProject = await projectManagementCommandSnapshot();
  const target = await mdtodoTaskLocator(command);
  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") || 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;
}

async function fillMdtodoField(testId, value) {
  if (typeof value !== "string" || !value.trim()) return { testId, filled: false };
  const locator = page.locator('[data-testid="' + cssEscape(testId) + '"]').first();
  await locator.waitFor({ state: "visible", timeout: 10000 });
  await locator.fill(value);
  return { testId, 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 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 = page.locator('[data-testid="' + cssEscape(field.testId) + '"]').first();
      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 = page.locator('[data-testid="' + cssEscape(editorTestId) + '"]').first();
  if (await visibleLocator(editor)) return { editorTestId, readTestId, opened: false };
  const read = page.locator('[data-testid="' + cssEscape(readTestId) + '"]').first();
  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-edit-body-save", [{ kind: "fill", testId: "mdtodo-edit-body", value: body, openByDblClickTestId: "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-edit-body-save", [{ kind: "fill", testId: "mdtodo-edit-body", value: body, openByDblClickTestId: "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 confirmVisible = await visibleLocator(page.locator('[data-testid="mdtodo-delete-cancel"]').first());
  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 beforeProject = await projectManagementCommandSnapshot({ includeRaw: true });
  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;
  if ((requestedTaskRef && beforeProject.selectedTaskRefRaw !== requestedTaskRef) || requestedTaskId) {
    await selectMdtodoTask({ ...command, taskRef: requestedTaskRef });
  }
  const projectBeforeClick = await projectManagementCommandSnapshot({ includeRaw: true });
  const button = page.locator('[data-testid="mdtodo-workbench-launch"], [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;
    try {
      chatPayload = await response.json();
    } catch (error) {
      chatPayloadError = errorSummary(error);
    }
    const headers = response.headers();
    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;
  try {
    payload = await launchResponse.json();
  } catch (error) {
    responseParseError = errorSummary(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),
    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-action="launch-workbench"]');
    const bodyRendered = document.querySelector('[data-testid="mdtodo-body-rendered"]');
    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") || 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"]')),
      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"], [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
  };
}

async function preflightSummary() {
  return { currentUrl: currentPageUrl(), title: await page.title().catch(() => null), pageId, auth: publicAuth(auth) };
}

async function samplePage(reason, options = {}) {
  if (options?.refreshObserver !== false) await maybeRefreshObserverPage(reason);
  const groupSeq = sampleSeq + 1;
  if (page && !page.isClosed()) {
    await sampleOnePage(page, { reason, groupSeq, pageRole: "control", targetPageId: pageId, pageEpoch: controlPageEpoch })
      .catch((error) => appendJsonl(files.errors, eventRecord("control-sample-error", { pageRole: "control", pageId, pageEpoch: controlPageEpoch, error: errorSummary(error) })));
  }
  if (observerPage && !observerPage.isClosed()) {
    await sampleOnePage(observerPage, { reason, groupSeq, pageRole: "observer", targetPageId: observerPageId, pageEpoch: observerPageEpoch }).catch((error) => appendJsonl(files.errors, eventRecord("observer-sample-error", { pageRole: "observer", pageId: observerPageId, pageEpoch: observerPageEpoch, error: errorSummary(error) })));
  }
  if (options?.screenshot !== false && screenshotIntervalMs > 0 && Date.now() - lastScreenshotAtMs >= screenshotIntervalMs) {
    await withHardTimeout(captureScreenshot("checkpoint", "jpeg"), 15000, "captureScreenshot checkpoint exceeded 15s")
      .catch((error) => appendJsonl(files.errors, eventRecord("screenshot-error", { pageRole: "control", pageId, error: errorSummary(error) })));
  }
  await writeHeartbeat({ status: terminalStatus });
}

async function sampleOnePage(targetPage, { reason, groupSeq, pageRole, targetPageId, pageEpoch }) {
  sampleSeq += 1;
  const evaluateTimeoutMs = Math.max(3000, Math.min(8000, Number(sampleIntervalMs) || 5000));
  const dom = await withHardTimeout(targetPage.evaluate((input) => {
    const trim = (value, limit = 500) => String(value || "").replace(/\s+/g, " ").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 textHashInput = (element) => trim(element.textContent || "", 800);
    const loadingTextPattern = /加载中|\bLoading\b/iu;
    const loadingUiPattern = /loading-state|loading-spinner|spinner|progress|skeleton|busy|pending/iu;
    const codeLikeSelector = "pre,code,.trace-row-body,.trace-row-markdown,.markdown-body,.message-text,[class*='trace-row' i],[class*='terminal' i],[class*='log' i],[class*='output' i]";
    const elementTextForLoading = (element) => [
      element.textContent || "",
      element.getAttribute("aria-label") || "",
      element.getAttribute("title") || "",
      element.getAttribute("data-testid") || ""
    ].map((value) => trim(value, 240)).filter(Boolean).join(" ");
    const elementLooksLikeLoadingUi = (element) => {
      const signal = [
        element.getAttribute("class") || "",
        element.getAttribute("data-testid") || "",
        element.getAttribute("role") || "",
        element.getAttribute("aria-busy") || "",
        element.getAttribute("aria-label") || "",
        element.getAttribute("title") || ""
      ].join(" ");
      return element.getAttribute("aria-busy") === "true" || element.getAttribute("role") === "status" || loadingUiPattern.test(signal);
    };
    const elementIsCodeLike = (element) => Boolean(element.closest(codeLikeSelector)) && !elementLooksLikeLoadingUi(element);
    const hasLoadingText = (element) => {
      const text = elementTextForLoading(element);
      if (!loadingTextPattern.test(text)) return false;
      if (elementIsCodeLike(element)) return false;
      return true;
    };
    const elementDescriptor = (element) => {
      if (!element) return null;
      const className = String(element.className || "").replace(/\s+/g, " ").trim().split(" ").slice(0, 6).join(" ");
      const identityDescendant = element.querySelector("[data-trace-id], [data-message-id], [data-session-id]");
      return {
        tag: element.tagName.toLowerCase(),
        testId: element.getAttribute("data-testid") || null,
        role: element.getAttribute("role") || null,
        id: element.getAttribute("id") || null,
        className: className || null,
        status: element.getAttribute("data-status") || element.getAttribute("aria-busy") || null,
        sessionId: element.getAttribute("data-session-id") || identityDescendant?.getAttribute("data-session-id") || null,
        messageId: element.getAttribute("data-message-id") || identityDescendant?.getAttribute("data-message-id") || null,
        traceId: element.getAttribute("data-trace-id") || identityDescendant?.getAttribute("data-trace-id") || null,
        ariaLabel: element.getAttribute("aria-label") || null
      };
    };
    const ownerKindFor = (element) => {
      const value = [
        element.getAttribute("data-testid") || "",
        element.getAttribute("class") || "",
        element.getAttribute("role") || "",
        element.tagName || ""
      ].join(" ").toLowerCase();
      if (/message|turn|agent|assistant/.test(value)) return "turn";
      if (/session|rail|sidebar|nav/.test(value)) return "session-nav";
      if (/composer|prompt|input|textarea/.test(value)) return "composer";
      if (/diagnostic|alert|error|warning/.test(value)) return "diagnostic";
      if (/trace|event|terminal|log/.test(value)) return "trace";
      if (/performance|metric|chart|table/.test(value)) return "performance";
      if (/main|workspace|workbench|root/.test(value)) return "workbench";
      return "unknown";
    };
    const ownerLabelFor = (element) => {
      const heading = element.querySelector("h1,h2,h3,h4,[data-testid*='title' i],[class*='title' i],[class*='header' i]");
      return trim(
        element.getAttribute("aria-label")
          || element.getAttribute("data-testid")
          || (heading ? heading.textContent || "" : "")
          || element.getAttribute("class")
          || element.tagName,
        160
      );
    };
    const ownerKeyFor = (element) => {
      const descriptor = elementDescriptor(element) || {};
      return [
        ownerKindFor(element),
        descriptor.testId || descriptor.id || descriptor.role || descriptor.className || descriptor.tag || "unknown",
        descriptor.sessionId || descriptor.messageId || descriptor.traceId || ""
      ].filter(Boolean).join(":").slice(0, 240);
    };
    const collectLoadingNodes = () => {
      const candidates = Array.from(document.querySelectorAll("body *"))
        .filter(visible)
        .filter(hasLoadingText)
        .filter((element) => !Array.from(element.children).some((child) => visible(child) && hasLoadingText(child)))
        .slice(-80);
      return candidates.map((element, index) => {
        const rect = element.getBoundingClientRect();
        const identityOwner = element.closest('[data-message-id], [data-trace-id], [data-session-id]');
        const structuralOwner = element.closest('article.message-card, .message-card, [data-testid*="message" i], [data-testid*="turn" i], [data-testid*="composer" i], [data-testid*="session" i], [class*="composer" i], [class*="session" i], [class*="trace" i], [class*="diagnostic" i], article, section, aside, main, form, [role="status"], [role="alert"], [role="article"], [role="navigation"]');
        const owner = identityOwner || structuralOwner || element;
        const ownerDescriptor = elementDescriptor(owner);
        return {
          index,
          tag: element.tagName.toLowerCase(),
          className: String(element.className || "").slice(0, 180),
          testId: element.getAttribute("data-testid"),
          role: element.getAttribute("role"),
          text: trim(elementTextForLoading(element), 240),
          ownerKind: ownerKindFor(owner),
          ownerKey: ownerKeyFor(owner),
          ownerLabel: ownerLabelFor(owner),
          owner: ownerDescriptor,
          ownerText: trim(owner.textContent || "", 300),
          rect: { x: Math.round(rect.x), y: Math.round(rect.y), width: Math.round(rect.width), height: Math.round(rect.height) },
        };
      });
    };
    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") || "";
      if (!href) return null;
      try {
        const parsed = new URL(href, location.href);
        const match = parsed.pathname.match(/\/(?:workbench|workspace)\/sessions\/([^/?#]+)/u);
        return match ? decodeURIComponent(match[1] || "") : null;
      } catch {
        const match = href.match(/\/(?:workbench|workspace)\/sessions\/([^/?#]+)/u);
        return match ? decodeURIComponent(match[1] || "") : null;
      }
    };
    const sessionTitleTextForElement = (element) => {
      const titleNode = element.querySelector("[data-testid*='session-title' i], [data-testid*='session-name' i], [class*='session-title' i], [class*='session-name' i], [data-testid*='title' i], [class*='title' i]");
      return trim(
        (titleNode ? titleNode.textContent || "" : "")
          || element.getAttribute("aria-label")
          || element.getAttribute("title")
          || element.textContent
          || "",
        240
      );
    };
    const sessionTitleFallbackPattern = /^(?:Session\s+)?ses_[A-Za-z0-9_.-]+/iu;
    const looksLikeSessionTitleFallback = (title, sessionId) => {
      const text = trim(title, 240);
      const id = String(sessionId || "").trim();
      if (!text) return true;
      if (sessionTitleFallbackPattern.test(text)) return true;
      if (!id) return false;
      return text === id || text.startsWith(id) || text.startsWith("Session " + id);
    };
    const collectSessionRailTitles = () => {
      const candidates = 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/']"))
        .filter(visible);
      const seen = new Set();
      const items = [];
      for (const element of candidates) {
        const sessionId = sessionIdForElement(element);
        const titleText = sessionTitleTextForElement(element);
        const key = (sessionId || "") + "|" + titleText;
        if (seen.has(key)) continue;
        seen.add(key);
        const rect = element.getBoundingClientRect();
        const fallbackTitle = looksLikeSessionTitleFallback(titleText, sessionId);
        items.push({
          index: items.length,
          tag: element.tagName.toLowerCase(),
          testId: element.getAttribute("data-testid"),
          role: element.getAttribute("role"),
          active: element.getAttribute("data-active") === "true" || element.getAttribute("aria-selected") === "true",
          sessionId,
          sessionIdPrefix: sessionId ? String(sessionId).slice(0, 12) : null,
          titleText,
          fallbackTitle,
          rect: { x: Math.round(rect.x), y: Math.round(rect.y), width: Math.round(rect.width), height: Math.round(rect.height) },
        });
      }
      const visibleItems = items.slice(0, 120);
      const fallbackItems = visibleItems.filter((item) => item.fallbackTitle);
      const visibleCount = visibleItems.length;
      const fallbackTitleCount = fallbackItems.length;
      return {
        visibleCount,
        fallbackTitleCount,
        fallbackTitleRatio: visibleCount > 0 ? Number((fallbackTitleCount / visibleCount).toFixed(4)) : 0,
        items: visibleItems.slice(0, 60),
        fallbackItems: fallbackItems.slice(0, 12),
      };
    };
    const diagnosticSummaryText = (element) => {
      const summarySelectors = [
        ".api-error-diagnostic-summary-text p",
        ".api-error-diagnostic-summary-text",
        "[class*='diagnostic-summary-text' i] p",
        "[class*='diagnostic-summary-text' i]",
        "[data-testid*='diagnostic-summary' i]",
        "[data-testid*='error-summary' i]",
        "[role='alert'] p",
        "[role='alert']"
      ];
      const parts = [];
      for (const selector of summarySelectors) {
        for (const candidate of Array.from(element.querySelectorAll(selector))) {
          if (!visible(candidate)) continue;
          const text = trim(candidate.textContent || "", 800);
          if (text && !parts.includes(text)) parts.push(text);
        }
      }
      const ownText = textHashInput(element);
      const text = parts.length > 0 ? parts.join(" ") : ownText;
      return text.replace(/\s+(?:!|i诊断|诊断详情)$/u, "").trim();
    };
    const diagnosticToggleOnly = (element, text) => {
      const compact = String(text || "").trim();
      if (!/^(?:!|i诊断|诊断|诊断详情)$/u.test(compact)) return false;
      const tag = element.tagName.toLowerCase();
      const role = element.getAttribute("role");
      const aria = element.getAttribute("aria-label") || "";
      const title = element.getAttribute("title") || "";
      return tag === "button" || role === "button" || /诊断/u.test(aria) || /诊断/u.test(title);
    };
    const stableMessageText = (element) => {
      const bodySelectors = [
        ".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 bodySelectors) {
        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);
        }
      }
      if (parts.length > 0) return parts.join(" ");
      const clone = element.cloneNode(true);
      for (const selector of [
        ".message-duration-meta",
        ".message-activity-meta",
        ".api-error-diagnostic",
        "[class*='diagnostic' i]",
        "[class*='trace' i]",
        "[data-trace-id]",
        "[data-testid*='trace' i]",
        "[data-testid*='event' i]",
        "[role='status']",
        "[role='alert']",
        "button"
      ]) {
        for (const child of Array.from(clone.querySelectorAll(selector))) child.remove();
      }
      return trim(clone.textContent || "", 1200);
    };
    const numericAttr = (element, names) => {
      for (const name of names) {
        const raw = element.getAttribute(name);
        const value = Number(raw);
        if (Number.isFinite(value)) return value;
      }
      return null;
    };
    const textAttr = (element, names) => {
      for (const name of names) {
        const raw = element.getAttribute(name);
        if (raw && String(raw).trim()) return String(raw).trim();
      }
      return null;
    };
    const summarizeElements = (elements, limit) => elements.filter(visible).slice(-limit).map((element, index) => {
      const rect = element.getBoundingClientRect();
      const owner = element.closest('article.message-card, .message-card[data-message-id], article[data-message-id], [data-trace-id]');
      const timeElement = element.matches("time,[datetime]") ? element : element.querySelector("time,[datetime]");
      return {
        index,
        tag: element.tagName.toLowerCase(),
        testId: element.getAttribute("data-testid"),
        role: element.getAttribute("role"),
        status: element.getAttribute("data-status") || element.getAttribute("aria-busy") || null,
        sessionId: element.getAttribute("data-session-id") || owner?.getAttribute("data-session-id") || null,
        messageId: element.getAttribute("data-message-id") || owner?.getAttribute("data-message-id") || element.getAttribute("id") || null,
        traceId: element.getAttribute("data-trace-id") || owner?.getAttribute("data-trace-id") || null,
        turnId: element.getAttribute("data-turn-id") || owner?.getAttribute("data-turn-id") || null,
        projectedSeq: numericAttr(element, ["data-projected-seq", "data-projectedseq", "data-seq", "data-sequence", "aria-posinset"]),
        sourceSeq: numericAttr(element, ["data-source-seq", "data-sourceseq", "data-source-event-seq"]),
        eventSeq: numericAttr(element, ["data-event-seq", "data-eventseq"]),
        eventTimestamp: textAttr(element, ["data-event-ts", "data-event-time", "data-timestamp", "datetime"]) || (timeElement ? textAttr(timeElement, ["datetime", "data-event-ts", "data-event-time", "data-timestamp"]) : null),
        eventTimeText: timeElement ? trim(timeElement.textContent || "", 80) : null,
        eventKind: textAttr(element, ["data-event-kind", "data-kind", "data-label", "data-status"]) || element.getAttribute("aria-label") || null,
        text: textHashInput(element),
        rect: { x: Math.round(rect.x), y: Math.round(rect.y), width: Math.round(rect.width), height: Math.round(rect.height) },
      };
    });
    const summarize = (selector, limit) => summarizeElements(Array.from(document.querySelectorAll(selector)), limit);
    const summarizeMessages = (selector, limit) => Array.from(document.querySelectorAll(selector)).filter(visible).slice(-limit).map((element, index) => {
      const rect = element.getBoundingClientRect();
      return {
        index,
        tag: element.tagName.toLowerCase(),
        testId: element.getAttribute("data-testid"),
        role: element.getAttribute("role"),
        dataRole: element.getAttribute("data-role"),
        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,
        durationText: trim(element.querySelector(".message-duration-meta")?.textContent || "", 120),
        activityText: trim(element.querySelector(".message-activity-meta")?.textContent || "", 120),
        text: stableMessageText(element),
        rect: { x: Math.round(rect.x), y: Math.round(rect.y), width: Math.round(rect.width), height: Math.round(rect.height) },
      };
    });
    const resourceTimingSample = (entry) => ({
      name: entry.name.split(/[?#]/u)[0].slice(0, 240),
      initiatorType: entry.initiatorType,
      startTime: Math.round(entry.startTime),
      duration: Math.round(entry.duration),
      workerStart: Math.round(entry.workerStart || 0),
      redirectStart: Math.round(entry.redirectStart || 0),
      redirectEnd: Math.round(entry.redirectEnd || 0),
      fetchStart: Math.round(entry.fetchStart || 0),
      domainLookupStart: Math.round(entry.domainLookupStart || 0),
      domainLookupEnd: Math.round(entry.domainLookupEnd || 0),
      connectStart: Math.round(entry.connectStart || 0),
      connectEnd: Math.round(entry.connectEnd || 0),
      secureConnectionStart: Math.round(entry.secureConnectionStart || 0),
      requestStart: Math.round(entry.requestStart || 0),
      responseStart: Math.round(entry.responseStart || 0),
      responseEnd: Math.round(entry.responseEnd || 0),
      transferSize: Number.isFinite(Number(entry.transferSize)) ? Number(entry.transferSize) : null,
      encodedBodySize: Number.isFinite(Number(entry.encodedBodySize)) ? Number(entry.encodedBodySize) : null,
      decodedBodySize: Number.isFinite(Number(entry.decodedBodySize)) ? Number(entry.decodedBodySize) : null,
      nextHopProtocol: entry.nextHopProtocol || null,
      responseStatus: Number.isFinite(Number(entry.responseStatus)) ? Number(entry.responseStatus) : null,
      serverTiming: Array.from(entry.serverTiming || []).slice(0, 8).map((item) => ({
        name: String(item.name || "").slice(0, 80),
        duration: Number.isFinite(Number(item.duration)) ? Math.round(Number(item.duration)) : null,
        description: String(item.description || "").slice(0, 120)
      })),
    });
    const opaqueDomId = (value) => String(value || "").trim();
    const collectProjectManagement = () => {
      const config = input?.projectManagement || {};
      const targetPaths = Array.isArray(config.targetPaths) ? config.targetPaths : [];
      const path = location.pathname;
      const configuredPath = targetPaths.some((target) => path === target || path.startsWith(String(target) + "/"));
      const root = document.querySelector('[data-testid="project-management-root"]');
      const mdtodoRoot = document.querySelector('[data-testid="project-management-mdtodo"]');
      const rootVisible = visible(root);
      const mdtodoVisible = visible(mdtodoRoot);
      if (!configuredPath && !rootVisible && !mdtodoVisible) return null;
      const sourceItems = Array.from(document.querySelectorAll('[data-testid="mdtodo-source-list"] [data-source-id], [data-source-id]')).filter(visible);
      const fileItems = Array.from(document.querySelectorAll('[data-testid="mdtodo-file-list"] [data-file-ref], [data-file-ref]')).filter(visible);
      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: trim(option.textContent || option.label || "", 180),
        selected: option.selected === true,
      })) : [];
      const selectedFileOption = fileOptions.find((option) => option.selected) || null;
      const taskItems = Array.from(document.querySelectorAll('[data-testid="mdtodo-task-tree"] [data-task-ref], [data-task-ref]')).filter(visible);
      const taskCandidates = Array.from(document.querySelectorAll('[data-testid="mdtodo-task-tree"] li, [data-testid="mdtodo-task-tree"] [role="treeitem"], [data-testid="mdtodo-task-tree"] [role="listitem"]')).filter(visible);
      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 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 statusCounts = {};
      for (const task of taskItems) {
        const status = task.getAttribute("data-task-status") || "unknown";
        statusCounts[status] = (statusCounts[status] || 0) + 1;
      }
      const launch = document.querySelector('[data-testid="mdtodo-workbench-launch"], [data-action="launch-workbench"]');
      const bodyRendered = document.querySelector('[data-testid="mdtodo-body-rendered"]');
      const reportPreview = document.querySelector('[data-testid="mdtodo-report-preview"]');
      const reportFullscreen = document.querySelector('[data-testid="mdtodo-report-fullscreen-dialog"]');
      const reportLinks = Array.from(document.querySelectorAll('[data-testid="mdtodo-report-link"]')).filter(visible);
      const blockers = Array.from(document.querySelectorAll('[data-testid="mdtodo-workbench-launch-blocker"], [data-testid="mdtodo-workbench-launch-error"], [role="alert"]')).filter(visible).slice(0, 12).map((element, index) => ({
        index,
        testId: element.getAttribute("data-testid"),
        role: element.getAttribute("role"),
        text: trim(element.textContent || "", 260),
      })).filter((item) => item.text);
      const workbenchLinks = Array.from(document.querySelectorAll('[data-testid="mdtodo-workbench-link-summary"] li, a[href*="/workbench/sessions/"]')).filter(visible);
      const measurePaneGap = (name, paneSelector, contentSelector) => {
        const pane = document.querySelector(paneSelector);
        if (!visible(pane)) return { name, visible: false };
        const rect = pane.getBoundingClientRect();
        const contentNodes = Array.from(pane.querySelectorAll(contentSelector)).filter(visible);
        const contentBottom = Math.max(rect.top, ...contentNodes.map((element) => element.getBoundingClientRect().bottom));
        const bottomGapPx = Math.max(0, Math.round(rect.bottom - contentBottom));
        const heightPx = Math.max(0, Math.round(rect.height));
        return {
          name,
          visible: true,
          widthPx: Math.max(0, Math.round(rect.width)),
          heightPx,
          bottomGapPx,
          bottomGapRatio: heightPx > 0 ? Number((bottomGapPx / heightPx).toFixed(3)) : 0,
          contentNodeCount: contentNodes.length,
        };
      };
      const paneGaps = [
        measurePaneGap("task-tree", '[data-testid="mdtodo-task-tree"]', '[data-task-ref], [role="treeitem"], [role="listitem"], li, button, input, select, .task-row-shell, .task-tools'),
        measurePaneGap("task-detail", '[data-testid="mdtodo-task-detail"]', '[data-testid="mdtodo-body-rendered"] > *, [data-testid="mdtodo-report-section"], [data-testid="mdtodo-workbench-launch"], [data-testid="mdtodo-delete-task"], [data-testid="mdtodo-task-detail-error"], .mdtodo-detail-header, .task-status-stack > *, .task-document-footer'),
        measurePaneGap("report-sidebar", '[data-testid="mdtodo-report-sidebar"]', '[data-testid="mdtodo-report-preview"] > *, [data-testid="mdtodo-report-error"], [data-testid="mdtodo-report-fullscreen"], [data-testid="mdtodo-report-close"], .report-sidebar-header, .report-preview .markdown-body > *'),
      ];
      return {
        pageKind: mdtodoVisible || path.startsWith("/projects/mdtodo") ? "project-management-mdtodo" : rootVisible || path === "/projects" || path.startsWith("/projects/") ? "project-management-root" : "project-management-unknown",
        configuredPath,
        rootVisible,
        mdtodoVisible,
        sourceCount: Math.max(sourceItems.length, sourceOptionCount),
        fileCount: Math.max(fileItems.length, fileOptionCount),
        taskCount: taskItems.length,
        taskRefMissingCount: Math.max(0, taskCandidates.length - taskItems.length),
        selectedSourceId: opaqueDomId(selectedSource?.getAttribute("data-source-id") || sourceSelect?.value),
        selectedFileRef: opaqueDomId(selectedFile?.getAttribute("data-file-ref") || fileSelect?.value),
        selectedFileLabel: selectedFile ? trim(selectedFile.textContent || "", 180) : selectedFileOption?.label || null,
        fileOptionLabels: fileOptions.map((option) => option.label).filter(Boolean).slice(0, 24),
        selectedTaskRef: opaqueDomId(selectedTask?.getAttribute("data-task-ref")),
        selectedTaskStatus: selectedTask?.getAttribute("data-task-status") || 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"]')),
        taskBodyVisible: visible(bodyRendered),
        taskBodyText: visible(bodyRendered) ? trim(bodyRendered.textContent || "", 500) : "",
        newTaskDraftVisible: visible(document.querySelector('[data-testid="mdtodo-new-title"], [data-testid="mdtodo-new-body"]')),
        taskStatusCounts: statusCounts,
        reportLinkCount: reportLinks.length,
        reportPreviewVisible: visible(reportPreview),
        reportPreviewText: visible(reportPreview) ? trim(reportPreview.textContent || "", 500) : "",
        reportFullscreenVisible: visible(reportFullscreen),
        launchButtonVisible: visible(launch),
        launchButtonEnabled: visible(launch) && !launch.disabled && launch.getAttribute("aria-disabled") !== "true",
        launchButtonText: trim(launch?.textContent || "", 120),
        blockerCount: blockers.length,
        blockers,
        paneGaps,
        workbenchLinkCount: workbenchLinks.length,
        valuesRedacted: true,
      };
    };
    const url = location.href;
    const routeSessionMatch = url.match(/\/workbench\/sessions\/([^/?#]+)/u);
    const activeSession = document.querySelector('[data-active="true"][data-session-id], [aria-selected="true"][data-session-id], .active[data-session-id]');
    const activeSessionId = activeSession ? activeSession.getAttribute("data-session-id") : null;
    const commandInput = document.querySelector("#command-input");
    const commandSubmit = document.querySelector('#command-send, #command-submit, [data-testid="command-submit"], [data-testid="composer-submit"], [data-testid="send-command"]');
    const composerWarning = document.querySelector(".composer-warning");
    const messageSelector = 'article.message-card, .message-card[data-message-id], article[data-message-id]';
    const stableTraceSelector = 'li.trace-render-row[data-row-id], li.trace-render-row[data-testid="trace-render-row"], [data-testid="trace-render-row"][data-row-id]';
    const fallbackTraceSelector = '[data-testid*="trace" i], [class*="trace" i], [data-trace-id], [data-testid*="event" i]';
    const diagnosticSelector = '.api-error-diagnostic, [class*="api-error-diagnostic" i], [class*="message-diagnostic" i], [class*="projection-diagnostic" i], [data-testid="api-error-diagnostic" i], [data-testid="error-diagnostic" i], [data-testid*="diagnostic" i], [role="alert"], [aria-live="assertive"]';
    const messages = summarizeMessages(messageSelector, 80);
    const stableTraceElements = Array.from(document.querySelectorAll(stableTraceSelector));
    const fallbackTraceElements = Array.from(document.querySelectorAll(fallbackTraceSelector)).filter((element) => element.matches('li,[role="listitem"],[data-testid*="trace-row" i],[data-testid*="event-row" i]'));
    const traceRows = summarizeElements(stableTraceElements.length > 0 ? stableTraceElements : fallbackTraceElements, 30);
    const loadings = collectLoadingNodes();
    const sessionRail = collectSessionRailTitles();
    const diagnostics = Array.from(document.querySelectorAll(diagnosticSelector)).filter(visible).slice(-40).map((element, index) => {
      const rect = element.getBoundingClientRect();
      const text = diagnosticSummaryText(element);
      if (!text || diagnosticToggleOnly(element, text)) return null;
      const traceMatch = text.match(/\b(?:trace_id=)?(trc_[A-Za-z0-9_-]+|[a-f0-9]{16,64})\b/iu);
      const httpStatusMatch = text.match(/\bHTTP\s+([1-5][0-9]{2})\b/iu);
      const idleMatch = text.match(/\bidle\s+(\d+)s\b/iu);
      const waitingForMatch = text.match(/\bwaitingFor=([^\s；;，,)]+)/iu);
      const lastEventLabelMatch = text.match(/\blastEventLabel=([^\s；;，,)]+)/iu);
      const diagnosticCode = httpStatusMatch ? "http-" + httpStatusMatch[1] : /turn\s*超过|无新活动/iu.test(text) ? "turn-idle-no-activity" : /Failed to fetch/iu.test(text) ? "failed-to-fetch" : "diagnostic";
      return {
        index,
        tag: element.tagName.toLowerCase(),
        className: String(element.className || "").slice(0, 240),
        testId: element.getAttribute("data-testid"),
        role: element.getAttribute("role"),
        compact: element.getAttribute("data-compact"),
        expanded: element.getAttribute("data-expanded") || element.getAttribute("aria-expanded"),
        title: element.getAttribute("title"),
        diagnosticCode,
        traceId: traceMatch?.[1] || null,
        httpStatus: httpStatusMatch ? Number(httpStatusMatch[1]) : null,
        idleSeconds: idleMatch ? Number(idleMatch[1]) : null,
        waitingFor: waitingForMatch?.[1] || null,
        lastEventLabel: lastEventLabelMatch?.[1] || null,
        text,
        rect: { x: Math.round(rect.x), y: Math.round(rect.y), width: Math.round(rect.width), height: Math.round(rect.height) },
      };
    }).filter(Boolean);
    const turns = Array.from(document.querySelectorAll('article.message-card[data-role="agent"], .message-card[data-role="agent"], article[data-role="agent"]')).filter(visible).map((element, index) => {
      const rect = element.getBoundingClientRect();
      const text = textHashInput(element);
      const traceElement = element.matches("[data-trace-id]") ? element : element.querySelector("[data-trace-id]");
      const traceMatch = text.match(/\btrc_[A-Za-z0-9_-]+\b/u);
      const durationText = trim(element.querySelector(".message-duration-meta")?.textContent || "", 120);
      const activityText = trim(element.querySelector(".message-activity-meta")?.textContent || "", 120);
      const directTraceId = element.getAttribute("data-trace-id") || traceElement?.getAttribute("data-trace-id") || traceMatch?.[0] || null;
      return {
        index,
        role: element.getAttribute("data-role") || "agent",
        status: element.getAttribute("data-status") || null,
        sessionId: element.getAttribute("data-session-id") || null,
        messageId: element.getAttribute("data-message-id") || element.getAttribute("id") || null,
        traceId: directTraceId,
        turnId: element.getAttribute("data-turn-id") || directTraceId,
        durationText,
        activityText,
        text,
        rect: { x: Math.round(rect.x), y: Math.round(rect.y), width: Math.round(rect.width), height: Math.round(rect.height) },
      };
    }).slice(-80);
    const active = document.activeElement;
    return {
      url,
      path: location.pathname,
      routeSessionId: routeSessionMatch ? decodeURIComponent(routeSessionMatch[1]) : null,
      activeSessionId,
      title: document.title,
      focus: active ? { tag: active.tagName.toLowerCase(), testId: active.getAttribute("data-testid"), role: active.getAttribute("role") } : null,
      viewport: { width: window.innerWidth, height: window.innerHeight, devicePixelRatio: window.devicePixelRatio },
      scroll: { x: Math.round(window.scrollX), y: Math.round(window.scrollY), height: Math.round(document.documentElement.scrollHeight), width: Math.round(document.documentElement.scrollWidth) },
      messages,
      traceRows,
      loadings,
      sessionRail,
      diagnostics,
      turns,
      composer: {
        inputPresent: visible(commandInput),
        inputDisabled: Boolean(commandInput?.disabled) || commandInput?.getAttribute("aria-disabled") === "true",
        warningPresent: visible(composerWarning),
        warningText: trim(composerWarning?.textContent || "", 160),
        submitPresent: visible(commandSubmit),
        submitDisabled: Boolean(commandSubmit?.disabled) || commandSubmit?.getAttribute("aria-disabled") === "true",
        submitAction: commandSubmit?.getAttribute("data-action") || null,
        submitText: trim(commandSubmit?.textContent || "", 80),
        submitTestId: commandSubmit?.getAttribute("data-testid") || null,
        activeStatus: activeSession?.getAttribute("data-status") || null,
        valuesRedacted: true
      },
      projectManagement: collectProjectManagement(),
      pageProvenance: {
        url: location.href,
        path: location.pathname,
        title: document.title,
        readyState: document.readyState,
        timeOrigin: Math.round(performance.timeOrigin || 0),
        navigationStartTime: (performance.getEntriesByType("navigation")[0] || null)?.startTime ?? null,
        scripts: Array.from(document.scripts).map((element) => {
          if (!element.src) return null;
          try {
            const url = new URL(element.src, location.href);
            const keys = Array.from(url.searchParams.keys()).sort();
            return url.pathname + (keys.length > 0 ? "?keys=" + keys.join(",") : "");
          } catch {
            return null;
          }
        }).filter(Boolean).sort(),
        stylesheets: Array.from(document.querySelectorAll('link[rel~="stylesheet"][href]')).map((element) => {
          try {
            const url = new URL(element.href, location.href);
            const keys = Array.from(url.searchParams.keys()).sort();
            return url.pathname + (keys.length > 0 ? "?keys=" + keys.join(",") : "");
          } catch {
            return null;
          }
        }).filter(Boolean).sort(),
        meta: Array.from(document.querySelectorAll("meta[name], meta[property]")).map((element) => ({
          key: String(element.getAttribute("name") || element.getAttribute("property") || "").slice(0, 120),
          content: String(element.getAttribute("content") || "").slice(0, 200),
        })).filter((item) => item.key).sort((a, b) => a.key.localeCompare(b.key)),
      },
      performance: performance.getEntriesByType("resource").slice(-80).map(resourceTimingSample),
    };
  }, { projectManagement }), evaluateTimeoutMs, "sampleOnePage DOM evaluate exceeded " + evaluateTimeoutMs + "ms").catch((error) => ({ error: errorSummary(error), url: pageUrl(targetPage) }));
  const sample = {
    seq: sampleSeq,
    sampleGroupSeq: groupSeq,
    ts: new Date().toISOString(),
    reason,
    pageRole,
    pageId: targetPageId,
    pageEpoch: Number.isFinite(Number(pageEpoch)) ? Number(pageEpoch) : 0,
    commandId: activeCommandId,
    observerInitiated: false,
    ...digestDom(dom, pageRole),
  };
  await appendJsonl(files.samples, sample);
}

function digestDom(dom, pageRole = "control") {
  if (dom && dom.error) return dom;
  const messages = Array.isArray(dom.messages) ? dom.messages.map((item) => ({ ...item, textHash: sha256Text(item.text || ""), textPreview: truncate(item.text || "", 160), textBytes: Buffer.byteLength(item.text || "") })) : [];
  const traceRows = Array.isArray(dom.traceRows) ? dom.traceRows.map((item) => ({ ...item, textHash: sha256Text(item.text || ""), textPreview: truncate(item.text || "", 160), textBytes: Buffer.byteLength(item.text || "") })) : [];
  const loadings = Array.isArray(dom.loadings) ? dom.loadings.map((item) => ({ ...item, textHash: sha256Text(item.text || ""), textPreview: truncate(item.text || "", 160), textBytes: Buffer.byteLength(item.text || ""), ownerTextHash: sha256Text(item.ownerText || ""), ownerTextPreview: truncate(item.ownerText || "", 160) })) : [];
  const sessionRail = digestSessionRail(dom.sessionRail);
  const diagnostics = Array.isArray(dom.diagnostics) ? dom.diagnostics.map((item) => ({ ...item, textHash: sha256Text(item.text || ""), textPreview: truncate(item.text || "", 260), textBytes: Buffer.byteLength(item.text || "") })) : [];
  const turns = Array.isArray(dom.turns) ? dom.turns.map((item) => ({ ...item, textHash: sha256Text(item.text || ""), textPreview: truncate(item.text || "", 200), textBytes: Buffer.byteLength(item.text || "") })) : [];
  const projectManagementSample = digestProjectManagement(dom.projectManagement);
  const pageProvenance = normalizePageProvenance(dom.pageProvenance, { reason: "sample", pageLoadSeq: currentPageProvenance?.pageLoadSeq ?? pageLoadSeq });
  if (pageRole === "control") currentPageProvenance = pageProvenance;
  return { ...dom, messages, traceRows, loadings, sessionRail, diagnostics, turns, projectManagement: projectManagementSample, pageProvenance: compactPageProvenance(pageProvenance) };
}

function digestProjectManagement(value) {
  if (!value || typeof value !== "object") return null;
  const opaque = (raw) => {
    const text = String(raw || "");
    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
    };
  };
  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 {
    pageKind: value.pageKind ?? null,
    configuredPath: value.configuredPath === true,
    rootVisible: value.rootVisible === true,
    mdtodoVisible: value.mdtodoVisible === true,
    sourceCount: Number.isFinite(Number(value.sourceCount)) ? Number(value.sourceCount) : 0,
    fileCount: Number.isFinite(Number(value.fileCount)) ? Number(value.fileCount) : 0,
    taskCount: Number.isFinite(Number(value.taskCount)) ? Number(value.taskCount) : 0,
    taskRefMissingCount: Number.isFinite(Number(value.taskRefMissingCount)) ? Number(value.taskRefMissingCount) : 0,
    selectedSourceId: opaque(value.selectedSourceId),
    selectedFileRef: opaque(value.selectedFileRef),
    selectedFileLabel: value.selectedFileLabel ? textDigest(value.selectedFileLabel, 120) : null,
    selectedFileLabelLooksDirect: value.selectedFileLabel ? fileLabelLooksDirect(value.selectedFileLabel) : null,
    fileOptionLabelSamples: fileLabels.slice(0, 10).map((item) => textDigest(item, 120)),
    fileOptionSuspiciousLabelCount: fileLabels.filter(suspiciousFileLabel).length,
    selectedTaskRef: opaque(value.selectedTaskRef),
    selectedTaskStatus: value.selectedTaskStatus ?? null,
    sourceSelectVisible: value.sourceSelectVisible === true,
    fileSelectVisible: value.fileSelectVisible === true,
    sourceConfigVisible: value.sourceConfigVisible === true,
    taskEditorVisible: value.taskEditorVisible === true,
    taskBodyVisible: value.taskBodyVisible === true,
    taskBody: value.taskBodyText ? textDigest(value.taskBodyText, 200) : null,
    newTaskDraftVisible: value.newTaskDraftVisible === true,
    taskStatusCounts: value.taskStatusCounts && typeof value.taskStatusCounts === "object" ? value.taskStatusCounts : {},
    reportLinkCount: Number.isFinite(Number(value.reportLinkCount)) ? Number(value.reportLinkCount) : 0,
    reportPreviewVisible: value.reportPreviewVisible === true,
    reportPreview: value.reportPreviewText ? textDigest(value.reportPreviewText, 200) : null,
    reportFullscreenVisible: value.reportFullscreenVisible === true,
    launchButtonVisible: value.launchButtonVisible === true,
    launchButtonEnabled: value.launchButtonEnabled === true,
    launchButtonText: value.launchButtonText ? textDigest(value.launchButtonText, 120) : null,
    blockerCount: Number.isFinite(Number(value.blockerCount)) ? Number(value.blockerCount) : 0,
    blockers: Array.isArray(value.blockers) ? value.blockers.slice(0, 12).map((item) => ({
      index: item?.index ?? null,
      testId: item?.testId ?? null,
      role: item?.role ?? null,
      ...textDigest(item?.text || "", 160),
    })) : [],
    paneGaps: Array.isArray(value.paneGaps) ? value.paneGaps.slice(0, 8).map((item) => ({
      name: item?.name ?? null,
      visible: item?.visible === true,
      widthPx: Number.isFinite(Number(item?.widthPx)) ? Number(item.widthPx) : null,
      heightPx: Number.isFinite(Number(item?.heightPx)) ? Number(item.heightPx) : null,
      bottomGapPx: Number.isFinite(Number(item?.bottomGapPx)) ? Number(item.bottomGapPx) : null,
      bottomGapRatio: Number.isFinite(Number(item?.bottomGapRatio)) ? Number(item.bottomGapRatio) : null,
      contentNodeCount: Number.isFinite(Number(item?.contentNodeCount)) ? Number(item.contentNodeCount) : null,
      valuesRedacted: true,
    })) : [],
    workbenchLinkCount: Number.isFinite(Number(value.workbenchLinkCount)) ? Number(value.workbenchLinkCount) : 0,
    valuesRedacted: true
  };
}

function digestSessionRail(value) {
  if (!value || typeof value !== "object") return null;
  const items = Array.isArray(value.items) ? value.items.map((item) => {
    const titleText = String(item?.titleText || item?.titlePreview || "");
    return {
      index: item?.index ?? null,
      tag: item?.tag ?? null,
      testId: item?.testId ?? null,
      role: item?.role ?? null,
      active: item?.active === true,
      sessionId: item?.sessionId ?? null,
      sessionIdPrefix: item?.sessionIdPrefix ?? (item?.sessionId ? String(item.sessionId).slice(0, 12) : null),
      fallbackTitle: item?.fallbackTitle === true,
      titleHash: sha256Text(titleText),
      titlePreview: truncate(titleText, 160),
      titleBytes: Buffer.byteLength(titleText),
      rect: item?.rect ?? null,
    };
  }) : [];
  const fallbackItems = items.filter((item) => item.fallbackTitle).slice(0, 12);
  const visibleCount = Number(value.visibleCount ?? items.length);
  const fallbackTitleCount = Number(value.fallbackTitleCount ?? fallbackItems.length);
  return {
    visibleCount: Number.isFinite(visibleCount) ? visibleCount : items.length,
    fallbackTitleCount: Number.isFinite(fallbackTitleCount) ? fallbackTitleCount : fallbackItems.length,
    fallbackTitleRatio: Number.isFinite(Number(value.fallbackTitleRatio)) ? Number(value.fallbackTitleRatio) : (items.length > 0 ? Number((fallbackItems.length / items.length).toFixed(4)) : 0),
    items,
    fallbackItems,
    valuesRedacted: true,
  };
}

async function captureScreenshot(reason, imageType = "png") {
  if (!page || page.isClosed()) throw new Error("page is not available for screenshot");
  artifactSeq += 1;
  const safeReason = safeId(String(reason || "manual")).slice(0, 40) || "manual";
  const type = imageType === "jpeg" || imageType === "jpg" ? "jpeg" : "png";
  const ext = type === "jpeg" ? "jpg" : "png";
  const file = path.join(dirs.screenshots, String(sampleSeq).padStart(6, "0") + "_" + String(artifactSeq).padStart(4, "0") + "_" + safeReason + "." + ext);
  const options = type === "jpeg" ? { path: file, type: "jpeg", quality: 70, fullPage: false } : { path: file, type: "png", fullPage: false };
  await page.screenshot(options);
  const meta = await fileMeta(file);
  const artifact = { seq: artifactSeq, sampleSeq, ts: new Date().toISOString(), kind: "screenshot", reason, path: file, type, byteCount: meta.byteCount, sha256: meta.sha256, pageId, currentUrl: currentPageUrl() };
  await appendJsonl(files.artifacts, artifact);
  lastScreenshotAtMs = Date.now();
  return artifact;
}

async function captureCommandScreenshot(command) {
  const shouldWaitProject = command.waitProjectManagementReady === true;
  const readiness = shouldWaitProject ? await waitForProjectManagementCommandReady({ timeoutMs: 15000 }) : null;
  if (readiness && readiness.ok !== true) {
    const error = new Error("screenshot project-management readiness wait failed: " + (readiness.reason || "not-ready"));
    error.details = { readiness, currentUrl: currentPageUrl(), pageId, valuesRedacted: true };
    throw error;
  }
  const artifact = await captureScreenshot(command.reason || command.label || "manual", command.imageType || "png");
  return { ...artifact, readiness, valuesRedacted: true };
}

function eventRecord(type, data) {
  const clean = sanitize(data) || {};
  return { ts: new Date().toISOString(), type, jobId, pageId: clean.pageId ?? pageId, pageRole: clean.pageRole ?? "control", sampleSeq, commandId: activeCommandId, ...clean };
}

async function summarizeWorkbenchResponseBody(response, request) {
  const method = String(request.method() || "GET").toUpperCase();
  const path = safeUrlPath(response.url()) || "";
  const resourceType = String(request.resourceType() || "");
  const status = Number(response.status());
  if (!shouldSummarizeWorkbenchResponseBody({ method, path, resourceType, status })) return { bodyRead: false };
  const headers = response.headers();
  const contentType = String(headers["content-type"] || headers["Content-Type"] || "");
  if (!/json/iu.test(contentType)) return { bodyRead: false, bodyReadSkipped: "non-json", valuesRedacted: true };
  const contentLength = Number(headers["content-length"] || headers["Content-Length"]);
  const maxBytes = 512 * 1024;
  if (Number.isFinite(contentLength) && contentLength > maxBytes) return { bodyRead: false, bodyReadSkipped: "content-length-too-large", bodyByteCount: contentLength, valuesRedacted: true };
  const text = await response.text();
  const byteCount = Buffer.byteLength(text);
  if (byteCount > maxBytes) return { bodyRead: true, bodyReadSkipped: "body-too-large", bodyByteCount: byteCount, bodyHash: sha256Text(text), valuesRedacted: true };
  let parsed = null;
  try {
    parsed = JSON.parse(text);
  } catch (error) {
    return { bodyRead: true, bodyReadSkipped: "json-parse-error", bodyByteCount: byteCount, bodyHash: sha256Text(text), bodyParseError: errorSummary(error), valuesRedacted: true };
  }
  return {
    bodyRead: true,
    bodyByteCount: byteCount,
    bodyHash: sha256Text(text),
    bodySummary: summarizeWorkbenchJsonBody(parsed, path),
    valuesRedacted: true
  };
}

function shouldSummarizeWorkbenchResponseBody({ method, path, resourceType, status }) {
  if (method !== "GET" && method !== "POST") return false;
  if (!Number.isFinite(status) || status < 200 || status >= 300) return false;
  if (resourceType === "eventsource" || path === "/v1/workbench/events") return false;
  return path === "/v1/agent/chat"
    || path === "/v1/agent/chat/steer"
    || path === "/v1/workbench/sessions"
    || /^\/v1\/workbench\/sessions\/[^/]+\/messages$/u.test(path)
    || /^\/v1\/workbench\/turns\/[^/]+$/u.test(path)
    || /^\/v1\/workbench\/traces\/[^/]+\/events$/u.test(path);
}

function summarizeWorkbenchJsonBody(value, path) {
  const traceIds = new Set();
  const sessionIds = new Set();
  const terminalTraceIds = new Set();
  const statusCounts = {};
  const counters = {
    objectCount: 0,
    arrayCount: 0,
    traceEventLikeCount: 0,
    messageLikeCount: 0,
    turnLikeCount: 0,
    terminalStatusCount: 0,
    runningStatusCount: 0,
    terminalTextCount: 0,
    finalTextFieldCount: 0,
    finalTextByteCount: 0
  };

  const visit = (node, key = "", parent = null, depth = 0) => {
    if (depth > 32 || node === null || node === undefined) return;
    if (typeof node === "string") {
      collectWorkbenchIdsFromText(node, traceIds, sessionIds);
      const normalizedStatus = normalizeWorkbenchStatus(key, node);
      if (normalizedStatus) {
        statusCounts[normalizedStatus] = (statusCounts[normalizedStatus] || 0) + 1;
        if (isWorkbenchTerminalStatus(normalizedStatus)) counters.terminalStatusCount += 1;
        if (isWorkbenchRunningStatus(normalizedStatus)) counters.runningStatusCount += 1;
      }
      if (isWorkbenchTerminalText(node)) {
        counters.terminalTextCount += 1;
        for (const traceId of workbenchTraceIdsFromRecord(parent)) terminalTraceIds.add(traceId);
      }
      if (isLikelyWorkbenchFinalTextField(key, parent, node)) {
        counters.finalTextFieldCount += 1;
        counters.finalTextByteCount += Buffer.byteLength(node);
        for (const traceId of workbenchTraceIdsFromRecord(parent)) terminalTraceIds.add(traceId);
      }
      return;
    }
    if (typeof node !== "object") return;
    if (Array.isArray(node)) {
      counters.arrayCount += 1;
      for (const item of node) visit(item, key, parent, depth + 1);
      return;
    }
    counters.objectCount += 1;
    const record = node;
    const recordTraceIds = workbenchTraceIdsFromRecord(record);
    for (const raw of [record.traceId, record.trace_id, record.id, record.turnId, record.messageId]) {
      if (typeof raw === "string") collectWorkbenchIdsFromText(raw, traceIds, sessionIds);
    }
    for (const raw of [record.sessionId, record.session_id]) {
      if (typeof raw === "string") collectWorkbenchIdsFromText(raw, traceIds, sessionIds);
    }
    const statusValue = record.status ?? record.state ?? record.phase ?? record.result ?? record.lifecycle;
    if (typeof statusValue === "string") {
      const normalizedStatus = normalizeWorkbenchStatus("status", statusValue);
      if (normalizedStatus) {
        statusCounts[normalizedStatus] = (statusCounts[normalizedStatus] || 0) + 1;
        if (isWorkbenchTerminalStatus(normalizedStatus)) {
          counters.terminalStatusCount += 1;
          for (const traceId of recordTraceIds) terminalTraceIds.add(traceId);
        }
        if (isWorkbenchRunningStatus(normalizedStatus)) counters.runningStatusCount += 1;
      }
    }
    if (record.traceId || record.trace_id || record.turnId || record.turn_id) counters.turnLikeCount += 1;
    if (record.messageId || record.message_id || record.role || record.author || record.content || record.text || record.finalResponse) counters.messageLikeCount += 1;
    if (record.projectedSeq !== undefined || record.sourceSeq !== undefined || record.eventSeq !== undefined || record.eventKind !== undefined || record.eventTimestamp !== undefined) counters.traceEventLikeCount += 1;
    for (const [childKey, childValue] of Object.entries(record)) visit(childValue, childKey, record, depth + 1);
  };
  visit(value);
  return {
    pathKind: workbenchBodyPathKind(path),
    traceIds: Array.from(traceIds).sort().slice(0, 12),
    terminalTraceIds: Array.from(terminalTraceIds).sort().slice(0, 12),
    sessionIds: Array.from(sessionIds).sort().slice(0, 12),
    statusCounts,
    ...counters,
    terminalEvidenceCount: counters.terminalStatusCount + counters.terminalTextCount,
    valuesRedacted: true
  };
}

function collectWorkbenchIdsFromText(value, traceIds, sessionIds) {
  const text = String(value || "");
  for (const match of text.matchAll(/\btrc_[A-Za-z0-9_-]+\b/gu)) traceIds.add(match[0]);
  for (const match of text.matchAll(/\bses_[A-Za-z0-9_-]+\b/gu)) sessionIds.add(match[0]);
}

function workbenchTraceIdsFromRecord(record) {
  if (!record || typeof record !== "object") return [];
  const values = [record.traceId, record.trace_id, record.turnId, record.turn_id, record.messageId, record.message_id, record.id];
  const ids = new Set();
  for (const raw of values) {
    if (typeof raw !== "string") continue;
    for (const match of raw.matchAll(/\btrc_[A-Za-z0-9_-]+\b/gu)) ids.add(match[0]);
  }
  return Array.from(ids).sort();
}

function normalizeWorkbenchStatus(key, value) {
  if (!/status|state|phase|result|lifecycle/iu.test(String(key || ""))) return null;
  const text = String(value || "").toLowerCase().replace(/[^a-z0-9_-]+/gu, "-").replace(/^-+|-+$/gu, "");
  if (!text) return null;
  if (/^(completed|complete|succeeded|success|finished|done|terminal|sealed)$/u.test(text)) return "completed";
  if (/^(failed|failure|error|errored)$/u.test(text)) return "failed";
  if (/^(canceled|cancelled|aborted|cancel)$/u.test(text)) return "canceled";
  if (/^(running|active|in-progress|in_progress|processing|streaming|executing)$/u.test(text)) return "running";
  if (/^(queued|pending|admitted|created|waiting)$/u.test(text)) return "pending";
  return text.slice(0, 80);
}

function isWorkbenchTerminalStatus(value) {
  return value === "completed" || value === "failed" || value === "canceled";
}

function isWorkbenchRunningStatus(value) {
  return value === "running" || value === "pending";
}

function isWorkbenchTerminalText(value) {
  return /轮次完成|轮次失败|轮次取消|已记录|final response|sealed final response|turn completed|turn failed|turn canceled|terminal result|\bcompleted\b|\bfailed\b|\bcanceled\b|\bcancelled\b|\bterminal\b|\bdone\b/iu.test(String(value || ""));
}

function isLikelyWorkbenchFinalTextField(key, parent, value) {
  const text = String(value || "").trim();
  if (!text) return false;
  const field = String(key || "");
  if (!/final|assistant|response|content|markdown|text|message|output|result/iu.test(field)) return false;
  const parentStatus = normalizeWorkbenchStatus("status", parent?.status ?? parent?.state ?? parent?.phase ?? parent?.result ?? "");
  const parentRole = String(parent?.role ?? parent?.author ?? parent?.dataRole ?? "").toLowerCase();
  return isWorkbenchTerminalStatus(parentStatus) || /assistant|agent|code/iu.test(parentRole) || /final|response|result/iu.test(field);
}

function workbenchBodyPathKind(path) {
  if (path === "/v1/agent/chat" || path === "/v1/agent/chat/steer") return "agent-chat-submit";
  if (path === "/v1/workbench/sessions") return "workbench-sessions";
  if (/^\/v1\/workbench\/sessions\/[^/]+\/messages$/u.test(path)) return "workbench-session-messages";
  if (/^\/v1\/workbench\/turns\/[^/]+$/u.test(path)) return "workbench-turn";
  if (/^\/v1\/workbench\/traces\/[^/]+\/events$/u.test(path)) return "workbench-trace-events";
  return "workbench";
}

function controlRecord(command, phase, detail) {
  return {
    ts: new Date().toISOString(),
    seq: commandSeq,
    phase,
    commandId: command.id,
    type: command.type,
    source: command.source || "file",
    input: commandInputSummary(command),
    beforeUrl: command.beforeUrl || null,
    afterUrl: currentPageUrl(),
    pageId,
    detail: sanitize(detail),
  };
}

function commandInputSummary(command) {
  const text = typeof command.text === "string" ? command.text : null;
  const opaque = (value) => {
    const raw = typeof value === "string" ? value : null;
    if (!raw) return null;
    return {
      hash: sha256Text(raw),
      preview: raw.length <= 18 ? raw : raw.slice(0, 10) + "..." + raw.slice(-5),
      bytes: Buffer.byteLength(raw),
      valuesRedacted: true
    };
  };
  return {
    type: command.type,
    path: command.path || null,
    url: command.url ? safeUrl(command.url) : null,
    sessionId: command.sessionId || command.value || null,
    provider: command.provider || null,
    afterRound: Number.isInteger(Number(command.afterRound)) ? Number(command.afterRound) : null,
    severity: command.severity || null,
    alternateSessionStrategy: command.alternateSessionStrategy || null,
    expectedSentinelRange: command.expectedSentinelRange || null,
    expectedActionWaitMs: command.expectedActionWaitMs === null || command.expectedActionWaitMs === undefined || command.expectedActionWaitMs === "" ? null : Number(command.expectedActionWaitMs),
    requireComposerReady: command.requireComposerReady === true,
    waitProjectManagementReady: command.waitProjectManagementReady === true,
    findingId: command.findingId || null,
    blocking: command.blocking === true ? true : command.blocking === false ? false : null,
    sourceId: opaque(command.sourceId),
    fileRef: opaque(command.fileRef),
    filename: command.filename ? truncate(command.filename, 200) : null,
    taskRef: opaque(command.taskRef),
    taskId: command.taskId || null,
    field: command.field || null,
    link: command.link ? truncate(command.link, 200) : null,
    titleHash: command.title ? sha256Text(command.title) : null,
    titleBytes: command.title ? Buffer.byteLength(command.title) : null,
    bodyHash: command.body ? sha256Text(command.body) : null,
    bodyBytes: command.body ? Buffer.byteLength(command.body) : null,
    status: command.status || null,
    hwpodId: opaque(command.hwpodId),
    nodeId: opaque(command.nodeId),
    workspaceRoot: opaque(command.workspaceRoot),
    root: opaque(command.root),
    label: command.label ? truncate(command.label, 200) : null,
    textHash: text === null ? null : sha256Text(text),
    textBytes: text === null ? null : Buffer.byteLength(text),
    textPreview: null,
    valuesRedacted: true,
  };
}

async function appendJsonl(file, value) {
  await appendFile(file, JSON.stringify(sanitize(value)) + "\n", { mode: 0o600 });
}

async function fileMeta(file) {
  const [buffer, stats] = await Promise.all([readFile(file), stat(file)]);
  return { byteCount: stats.size, sha256: "sha256:" + createHash("sha256").update(buffer).digest("hex") };
}

function currentPageUrl() {
  return pageUrl(page);
}

function pageUrl(targetPage) {
  try { return targetPage && !targetPage.isClosed() ? targetPage.url() : null; } catch { return null; }
}

function routeSessionIdFromUrl(value) {
  try {
    const pathname = new URL(String(value || ""), baseUrl).pathname;
    const match = pathname.match(/\/workbench\/sessions\/([^/?#]+)/u);
    return match ? decodeURIComponent(match[1] || "") : null;
  } catch {
    return null;
  }
}

function safeFrameUrl(frame) {
  try { return frame ? safeUrl(frame.url()) : null; } catch { return null; }
}

function safeUrl(value) {
  try {
    const url = new URL(String(value), baseUrl);
    for (const key of Array.from(url.searchParams.keys())) {
      if (/token|key|secret|password|auth|cookie/iu.test(key)) url.searchParams.set(key, "[redacted]");
    }
    return url.toString();
  } catch {
    return truncate(String(value || ""), 300);
  }
}

function safeUrlPath(value) {
  try {
    return new URL(String(value || ""), baseUrl).pathname;
  } catch {
    return null;
  }
}

function normalizeBaseUrl(value) {
  const raw = value || "http://127.0.0.1:3000";
  const url = new URL(raw);
  return url.origin;
}

function parseViewport(value) {
  const match = String(value).match(/^(\d{3,5})x(\d{3,5})$/u);
  return match ? { width: Number(match[1]), height: Number(match[2]) } : { width: 1440, height: 900 };
}

function positiveInteger(value, fallback) {
  const parsed = Number(value);
  return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : fallback;
}

function boundedInteger(value, fallback, min, max) {
  const parsed = Number(value);
  if (!Number.isFinite(parsed)) return fallback;
  const integer = Math.floor(parsed);
  if (integer < min || integer > max) return fallback;
  return integer;
}

function positiveNumber(value, fallback) {
  const parsed = Number(value);
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
}

function requiredPositiveThreshold(raw, key) {
  const parsed = Number(raw?.[key]);
  if (!Number.isFinite(parsed) || parsed <= 0) {
    throw new Error("UNIDESK_WEB_OBSERVE_ALERT_THRESHOLDS_JSON requires positive " + key + "; configure config/hwlab-node-lanes.yaml webProbe.alertThresholds");
  }
  return parsed;
}

function parseAlertThresholds(value) {
  if (!value) {
    throw new Error("UNIDESK_WEB_OBSERVE_ALERT_THRESHOLDS_JSON is required; configure config/hwlab-node-lanes.yaml webProbe.alertThresholds for the selected node/lane");
  }
  const raw = (() => {
    try { return JSON.parse(value); } catch (error) { throw new Error("UNIDESK_WEB_OBSERVE_ALERT_THRESHOLDS_JSON is invalid JSON: " + (error instanceof Error ? error.message : String(error))); }
  })();
  const sessionRailFallbackRatio = requiredPositiveThreshold(raw, "sessionRailFallbackRatio");
  if (sessionRailFallbackRatio > 1) {
    throw new Error("UNIDESK_WEB_OBSERVE_ALERT_THRESHOLDS_JSON sessionRailFallbackRatio must be <= 1");
  }
  return {
    sameOriginApiSlowMs: requiredPositiveThreshold(raw, "sameOriginApiSlowMs"),
    partialApiSlowMs: requiredPositiveThreshold(raw, "partialApiSlowMs"),
    longLivedStreamOpenSlowMs: requiredPositiveThreshold(raw, "longLivedStreamOpenSlowMs"),
    visibleLoadingSlowMs: requiredPositiveThreshold(raw, "visibleLoadingSlowMs"),
    turnTimingSampleSlackSeconds: requiredPositiveThreshold(raw, "turnTimingSampleSlackSeconds"),
    turnElapsedSevereTimeoutSeconds: requiredPositiveThreshold(raw, "turnElapsedSevereTimeoutSeconds"),
    uncommandedStateChangeCommandWindowMs: requiredPositiveThreshold(raw, "uncommandedStateChangeCommandWindowMs"),
    scrollJumpCommandWindowMs: requiredPositiveThreshold(raw, "scrollJumpCommandWindowMs"),
    scrollJumpFromY: requiredPositiveThreshold(raw, "scrollJumpFromY"),
    scrollJumpToY: requiredPositiveThreshold(raw, "scrollJumpToY"),
    sessionRailFallbackRatio,
    crossPageProjectionDivergenceRedMs: positiveNumber(raw.crossPageProjectionDivergenceRedMs, requiredPositiveThreshold(raw, "visibleLoadingSlowMs")),
    source: "yaml-env",
  };
}

function parseProjectManagementConfig(value) {
  if (!value || value === "null") {
    return {
      enabled: false,
      targetPaths: [],
      readinessSelectors: [],
      naturalApiPathPrefixes: [],
      commandAllowlist: [],
      launchRoute: "",
      slowApiBudgetMs: 0,
      source: "yaml-env",
      valuesRedacted: true
    };
  }
  const raw = (() => {
    try { return JSON.parse(value); } catch (error) { throw new Error("UNIDESK_WEB_OBSERVE_PROJECT_MANAGEMENT_JSON is invalid JSON: " + (error instanceof Error ? error.message : String(error))); }
  })();
  const stringList = (key) => {
    const list = raw?.[key];
    if (!Array.isArray(list) || list.some((item) => typeof item !== "string" || item.length === 0)) throw new Error("UNIDESK_WEB_OBSERVE_PROJECT_MANAGEMENT_JSON requires string[] " + key + "; configure config/hwlab-node-lanes.yaml webProbe.projectManagement");
    return list;
  };
  const positive = (key) => {
    const numeric = Number(raw?.[key]);
    if (!Number.isFinite(numeric) || numeric <= 0) throw new Error("UNIDESK_WEB_OBSERVE_PROJECT_MANAGEMENT_JSON requires positive " + key + "; configure config/hwlab-node-lanes.yaml webProbe.projectManagement");
    return numeric;
  };
  if (raw?.enabled !== true && raw?.enabled !== false) throw new Error("UNIDESK_WEB_OBSERVE_PROJECT_MANAGEMENT_JSON requires boolean enabled");
  if (raw.enabled !== true) return { enabled: false, targetPaths: [], readinessSelectors: [], naturalApiPathPrefixes: [], commandAllowlist: [], launchRoute: "", slowApiBudgetMs: 0, source: "yaml-env", valuesRedacted: true };
  const targetPaths = stringList("targetPaths");
  const readinessSelectors = stringList("readinessSelectors");
  const naturalApiPathPrefixes = stringList("naturalApiPathPrefixes");
  const commandAllowlist = stringList("commandAllowlist");
  const launchRoute = String(raw.launchRoute || "");
  if (!launchRoute.startsWith("/")) throw new Error("UNIDESK_WEB_OBSERVE_PROJECT_MANAGEMENT_JSON launchRoute must be an absolute path");
  return {
    enabled: true,
    targetPaths,
    readinessSelectors,
    naturalApiPathPrefixes,
    commandAllowlist,
    launchRoute,
    slowApiBudgetMs: positive("slowApiBudgetMs"),
    source: "yaml-env",
    valuesRedacted: true
  };
}

function sha256Text(value) {
  return "sha256:" + createHash("sha256").update(String(value)).digest("hex");
}

function safeId(value) {
  return String(value || "").replace(/[^A-Za-z0-9_.-]+/gu, "-").replace(/^-+|-+$/gu, "").slice(0, 120) || "item";
}

function cssEscape(value) {
  return String(value).replace(/\\/gu, "\\\\").replace(/"/gu, "\\\"");
}

function truncate(value, limit) {
  const text = String(value || "");
  return text.length > limit ? text.slice(0, limit) + "..." : text;
}

function sanitize(value) {
  if (value === null || value === undefined) return value;
  if (typeof value === "string") return value === password ? "[redacted]" : value.replaceAll(password, "[redacted]");
  if (typeof value === "number" || typeof value === "boolean") return value;
  if (Array.isArray(value)) return value.map(sanitize);
  if (typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, item]) => /password|cookie|authorization|token|secret/iu.test(key) ? [key, "[redacted]"] : [key, sanitize(item)]));
  return String(value);
}

function errorSummary(error) {
  const summary = { name: error && error.name ? error.name : "Error", message: error && error.message ? truncate(error.message, 1000) : truncate(String(error), 1000), stackTail: error && error.stack ? truncate(error.stack, 2000) : null };
  if (error && error.webProbeAuth) summary.auth = sanitize(error.webProbeAuth);
  if (error && Array.isArray(error.attempts)) summary.attempts = sanitize(error.attempts.slice(-5));
  if (error && error.navigationReadiness) summary.navigationReadiness = sanitize(error.navigationReadiness);
  if (error && error.details) summary.details = sanitize(error.details);
  if (error && error.target) summary.target = sanitize(error.target);
  return summary;
}

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
}

function withHardTimeout(promise, timeoutMs, message) {
  const guarded = Promise.resolve(promise);
  let timer = null;
  const timeout = new Promise((_, reject) => {
    timer = setTimeout(() => reject(new Error(message)), Math.max(1, Number(timeoutMs) || 1));
    if (timer && typeof timer.unref === "function") timer.unref();
  });
  return Promise.race([guarded, timeout]).finally(() => {
    if (timer) clearTimeout(timer);
    guarded.catch(() => {});
  });
}
`;
}
