codex-chatgpt-control 0.5.1-alpha.5 → 0.5.1-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -215,10 +215,10 @@ function classifyVisibleText(text) {
215
215
  var en = {
216
216
  // --- Primary interaction path (accessible names) ---
217
217
  composerTextbox: ["Chat with ChatGPT", "Ask ChatGPT"],
218
- workComposerTextbox: ["Work on anything", "Work on something"],
218
+ workComposerTextbox: ["Work on anything", "Work on something", "Work with ChatGPT"],
219
219
  projectComposerPrefixes: ["New chat in"],
220
220
  newWork: ["Work on something else", "New work", "New task"],
221
- sendButton: ["Send prompt"],
221
+ sendButton: ["Send prompt", "Send"],
222
222
  searchChatsButton: ["Search chats"],
223
223
  searchChatsPlaceholder: ["Search chats..."],
224
224
  newChat: ["New chat"],
@@ -291,7 +291,7 @@ var en = {
291
291
  /** Exact-match transient assistant placeholders filtered out of captured responses. */
292
292
  transientAssistant: ["thinking", "reasoning", "searching", "searching the web"],
293
293
  /** Streaming "stop" control text, matched while a response generates. */
294
- stopControl: ["stop generating", "stop streaming", "stop answering"],
294
+ stopControl: ["stop generating", "stop streaming", "stop answering", "stop"],
295
295
  /** Interrupted generation markers shown after the assistant stops before completion. */
296
296
  stoppedAssistant: ["stopped thinking", "stopped answering", "generation stopped"],
297
297
  /** Response-action affordance text (fallback to the structural copy-button locator). */
@@ -6433,6 +6433,13 @@ function normalizePage(pageOrTab) {
6433
6433
  const value = providerValue(primary, property) ?? providerValue(maybe, property);
6434
6434
  if (isProviderRecord(value)) normalized[property] = value;
6435
6435
  }
6436
+ const rawAx = providerValue(maybe, "ax");
6437
+ if (normalized.keyboard === void 0 && isProviderRecord(rawAx)) {
6438
+ const pressKey = providerCallable(rawAx, "pressKey");
6439
+ if (pressKey !== void 0) normalized.keyboard = { press: async (key) => {
6440
+ await pressKey(null, key);
6441
+ } };
6442
+ }
6436
6443
  if (isProviderRecord(embedded)) normalized.playwright = embedded;
6437
6444
  for (const method of [
6438
6445
  "url",
@@ -6889,10 +6896,19 @@ function extractRoleMessageHtml(html) {
6889
6896
  const root = parseHtmlFragment(html);
6890
6897
  const messages = [];
6891
6898
  walkElementsWithAncestors(root, [], (element, ancestors) => {
6892
- const role = element.attrs["data-message-author-role"];
6899
+ const current = element.attrs["data-message-author-role"] === void 0 && element.attrs["data-chatgpt-search-message-ids"] !== void 0;
6900
+ if (current && !ancestors.some((ancestor) => ancestor.tag === "main")) return;
6901
+ const role = element.attrs["data-message-author-role"] ?? (current ? element.attrs["data-chatgpt-search-unit-key"]?.split(":").at(-1) : void 0);
6893
6902
  if (role === "user" || role === "assistant") {
6894
6903
  const metadataElement = [...ancestors].reverse().find((ancestor) => ancestor.attrs["data-testid"]?.startsWith("conversation-turn")) ?? element;
6895
- messages.push({ role, html: serializeChildren(element), metadataHtml: serializeNode(metadataElement) });
6904
+ let content = element;
6905
+ if (current) {
6906
+ walkElements(element, (child) => {
6907
+ if (role === "user" && child.attrs["data-user-message-bubble"] === "true" || role === "assistant" && child.attrs["data-markdown-text-style"] === "assistant-message" && child.children.length > 0) content = child;
6908
+ });
6909
+ }
6910
+ const messageHtml = serializeChildren(content).replace(/<h4\b[^>]*data-conversation-role=["']assistant["'][^>]*>[\s\S]*?<\/h4>/i, "");
6911
+ messages.push({ role, html: messageHtml, metadataHtml: serializeNode(metadataElement) });
6896
6912
  }
6897
6913
  });
6898
6914
  return messages;
@@ -7427,15 +7443,19 @@ function extractMessagesFromHtml(html, args = {}) {
7427
7443
  async function readMessages(page, args = {}) {
7428
7444
  if (typeof page.evaluate === "function") {
7429
7445
  const messages = await page.evaluate(() => {
7430
- const nodes = Array.from(document.querySelectorAll("[data-message-author-role]"));
7446
+ const legacy = Array.from(document.querySelectorAll("[data-message-author-role]"));
7447
+ const nodes = legacy.length > 0 ? legacy : Array.from(document.querySelectorAll(
7448
+ "main [data-chatgpt-search-unit-key][data-chatgpt-search-message-ids]"
7449
+ ));
7431
7450
  return nodes.map((node) => {
7432
- const role = node.getAttribute("data-message-author-role");
7451
+ const role = node.getAttribute("data-message-author-role") ?? node.getAttribute("data-chatgpt-search-unit-key")?.split(":").at(-1);
7433
7452
  if (role !== "user" && role !== "assistant") {
7434
7453
  return void 0;
7435
7454
  }
7455
+ const content = role === "user" ? node.querySelector?.('[data-user-message-bubble="true"]') : node.querySelector?.('[data-markdown-text-style="assistant-message"]');
7436
7456
  return {
7437
7457
  role,
7438
- html: node.innerHTML,
7458
+ html: content?.innerHTML || node.innerHTML.replace(/<h4\b[^>]*data-conversation-role=["']assistant["'][^>]*>[\s\S]*?<\/h4>/i, ""),
7439
7459
  metadataHtml: node.closest("[data-testid^='conversation-turn']")?.outerHTML ?? node.outerHTML
7440
7460
  };
7441
7461
  }).filter(Boolean);
@@ -7451,12 +7471,16 @@ async function readMessages(page, args = {}) {
7451
7471
  async function readLatestMessage(page, role = "assistant", format = "markdown", maxChars) {
7452
7472
  if (typeof page.evaluate === "function") {
7453
7473
  const message = await page.evaluate((wantedRole) => {
7454
- const nodes = Array.from(document.querySelectorAll(`[data-message-author-role="${wantedRole}"]`));
7474
+ const legacy = Array.from(document.querySelectorAll(`[data-message-author-role="${wantedRole}"]`));
7475
+ const nodes = legacy.length > 0 ? legacy : Array.from(document.querySelectorAll(
7476
+ `main [data-chatgpt-search-unit-key$=":${wantedRole}"][data-chatgpt-search-message-ids]`
7477
+ ));
7455
7478
  const node = nodes.at(-1);
7456
7479
  if (node === void 0) return void 0;
7480
+ const content = wantedRole === "user" ? node.querySelector?.('[data-user-message-bubble="true"]') : node.querySelector?.('[data-markdown-text-style="assistant-message"]');
7457
7481
  return {
7458
7482
  role: wantedRole,
7459
- html: node.innerHTML,
7483
+ html: content?.innerHTML || node.innerHTML.replace(/<h4\b[^>]*data-conversation-role=["']assistant["'][^>]*>[\s\S]*?<\/h4>/i, ""),
7460
7484
  metadataHtml: node.closest("[data-testid^='conversation-turn']")?.outerHTML ?? node.outerHTML
7461
7485
  };
7462
7486
  }, role).catch(() => void 0);
@@ -7475,9 +7499,15 @@ async function readLatestMessage(page, role = "assistant", format = "markdown",
7475
7499
  async function readLatestMessageText(page, role = "assistant") {
7476
7500
  if (typeof page.evaluate === "function") {
7477
7501
  return page.evaluate((wantedRole) => {
7478
- const nodes = Array.from(document.querySelectorAll(`[data-message-author-role="${wantedRole}"]`));
7502
+ const legacy = Array.from(document.querySelectorAll(`[data-message-author-role="${wantedRole}"]`));
7503
+ const nodes = legacy.length > 0 ? legacy : Array.from(document.querySelectorAll(
7504
+ `main [data-chatgpt-search-unit-key$=":${wantedRole}"][data-chatgpt-search-message-ids]`
7505
+ ));
7479
7506
  const node = nodes.at(-1);
7480
- return node?.innerText ?? node?.textContent ?? void 0;
7507
+ if (node?.getAttribute("data-chatgpt-search-unit-key") === null) return node?.innerText ?? node?.textContent ?? void 0;
7508
+ if (node === void 0) return void 0;
7509
+ const content = wantedRole === "user" ? node.querySelector?.('[data-user-message-bubble="true"]') : node.querySelector?.('[data-markdown-text-style="assistant-message"]');
7510
+ return content?.innerText || content?.textContent || (node.innerText ?? node.textContent ?? "").replace(node.querySelector?.('[data-conversation-role="assistant"]')?.textContent ?? "", "").trim();
7481
7511
  }, role).catch(() => void 0);
7482
7512
  }
7483
7513
  return readLatestMessage(page, role, "normalized_text").then((message) => message?.text).catch(() => void 0);
@@ -7485,10 +7515,14 @@ async function readLatestMessageText(page, role = "assistant") {
7485
7515
  async function readLatestMessageTextSnapshot(page, role) {
7486
7516
  if (typeof page.evaluate === "function") {
7487
7517
  return page.evaluate((wantedRole) => {
7488
- const allNodes = Array.from(document.querySelectorAll("[data-message-author-role]"));
7489
- const roleNodes = allNodes.filter((node) => node.getAttribute("data-message-author-role") === wantedRole);
7518
+ const legacy = Array.from(document.querySelectorAll("[data-message-author-role]"));
7519
+ const allNodes = legacy.length > 0 ? legacy : Array.from(document.querySelectorAll(
7520
+ "main [data-chatgpt-search-unit-key][data-chatgpt-search-message-ids]"
7521
+ )).filter((node) => /:(?:user|assistant)$/.test(node.getAttribute("data-chatgpt-search-unit-key") ?? ""));
7522
+ const roleNodes = allNodes.filter((node) => (node.getAttribute("data-message-author-role") ?? node.getAttribute("data-chatgpt-search-unit-key")?.split(":").at(-1)) === wantedRole);
7490
7523
  const latest = roleNodes.at(-1);
7491
- const latestText2 = latest?.innerText ?? latest?.textContent ?? void 0;
7524
+ const content = wantedRole === "user" ? latest?.querySelector?.('[data-user-message-bubble="true"]') : latest?.querySelector?.('[data-markdown-text-style="assistant-message"]');
7525
+ const latestText2 = content?.innerText || content?.textContent || (latest === void 0 ? void 0 : (latest.innerText ?? latest.textContent ?? "").replace(latest.querySelector?.('[data-conversation-role="assistant"]')?.textContent ?? "", "").trim());
7492
7526
  const snapshot2 = { turnCount: allNodes.length };
7493
7527
  if (latestText2 !== void 0) snapshot2.latestText = latestText2;
7494
7528
  return snapshot2;
@@ -7512,7 +7546,10 @@ async function countPageMessages(page, role) {
7512
7546
  if (typeof page.evaluate === "function") {
7513
7547
  return page.evaluate((wantedRole) => {
7514
7548
  const selector = wantedRole === void 0 ? "[data-message-author-role]" : `[data-message-author-role="${wantedRole}"]`;
7515
- return document.querySelectorAll(selector).length;
7549
+ const legacyCount = document.querySelectorAll(selector).length;
7550
+ if (legacyCount > 0) return legacyCount;
7551
+ const currentSelector = wantedRole === void 0 ? 'main [data-chatgpt-search-unit-key$=":user"][data-chatgpt-search-message-ids], main [data-chatgpt-search-unit-key$=":assistant"][data-chatgpt-search-message-ids]' : `main [data-chatgpt-search-unit-key$=":${wantedRole}"][data-chatgpt-search-message-ids]`;
7552
+ return document.querySelectorAll(currentSelector).length;
7516
7553
  }, role);
7517
7554
  }
7518
7555
  return countMessages(await readMessages(page), role);
@@ -7720,8 +7757,9 @@ var generatedArtifactDownloadClauses = [
7720
7757
  ];
7721
7758
  var cssSelectors = {
7722
7759
  assistantMessages: "[data-message-author-role='assistant']",
7723
- userMessages: "[data-message-author-role='user']",
7724
- roleMessages: "[data-message-author-role]",
7760
+ currentAssistantMessages: "main [data-chatgpt-search-unit-key$=':assistant'][data-chatgpt-search-message-ids]",
7761
+ userMessages: "[data-message-author-role='user'], main [data-chatgpt-search-unit-key$=':user'][data-chatgpt-search-message-ids]",
7762
+ roleMessages: "[data-message-author-role], main [data-chatgpt-search-unit-key$=':user'][data-chatgpt-search-message-ids], main [data-chatgpt-search-unit-key$=':assistant'][data-chatgpt-search-message-ids]",
7725
7763
  conversationTurns: "[data-testid^='conversation-turn']",
7726
7764
  hiddenFileInputs: "input[type='file']",
7727
7765
  downloadControls: downloadControlClauses.join(", "),
@@ -8758,8 +8796,11 @@ async function readAssistantGenerationState(page, options = {}) {
8758
8796
  const visibleStopButtons = Array.from(document.querySelectorAll("button")).filter((button) => isVisible(button) && button.disabled !== true && button.getAttribute("aria-disabled") !== "true" && isScopedStopControl(button) && matchingLabels2(button, args.send).length === 0 && matchingLabels2(button, args.stop).length > 0);
8759
8797
  const activeSignals = [...new Set(visibleStopButtons.flatMap((button) => matchingLabels2(button, args.stop)))];
8760
8798
  const turns = Array.from(document.querySelectorAll("[data-testid^='conversation-turn']"));
8761
- const latestAssistant = Array.from(document.querySelectorAll("[data-message-author-role='assistant']")).at(-1);
8762
- const latestTurn = latestAssistant?.closest("[data-testid^='conversation-turn']") ?? turns.at(-1);
8799
+ const legacyAssistants = Array.from(document.querySelectorAll("[data-message-author-role='assistant']"));
8800
+ const latestAssistant = (legacyAssistants.length > 0 ? legacyAssistants : Array.from(document.querySelectorAll(
8801
+ 'main [data-chatgpt-search-unit-key$=":assistant"][data-chatgpt-search-message-ids]'
8802
+ ))).at(-1);
8803
+ const latestTurn = latestAssistant?.closest("[data-testid^='conversation-turn']") ?? turns.at(-1) ?? (latestAssistant?.hasAttribute?.("data-chatgpt-search-unit-key") === true ? latestAssistant : void 0);
8763
8804
  const stoppedSignals = latestTurn === void 0 ? [] : [...new Set(Array.from(latestTurn.querySelectorAll(
8764
8805
  "button, [role='status'], [aria-label], [title], p, span, div"
8765
8806
  )).filter((element) => isVisible(element)).flatMap((element) => matchingLabels2(element, args.stopped)))];
@@ -8797,7 +8838,17 @@ async function latestAssistantTurnHasResponseActions(page) {
8797
8838
  if (typeof page.evaluate === "function") {
8798
8839
  const scoped = await page.evaluate((phrases) => {
8799
8840
  const turns = Array.from(document.querySelectorAll("[data-testid^='conversation-turn']"));
8800
- if (turns.length === 0) return void 0;
8841
+ if (turns.length === 0) {
8842
+ const latest = Array.from(document.querySelectorAll('main [data-chatgpt-search-unit-key$=":assistant"][data-chatgpt-search-message-ids]')).at(-1);
8843
+ if (latest === void 0) return void 0;
8844
+ let owner = latest;
8845
+ for (let depth = 0; owner !== null && depth < 6 && owner.tagName !== "MAIN"; depth += 1, owner = owner.parentElement) {
8846
+ const assistants = Array.from(owner.querySelectorAll('[data-chatgpt-search-unit-key$=":assistant"][data-chatgpt-search-message-ids]'));
8847
+ if (owner !== latest && (assistants.length !== 1 || assistants[0] !== latest)) break;
8848
+ if (Array.from(owner.querySelectorAll("button")).some((button) => phrases.some((phrase) => [button.innerText, button.textContent, button.getAttribute("aria-label"), button.getAttribute("title")].some((value) => (value ?? "").trim().toLowerCase() === phrase.trim().toLowerCase())))) return true;
8849
+ }
8850
+ return false;
8851
+ }
8801
8852
  const latestTurn = turns.reverse().find(
8802
8853
  (turn) => turn.querySelector("[data-message-author-role='assistant']") !== null
8803
8854
  );
@@ -8967,11 +9018,15 @@ async function readWaitDomSnapshot(page) {
8967
9018
  void __combinedWaitSnapshot;
8968
9019
  const normalizeWs = (value) => value.replace(/\s+/g, " ").trim();
8969
9020
  const normalizeLower = (value) => (value ?? "").trim().toLowerCase();
8970
- const nodes = Array.from(document.querySelectorAll("[data-message-author-role]"));
8971
- const assistantNodes = nodes.filter((node) => node.getAttribute("data-message-author-role") === "assistant");
9021
+ const legacy = Array.from(document.querySelectorAll("[data-message-author-role]"));
9022
+ const nodes = legacy.length > 0 ? legacy : Array.from(document.querySelectorAll(
9023
+ "main [data-chatgpt-search-unit-key][data-chatgpt-search-message-ids]"
9024
+ )).filter((node) => /:(?:user|assistant)$/.test(node.getAttribute("data-chatgpt-search-unit-key") ?? ""));
9025
+ const assistantNodes = nodes.filter((node) => (node.getAttribute("data-message-author-role") ?? node.getAttribute("data-chatgpt-search-unit-key")?.split(":").at(-1)) === "assistant");
8972
9026
  const latestAssistant = assistantNodes.at(-1);
8973
9027
  const latestAssistantTurnIndex = latestAssistant === void 0 ? void 0 : nodes.indexOf(latestAssistant) + 1;
8974
- const normalizedText = normalizeWs(latestAssistant?.innerText ?? latestAssistant?.textContent ?? "");
9028
+ const content = latestAssistant?.querySelector?.('[data-markdown-text-style="assistant-message"]');
9029
+ const normalizedText = normalizeWs(content?.innerText || content?.textContent || (latestAssistant?.innerText ?? latestAssistant?.textContent ?? "").replace(latestAssistant?.querySelector?.('[data-conversation-role="assistant"]')?.textContent ?? "", ""));
8975
9030
  let hash = 2166136261;
8976
9031
  for (let index = 0; index < normalizedText.length; index += 1) {
8977
9032
  hash ^= normalizedText.charCodeAt(index);
@@ -9009,7 +9064,7 @@ async function readWaitDomSnapshot(page) {
9009
9064
  };
9010
9065
  const visibleStopButtons = Array.from(document.querySelectorAll("button")).filter((button) => isVisible(button) && button.disabled !== true && button.getAttribute("aria-disabled") !== "true" && isScopedStopControl(button) && matchingLabels2(button, args.send).length === 0 && matchingLabels2(button, args.stop).length > 0);
9011
9066
  const activeSignals = [...new Set(visibleStopButtons.flatMap((button) => matchingLabels2(button, args.stop)))];
9012
- const latestAssistantTurn = latestAssistant?.closest("[data-testid^='conversation-turn']") ?? Array.from(document.querySelectorAll("[data-testid^='conversation-turn']")).at(-1);
9067
+ const latestAssistantTurn = latestAssistant?.closest("[data-testid^='conversation-turn']") ?? Array.from(document.querySelectorAll("[data-testid^='conversation-turn']")).at(-1) ?? (latestAssistant?.hasAttribute?.("data-chatgpt-search-unit-key") === true ? latestAssistant : void 0);
9013
9068
  const stoppedSignals = latestAssistantTurn === void 0 ? [] : [...new Set(Array.from(latestAssistantTurn.querySelectorAll(
9014
9069
  "button, [role='status'], [aria-label], [title], p, span, div"
9015
9070
  )).filter((element) => isVisible(element)).flatMap((element) => matchingLabels2(element, args.stopped)))];
@@ -9022,7 +9077,18 @@ async function readWaitDomSnapshot(page) {
9022
9077
  const turns = Array.from(document.querySelectorAll("[data-testid^='conversation-turn']"));
9023
9078
  let hasResponseActions;
9024
9079
  if (turns.length === 0) {
9025
- hasResponseActions = void 0;
9080
+ if (latestAssistant?.hasAttribute?.("data-chatgpt-search-unit-key") === true) {
9081
+ hasResponseActions = false;
9082
+ let owner = latestAssistant;
9083
+ for (let depth = 0; owner !== null && depth < 6 && owner.tagName !== "MAIN"; depth += 1, owner = owner.parentElement) {
9084
+ const assistants = Array.from(owner.querySelectorAll('[data-chatgpt-search-unit-key$=":assistant"][data-chatgpt-search-message-ids]'));
9085
+ if (owner !== latestAssistant && (assistants.length !== 1 || assistants[0] !== latestAssistant)) break;
9086
+ if (Array.from(owner.querySelectorAll("button")).some((button) => args.actions.some((phrase) => [button.getAttribute("aria-label"), button.getAttribute("title"), button.textContent].some((value) => (value ?? "").trim().toLowerCase() === phrase.trim().toLowerCase())))) {
9087
+ hasResponseActions = true;
9088
+ break;
9089
+ }
9090
+ }
9091
+ }
9026
9092
  } else {
9027
9093
  const latestTurn = [...turns].reverse().find(
9028
9094
  (turn) => turn.querySelector("[data-message-author-role='assistant']") !== null
@@ -11552,8 +11618,13 @@ async function readSurfaceSnapshot(page) {
11552
11618
  };
11553
11619
  const normalize2 = (value) => value.replace(/\s+/g, " ").trim();
11554
11620
  const normalizeComparable = (value) => normalize2(value).toLocaleLowerCase();
11555
- const wantedSurfaceLabels = new Set(surfaceOptionLabels.map(normalizeComparable));
11556
- const composerRoots = Array.from(document.querySelectorAll(
11621
+ const chatSurfaceLabels = new Set(surfaceOptionLabels.chat.map(normalizeComparable));
11622
+ const workSurfaceLabels = new Set(surfaceOptionLabels.work.map(normalizeComparable));
11623
+ const wantedSurfaceLabels = /* @__PURE__ */ new Set([...chatSurfaceLabels, ...workSurfaceLabels]);
11624
+ const currentComposerRoots = Array.from(document.querySelectorAll(
11625
+ 'main form[data-thread-find-composer="true"]'
11626
+ )).filter(visible);
11627
+ const composerRoots = currentComposerRoots.length > 0 ? currentComposerRoots : Array.from(document.querySelectorAll(
11557
11628
  "main form, main [data-testid*='composer' i], main [class*='composer' i]"
11558
11629
  )).filter(visible);
11559
11630
  const main = document.querySelector("main");
@@ -11600,9 +11671,19 @@ async function readSurfaceSnapshot(page) {
11600
11671
  "h1, h2, h3, form, [data-testid*='composer' i], [class*='composer' i]"
11601
11672
  )).filter(visible).slice(0, 32);
11602
11673
  const mainText = normalize2(surfaceTextNodes.map(labelFor).join(" ")).slice(0, 2e3);
11603
- const selectedSurfaceLabels = Array.from(new Set(Array.from(document.querySelectorAll(
11674
+ const selectedRadioLabels = Array.from(document.querySelectorAll(
11604
11675
  "[role='radio'][aria-checked='true'], [role='radio'][data-state='checked'], input[type='radio']:checked"
11605
- )).filter(visible).map(labelFor).map(normalize2).filter((label) => wantedSurfaceLabels.has(normalizeComparable(label))))).slice(0, 4);
11676
+ )).filter(visible).map(labelFor).map(normalize2).filter((label) => wantedSurfaceLabels.has(normalizeComparable(label)));
11677
+ const pressedGroups = Array.from(document.querySelectorAll("main [role='group']")).filter(visible).filter((group) => {
11678
+ let ancestor = group;
11679
+ for (let depth = 0; ancestor !== null && depth < 64; depth += 1) {
11680
+ if (ancestor.hasAttribute("data-message-author-role") || ancestor.tagName === "ARTICLE" || ["dialog", "menu", "listbox"].includes(ancestor.getAttribute("role") ?? "")) return false;
11681
+ ancestor = ancestor.parentElement;
11682
+ }
11683
+ return ancestor === null;
11684
+ }).map((group) => Array.from(group.querySelectorAll("button[aria-pressed]")).filter((button) => button.parentElement === group && visible(button) && button.getAttribute("disabled") === null && button.getAttribute("aria-disabled") !== "true")).filter((buttons) => buttons.length === 2 && new Set(buttons.map((button) => normalizeComparable(labelFor(button)))).size === 2 && buttons.every((button) => wantedSurfaceLabels.has(normalizeComparable(labelFor(button))) && ["true", "false"].includes(button.getAttribute("aria-pressed") ?? "")) && buttons.filter((button) => chatSurfaceLabels.has(normalizeComparable(labelFor(button)))).length === 1 && buttons.filter((button) => workSurfaceLabels.has(normalizeComparable(labelFor(button)))).length === 1 && buttons.filter((button) => button.getAttribute("aria-pressed") === "true").length === 1);
11685
+ const selectedButtonLabels = pressedGroups.length === 1 ? pressedGroups[0].filter((button) => button.getAttribute("aria-pressed") === "true").map(labelFor).map(normalize2) : [];
11686
+ const selectedSurfaceLabels = Array.from(/* @__PURE__ */ new Set([...selectedRadioLabels, ...selectedButtonLabels])).slice(0, 4);
11606
11687
  const workPopoverOpen = Array.from(document.querySelectorAll(
11607
11688
  '[data-testid="composer-intelligence-picker-content"]'
11608
11689
  )).filter(visible).some((root) => {
@@ -11613,10 +11694,10 @@ async function readSurfaceSnapshot(page) {
11613
11694
  return speed.length === 1 && sliders.length === 1;
11614
11695
  });
11615
11696
  return { composerLabels, mainControls, composerControls, controlGroups, mainText, selectedSurfaceLabels, workPopoverOpen };
11616
- }, [
11617
- ...localeLabels.experienceOptions.chat,
11618
- ...localeLabels.experienceOptions.work
11619
- ]).catch(() => ({ composerLabels: [], mainControls: [], mainText: "", selectedSurfaceLabels: [] }));
11697
+ }, {
11698
+ chat: localeLabels.experienceOptions.chat,
11699
+ work: localeLabels.experienceOptions.work
11700
+ }).catch(() => ({ composerLabels: [], mainControls: [], mainText: "", selectedSurfaceLabels: [] }));
11620
11701
  return { url, ...snapshot };
11621
11702
  }
11622
11703
  function profileFromSnapshot(snapshot, experience) {
@@ -12258,7 +12339,7 @@ async function readChatPopover(page) {
12258
12339
  return current2 === null;
12259
12340
  };
12260
12341
  const normalize2 = (value) => value.replace(/\s+/g, " ").trim();
12261
- const roots = document.querySelectorAll('[data-testid="composer-intelligence-picker-content"]');
12342
+ const roots = Array.from(document.querySelectorAll('[data-testid="composer-intelligence-picker-content"]')).concat(Array.from(document.querySelectorAll("[data-model-picker-view]")));
12262
12343
  if (roots.length > 8) return { acted: false };
12263
12344
  const activeRoots = Array.from(roots).filter((node) => visible(node));
12264
12345
  if (activeRoots.length !== 1) return { acted: false };
@@ -12274,7 +12355,12 @@ async function readChatPopover(page) {
12274
12355
  ancestor = ancestor.parentNode;
12275
12356
  }
12276
12357
  if (menu?.getAttribute("data-state") !== "open" || !visible(menu)) return { acted: false };
12277
- const nodes = [];
12358
+ const currentProfile = root.hasAttribute("data-model-picker-view");
12359
+ if (currentProfile) {
12360
+ const triggers = Array.from(document.querySelectorAll('button[data-codex-intelligence-trigger="true"]')).filter((trigger) => trigger.getAttribute("id") === menu.getAttribute("aria-labelledby") && trigger.getAttribute("aria-haspopup") === "menu" && trigger.getAttribute("aria-expanded") === "true");
12361
+ if (triggers.length !== 1) return { acted: false };
12362
+ }
12363
+ const nodes = [root];
12278
12364
  const text = /* @__PURE__ */ new Map();
12279
12365
  let current = root.firstChild;
12280
12366
  let count = 0;
@@ -12308,25 +12394,28 @@ async function readChatPopover(page) {
12308
12394
  }
12309
12395
  return false;
12310
12396
  };
12311
- const owners = nodes.filter((node) => node.getAttribute("data-has-slider") === "true" && node.getAttribute("data-has-advanced-view") === "true" && node.getAttribute("data-model-selection-view") === "true" && visible(node));
12397
+ const owners = currentProfile ? [root] : nodes.filter((node) => node.getAttribute("data-has-slider") === "true" && node.getAttribute("data-has-advanced-view") === "true" && node.getAttribute("data-model-selection-view") === "true" && visible(node));
12312
12398
  if (owners.length !== 1) return { acted: false };
12313
12399
  const owner = owners[0];
12314
- const view = owner.getAttribute("data-view");
12400
+ const view = owner.getAttribute(currentProfile ? "data-model-picker-view" : "data-view");
12315
12401
  if (view !== "simple" && view !== "advanced") return { acted: false };
12316
- const panels = nodes.filter((node) => node.getAttribute("data-testid") === `composer-model-picker-slider-${view}-view` && within(node, owner) && node.getAttribute("data-active") === "true" && visible(node));
12402
+ const panels = nodes.filter((node) => (currentProfile ? node.parentNode === owner : node.getAttribute("data-testid") === `composer-model-picker-slider-${view}-view` && within(node, owner)) && node.getAttribute("data-active") === "true" && visible(node));
12317
12403
  if (panels.length !== 1) return { acted: false };
12318
12404
  const panel = panels[0];
12319
- const toggles = nodes.filter((node) => node.getAttribute("role") === "menuitem" && node.getAttribute("data-interactive") === "true" && node.getAttribute("aria-expanded") === String(view === "advanced") && within(node, owner) && visible(node));
12405
+ const toggles = nodes.filter((node) => node.getAttribute("role") === "menuitem" && node.getAttribute("data-interactive") === "true" && (currentProfile ? node.getAttribute("data-model-picker-view-toggle") === "true" : node.getAttribute("aria-expanded") === String(view === "advanced")) && within(node, owner) && visible(node));
12320
12406
  if (toggles.length > 1 || view === "simple" && toggles.length !== 1) return { acted: false };
12321
12407
  const modelNodes = view === "advanced" ? nodes.filter((node) => within(node, panel) && node.getAttribute("role") === "menuitemradio" && visible(node)) : [];
12322
12408
  if (modelNodes.length > 32) return { acted: false };
12323
12409
  const modelLabel = (node) => {
12324
- const primary = nodes.filter((child) => within(child, node) && (child.getAttribute("class") ?? "").split(/\s+/).includes("truncate"));
12410
+ const primary = nodes.filter((child) => {
12411
+ const classes = (child.getAttribute("class") ?? "").split(/\s+/);
12412
+ return within(child, node) && classes.includes("truncate") && !classes.includes("text-xs");
12413
+ });
12325
12414
  return normalize2(primary.length === 1 ? text.get(primary[0]) ?? "" : text.get(node) ?? "");
12326
12415
  };
12327
12416
  const snapshot = {
12328
12417
  view,
12329
- rootIndex: Array.from(roots).indexOf(root),
12418
+ rootIndex: currentProfile ? Array.from(document.querySelectorAll("[data-model-picker-view]")).indexOf(root) : Array.from(document.querySelectorAll('[data-testid="composer-intelligence-picker-content"]')).indexOf(root),
12330
12419
  toggleIndex: nodes.filter((node) => node.getAttribute("role") === "menuitem" && node.getAttribute("data-interactive") === "true").indexOf(toggles[0]),
12331
12420
  modelOptions: modelNodes.map((node) => ({
12332
12421
  label: modelLabel(node),
@@ -12334,6 +12423,7 @@ async function readChatPopover(page) {
12334
12423
  index: nodes.filter((row) => within(row, panel) && row.getAttribute("role") === "menuitemradio").indexOf(node)
12335
12424
  })).filter((option) => option.label.length > 0)
12336
12425
  };
12426
+ if (currentProfile) snapshot.profile = "model_picker_v2";
12337
12427
  const triggerId = menu.getAttribute("aria-labelledby");
12338
12428
  if (triggerId !== null && triggerId.length > 0 && triggerId.length < 240 && !/\s/.test(triggerId)) snapshot.triggerId = triggerId;
12339
12429
  const checked = snapshot.modelOptions.filter((option) => option.checked);
@@ -12352,7 +12442,7 @@ async function readChatPopover(page) {
12352
12442
  if (sliders.length === 1) {
12353
12443
  const slider = sliders[0];
12354
12444
  const powers = nodes.filter((node) => within(slider, node) && within(node, panel) && node.getAttribute("role") === "menuitem" && visible(node) && config.powerLabels.some((label) => normalize2(node.getAttribute("aria-label") ?? "").toLocaleLowerCase() === label.toLocaleLowerCase()));
12355
- const sliderOwners = nodes.filter((node) => within(slider, node) && within(node, panel) && node.hasAttribute("data-model-reasoning-effort-slider"));
12445
+ const sliderOwners = nodes.filter((node) => within(slider, node) && within(node, panel) && node.hasAttribute(currentProfile ? "data-model-picker-power-slider" : "data-model-reasoning-effort-slider"));
12356
12446
  const integer = (name) => {
12357
12447
  const raw = slider.getAttribute(name);
12358
12448
  return raw !== null && /^-?\d+$/.test(raw) && Number.isSafeInteger(Number(raw)) ? Number(raw) : void 0;
@@ -12381,11 +12471,46 @@ async function readChatPopover(page) {
12381
12471
  return observation !== void 0 && observation !== null && typeof observation.acted === "boolean" ? observation : { acted: false };
12382
12472
  }
12383
12473
  function observedRoot(page, snapshot) {
12384
- const roots = page.locator?.('[data-testid="composer-intelligence-picker-content"]');
12474
+ const roots = page.locator?.(snapshot.profile === "model_picker_v2" ? "[data-model-picker-view]" : '[data-testid="composer-intelligence-picker-content"]');
12385
12475
  return roots?.nth?.(snapshot.rootIndex) ?? (snapshot.rootIndex === 0 ? roots : void 0);
12386
12476
  }
12477
+ async function openChatPopover(page) {
12478
+ if ((await readChatPopover(page)).snapshot !== void 0) return true;
12479
+ let trigger = page.locator?.('button[data-codex-intelligence-trigger="true"][aria-haspopup="menu"]');
12480
+ let count = await trigger?.count?.().catch(() => 0) ?? 0;
12481
+ if (count === 0) return void 0;
12482
+ if (count > 1 && count <= 8 && typeof trigger?.filter === "function") {
12483
+ trigger = trigger.filter({ visible: true });
12484
+ count = await trigger.count?.().catch(() => 0) ?? 0;
12485
+ }
12486
+ if (count !== 1) return false;
12487
+ if (trigger?.click === void 0 || trigger.evaluate === void 0) return void 0;
12488
+ const actionable = await trigger.evaluate((element) => {
12489
+ if (element.tagName !== "BUTTON" || element.getAttribute("data-codex-intelligence-trigger") !== "true" || element.getAttribute("aria-haspopup") !== "menu") return void 0;
12490
+ let current = element;
12491
+ for (let depth = 0; current !== null && depth < 64; depth += 1) {
12492
+ if (current.nodeType !== 1) break;
12493
+ const node = current;
12494
+ if (node.hidden || node.hasAttribute("hidden") || node.hasAttribute("inert") || node.disabled || node.getAttribute("aria-hidden") === "true" || node.getAttribute("aria-disabled") === "true") return false;
12495
+ const style = window.getComputedStyle(node);
12496
+ if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0" || current === element && style.pointerEvents === "none") return false;
12497
+ current = current.parentNode;
12498
+ }
12499
+ if (current?.nodeType === 1) return false;
12500
+ const rect = element.getBoundingClientRect();
12501
+ return rect.width > 0 && rect.height > 0 && element.getAttribute("aria-expanded") === "false";
12502
+ }).catch(() => void 0);
12503
+ if (actionable === void 0) return void 0;
12504
+ if (!actionable) return false;
12505
+ await trigger.click();
12506
+ for (let attempt = 0; attempt < 5; attempt += 1) {
12507
+ await waitForPopoverTransition(page, 100);
12508
+ if ((await readChatPopover(page)).snapshot !== void 0) return true;
12509
+ }
12510
+ return false;
12511
+ }
12387
12512
  async function clickObservedControl(page, snapshot, modelIndex) {
12388
- const selector = modelIndex === void 0 ? '[role="menuitem"][data-interactive="true"]' : '[data-testid="composer-model-picker-slider-advanced-view"][data-active="true"] [role="menuitemradio"]';
12513
+ const selector = modelIndex === void 0 ? '[role="menuitem"][data-interactive="true"]' : snapshot.profile === "model_picker_v2" ? ':scope > [data-active="true"] [role="menuitemradio"]' : '[data-testid="composer-model-picker-slider-advanced-view"][data-active="true"] [role="menuitemradio"]';
12389
12514
  const candidates = observedRoot(page, snapshot)?.locator?.(selector);
12390
12515
  const index = modelIndex ?? snapshot.toggleIndex;
12391
12516
  const target = candidates?.nth?.(index) ?? (index === 0 ? candidates : void 0);
@@ -12399,7 +12524,7 @@ async function clickObservedControl(page, snapshot, modelIndex) {
12399
12524
  if (node.hidden || node.hasAttribute("hidden") || node.hasAttribute("inert") || node.getAttribute("aria-hidden") === "true" || node.getAttribute("aria-disabled") === "true" || node.getAttribute("data-active") === "false") return void 0;
12400
12525
  const style = window.getComputedStyle(node);
12401
12526
  if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0" || style.pointerEvents === "none") return void 0;
12402
- if (node.getAttribute("data-testid") === "composer-intelligence-picker-content") owned = true;
12527
+ if (node.getAttribute("data-testid") === "composer-intelligence-picker-content" || node.getAttribute("data-model-picker-view") === "simple" || node.getAttribute("data-model-picker-view") === "advanced") owned = true;
12403
12528
  current = current.parentNode;
12404
12529
  }
12405
12530
  const rect = element.getBoundingClientRect();
@@ -12415,7 +12540,8 @@ async function clickObservedControl(page, snapshot, modelIndex) {
12415
12540
  if (label.length > 512) return void 0;
12416
12541
  let parent = child.parentNode;
12417
12542
  for (let depth = 0; parent !== null && parent !== element && depth < 64; depth += 1) {
12418
- if (parent.nodeType === 1 && (parent.getAttribute("class") ?? "").split(/\s+/).includes("truncate")) {
12543
+ const classes = parent.nodeType === 1 ? (parent.getAttribute("class") ?? "").split(/\s+/) : [];
12544
+ if (classes.includes("truncate") && !classes.includes("text-xs")) {
12419
12545
  primary.set(parent, (primary.get(parent) ?? "") + (child.nodeValue ?? ""));
12420
12546
  }
12421
12547
  parent = parent.parentNode;
@@ -12429,9 +12555,9 @@ async function clickObservedControl(page, snapshot, modelIndex) {
12429
12555
  if (child === element || child === null) break;
12430
12556
  child = child.nextSibling;
12431
12557
  }
12432
- return { role: element.getAttribute("role"), expanded: element.getAttribute("aria-expanded"), label: (primary.size === 1 ? [...primary.values()][0] : label).replace(/\s+/g, " ").trim() };
12558
+ return { role: element.getAttribute("role"), expanded: element.getAttribute("aria-expanded"), viewToggle: element.getAttribute("data-model-picker-view-toggle"), label: (primary.size === 1 ? [...primary.values()][0] : label).replace(/\s+/g, " ").trim() };
12433
12559
  }).catch(() => void 0);
12434
- if (state === void 0 || state.role !== (modelIndex === void 0 ? "menuitem" : "menuitemradio") || modelIndex === void 0 && state.expanded !== String(snapshot.view === "advanced")) return false;
12560
+ if (state === void 0 || state.role !== (modelIndex === void 0 ? "menuitem" : "menuitemradio") || modelIndex === void 0 && (snapshot.profile === "model_picker_v2" ? state.viewToggle !== "true" : state.expanded !== String(snapshot.view === "advanced"))) return false;
12435
12561
  const expectedModel = snapshot.modelOptions.find((option) => option.index === modelIndex);
12436
12562
  if (modelIndex !== void 0 && (expectedModel === void 0 || state.label !== expectedModel.label)) return false;
12437
12563
  await target.click();
@@ -12442,9 +12568,9 @@ async function closeChatPopover(page, before) {
12442
12568
  if (snapshot === void 0) return false;
12443
12569
  const escapedId = snapshot.triggerId?.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
12444
12570
  const menu = escapedId === void 0 ? void 0 : page.locator?.(`[role="menu"][aria-labelledby="${escapedId}"]`);
12445
- if (menu?.press !== void 0 && await menu.count?.() === 1) await menu.press("Escape");
12446
- else if (page.keyboard?.press !== void 0) await page.keyboard.press("Escape");
12571
+ if (page.keyboard?.press !== void 0) await page.keyboard.press("Escape");
12447
12572
  else if (page.cua?.keypress !== void 0) await page.cua.keypress({ keys: ["ESC"] });
12573
+ else if (menu?.press !== void 0 && await menu.count?.() === 1) await menu.press("Escape");
12448
12574
  else return false;
12449
12575
  for (let attempt = 0; attempt < 5; attempt += 1) {
12450
12576
  await waitForPopoverTransition(page, 100);
@@ -12459,6 +12585,7 @@ async function waitForPopoverTransition(page, milliseconds) {
12459
12585
  async function reopenChatPopover(page, before) {
12460
12586
  if (before.triggerId === void 0) return void 0;
12461
12587
  if (!await closeChatPopover(page, before)) return void 0;
12588
+ if (before.profile === "model_picker_v2") await waitForPopoverTransition(page, 250);
12462
12589
  const trigger = page.locator?.(`[id="${before.triggerId.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"]`);
12463
12590
  if (trigger?.click === void 0 || trigger.evaluate === void 0 || await trigger.count?.() !== 1) return void 0;
12464
12591
  const isVisible = () => trigger.evaluate((element) => {
@@ -12555,7 +12682,7 @@ async function selectChatPopoverEffort(page, labels) {
12555
12682
  return void 0;
12556
12683
  }
12557
12684
  if (before.slider.current === target) return before;
12558
- const candidates = observedRoot(page, before)?.locator?.('[data-testid="composer-model-picker-slider-simple-view"][data-active="true"] [role="slider"]');
12685
+ const candidates = observedRoot(page, before)?.locator?.(before.profile === "model_picker_v2" ? ':scope > [data-active="true"] [role="slider"]' : '[data-testid="composer-model-picker-slider-simple-view"][data-active="true"] [role="slider"]');
12559
12686
  const locator = candidates?.nth?.(before.slider.index) ?? (before.slider.index === 0 ? candidates : void 0);
12560
12687
  if (locator?.press === void 0 || locator.evaluate === void 0 || await locator.count?.() !== 1) return void 0;
12561
12688
  const locatorState = await locator.evaluate((element) => {
@@ -12569,6 +12696,12 @@ async function selectChatPopoverEffort(page, labels) {
12569
12696
  if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0" || style.pointerEvents === "none") return void 0;
12570
12697
  if (node.getAttribute("data-testid") === "composer-model-picker-slider-simple-view" && node.getAttribute("data-active") === "true") panel = true;
12571
12698
  if (node.getAttribute("data-testid") === "composer-intelligence-picker-content") owned = true;
12699
+ if (node.getAttribute("data-model-picker-view") === "simple") {
12700
+ owned = true;
12701
+ let track = element;
12702
+ while (track !== null && track.parentNode !== node) track = track.parentNode;
12703
+ if (track?.nodeType === 1 && track.getAttribute("data-active") === "true") panel = true;
12704
+ }
12572
12705
  current = current.parentNode;
12573
12706
  }
12574
12707
  const rect = element.getBoundingClientRect();
@@ -12909,7 +13042,8 @@ async function getMode(env, args = {}) {
12909
13042
  }
12910
13043
  const page = env.page;
12911
13044
  try {
12912
- const modes = await visibleModeButtonLabelList(page);
13045
+ const popover = (await readChatPopover(page)).snapshot;
13046
+ const modes = popover?.effort === void 0 ? await visibleModeButtonLabelList(page) : [popover.effort];
12913
13047
  const warnings = modes.length === 0 ? ["No mode-labelled composer control is currently visible, so the active ChatGPT mode could not be read."] : [];
12914
13048
  return resultOk({ modes }, await contextFromPage(page), warnings);
12915
13049
  } catch (error) {
@@ -12996,6 +13130,8 @@ async function clickFirstUniqueButton(page, labels) {
12996
13130
  return false;
12997
13131
  }
12998
13132
  async function clickModeOpener(page, modeButtons) {
13133
+ const currentPicker = await openChatPopover(page);
13134
+ if (currentPicker !== void 0) return currentPicker;
12999
13135
  if (await clickFirstUniqueButton(page, modeButtons)) {
13000
13136
  return true;
13001
13137
  }
@@ -13971,6 +14107,10 @@ async function selectChatAxis(env, axis, requested, timeoutMs) {
13971
14107
  }
13972
14108
  async function openConfigurationRoot(page, experience) {
13973
14109
  if (experience !== "unknown" && (await readChatPopover(page)).snapshot !== void 0) return true;
14110
+ if (experience !== "unknown") {
14111
+ const currentPicker = await openChatPopover(page);
14112
+ if (currentPicker !== void 0) return currentPicker;
14113
+ }
13974
14114
  const existing = await readConfigurationPanel(page);
13975
14115
  if (existing.axisRows.length > 0) {
13976
14116
  return true;
@@ -15480,12 +15620,16 @@ async function tryGeneratedFilePreviewDownload(page, args) {
15480
15620
  const selected = selectGeneratedFileAffordance(candidates, args);
15481
15621
  if (selected === void 0) return void 0;
15482
15622
  try {
15483
- const assistantMessages = requiredLocator(page, cssSelectors.assistantMessages);
15484
- const assistantCount = await locatorCountWithTimeout(
15623
+ let assistantMessages = requiredLocator(page, cssSelectors.assistantMessages);
15624
+ let assistantCount = await locatorCountWithTimeout(
15485
15625
  assistantMessages,
15486
15626
  localGuardTimeout(timeoutMs, 5e3),
15487
15627
  "generated_file_assistant_count_timeout"
15488
15628
  );
15629
+ if (assistantCount === 0) {
15630
+ assistantMessages = requiredLocator(page, cssSelectors.currentAssistantMessages);
15631
+ assistantCount = await locatorCountWithTimeout(assistantMessages, localGuardTimeout(timeoutMs, 5e3), "generated_file_assistant_count_timeout");
15632
+ }
15489
15633
  if (selected.assistantIndex < 0 || selected.assistantIndex >= assistantCount) {
15490
15634
  throw new Error("The selected generated-file assistant turn is no longer present.");
15491
15635
  }
@@ -15513,11 +15657,12 @@ async function tryGeneratedFilePreviewDownload(page, args) {
15513
15657
  }
15514
15658
  await affordance.click({ timeoutMs: localGuardTimeout(timeoutMs, 1e4) });
15515
15659
  const labelledPreview = requiredLocator(page, `section[aria-label="${escapeCssAttribute(selected.filename)}"]`);
15660
+ const currentPreview = page.getByRole?.("tabpanel", { name: selected.filename, exact: true });
15516
15661
  const workbookPreviews = requiredLocator(page, "section[data-testid^='popcorn-']");
15517
15662
  const workbookPreview = typeof workbookPreviews.filter === "function" ? workbookPreviews.filter({ hasText: selected.filename }) : void 0;
15518
15663
  const download = await waitForPreviewDownloadControl(
15519
15664
  page,
15520
- workbookPreview === void 0 ? [labelledPreview] : [labelledPreview, workbookPreview],
15665
+ [...currentPreview === void 0 ? [] : [currentPreview], labelledPreview, ...workbookPreview === void 0 ? [] : [workbookPreview]],
15521
15666
  timeoutMs
15522
15667
  );
15523
15668
  if (download === void 0) {
@@ -15558,17 +15703,20 @@ async function inspectGeneratedFileAffordances(page, timeoutMs) {
15558
15703
  const prefix = downloadLabels.map((label) => label.trim()).filter(Boolean).sort((left, right) => right.length - left.length).find((label) => lowered.startsWith(`${label.toLocaleLowerCase()} `));
15559
15704
  return prefix === void 0 ? trimmed : trimmed.slice(prefix.length).trim();
15560
15705
  };
15561
- const assistants = Array.from(document.querySelectorAll("[data-message-author-role='assistant']"));
15706
+ const legacyAssistants = Array.from(document.querySelectorAll("[data-message-author-role='assistant']"));
15707
+ const assistants = legacyAssistants.length > 0 ? legacyAssistants : Array.from(document.querySelectorAll(
15708
+ 'main [data-chatgpt-search-unit-key$=":assistant"][data-chatgpt-search-message-ids]'
15709
+ ));
15562
15710
  return assistants.flatMap(
15563
15711
  (assistant, assistantIndex) => Array.from(assistant.querySelectorAll("button[aria-label], a[download], a[href*='/backend-api/files/']")).filter(visible).map((element) => {
15564
15712
  const controlLabel = (element.getAttribute("aria-label") ?? element.textContent ?? "").trim();
15565
15713
  const text = (element.textContent ?? "").trim();
15566
15714
  return {
15567
15715
  assistantIndex,
15568
- filename: normalizedFilename(controlLabel),
15716
+ filename: controlLabel.startsWith("Open preview of ") ? controlLabel.slice("Open preview of ".length) : normalizedFilename(controlLabel),
15569
15717
  controlLabel,
15570
15718
  tag: element.tagName.toLocaleLowerCase(),
15571
- textFilename: normalizedFilename(text)
15719
+ textFilename: controlLabel.startsWith("Open preview of ") ? element.parentElement?.querySelector("[title]")?.getAttribute("title") ?? "" : normalizedFilename(text)
15572
15720
  };
15573
15721
  }).filter((item) => (item.tag === "button" || item.tag === "a") && fileLike(item.filename) && item.filename === item.textFilename).map(({ assistantIndex: index, filename, controlLabel, tag }) => ({
15574
15722
  assistantIndex: index,