@oh-my-pi/pi-coding-agent 16.1.8 → 16.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/cli.js +4775 -3942
  3. package/dist/types/cli/flag-tables.d.ts +17 -0
  4. package/dist/types/cli-commands.d.ts +4 -2
  5. package/dist/types/config/settings-schema.d.ts +10 -0
  6. package/dist/types/edit/streaming.d.ts +5 -5
  7. package/dist/types/modes/controllers/command-controller.d.ts +1 -1
  8. package/dist/types/modes/controllers/selector-controller.d.ts +0 -1
  9. package/dist/types/modes/interactive-mode.d.ts +1 -1
  10. package/dist/types/modes/types.d.ts +1 -1
  11. package/dist/types/sdk.d.ts +1 -0
  12. package/dist/types/session/agent-session.d.ts +13 -0
  13. package/dist/types/system-prompt.d.ts +2 -0
  14. package/dist/types/tools/browser/aria/aria-snapshot.d.ts +39 -0
  15. package/dist/types/tools/browser/cmux/cmux-tab.d.ts +3 -0
  16. package/dist/types/tools/browser/launch.d.ts +5 -0
  17. package/dist/types/tools/browser.d.ts +1 -0
  18. package/dist/types/tools/find.d.ts +1 -2
  19. package/dist/types/tools/write.d.ts +1 -2
  20. package/package.json +12 -12
  21. package/scripts/generate-aria-snapshot.ts +134 -0
  22. package/src/cli/args.ts +0 -1
  23. package/src/cli/flag-tables.ts +42 -0
  24. package/src/cli/profile-bootstrap.ts +1 -11
  25. package/src/cli-commands.ts +48 -3
  26. package/src/config/model-registry.ts +6 -7
  27. package/src/config/settings-schema.ts +12 -0
  28. package/src/edit/streaming.ts +5 -5
  29. package/src/internal-urls/docs-index.generated.txt +1 -1
  30. package/src/lsp/client.ts +39 -25
  31. package/src/modes/controllers/command-controller.ts +18 -3
  32. package/src/modes/controllers/event-controller.ts +20 -0
  33. package/src/modes/controllers/mcp-command-controller.ts +40 -12
  34. package/src/modes/controllers/selector-controller.ts +3 -5
  35. package/src/modes/interactive-mode.ts +1 -1
  36. package/src/modes/types.ts +1 -1
  37. package/src/prompts/system/project-prompt.md +2 -0
  38. package/src/prompts/system/system-prompt.md +0 -1
  39. package/src/prompts/tools/browser.md +2 -0
  40. package/src/sdk.ts +8 -1
  41. package/src/session/agent-session.ts +36 -1
  42. package/src/slash-commands/builtin-registry.ts +24 -66
  43. package/src/system-prompt.ts +15 -3
  44. package/src/tools/bash.ts +3 -3
  45. package/src/tools/browser/aria/aria-snapshot.bundle.txt +7 -0
  46. package/src/tools/browser/aria/aria-snapshot.ts +103 -0
  47. package/src/tools/browser/cmux/cmux-tab.ts +36 -1
  48. package/src/tools/browser/launch.ts +107 -31
  49. package/src/tools/browser/tab-worker.ts +89 -17
  50. package/src/tools/browser.ts +5 -0
  51. package/src/tools/find.ts +1 -2
  52. package/src/tools/index.ts +1 -1
  53. package/src/tools/puppeteer/00_stealth_tampering.txt +16 -35
  54. package/src/tools/puppeteer/01_stealth_activity.txt +79 -19
  55. package/src/tools/puppeteer/02_stealth_hairline.txt +57 -11
  56. package/src/tools/puppeteer/03_stealth_botd.txt +10 -14
  57. package/src/tools/puppeteer/04_stealth_iframe.txt +156 -63
  58. package/src/tools/puppeteer/05_stealth_webgl.txt +222 -64
  59. package/src/tools/puppeteer/06_stealth_screen.txt +255 -67
  60. package/src/tools/puppeteer/07_stealth_fonts.txt +17 -15
  61. package/src/tools/puppeteer/08_stealth_audio.txt +32 -20
  62. package/src/tools/puppeteer/09_stealth_locale.txt +45 -40
  63. package/src/tools/puppeteer/10_stealth_plugins.txt +12 -8
  64. package/src/tools/puppeteer/11_stealth_hardware.txt +57 -6
  65. package/src/tools/puppeteer/12_stealth_codecs.txt +20 -18
  66. package/src/tools/puppeteer/13_stealth_worker.txt +206 -45
  67. package/src/tools/write.ts +1 -2
  68. package/src/utils/git.ts +3 -2
@@ -22,6 +22,12 @@ import { resizeImage } from "../../utils/image-resize";
22
22
  import { resolveToCwd } from "../path-utils";
23
23
  import { formatScreenshot } from "../render-utils";
24
24
  import { ToolAbortError, ToolError, throwIfAborted } from "../tool-errors";
25
+ import {
26
+ type AriaSnapshotOptions,
27
+ captureAriaSnapshot,
28
+ parseAriaRefSelector,
29
+ resolveAriaRefHandle,
30
+ } from "./aria/aria-snapshot";
25
31
  import {
26
32
  applyStealthPatches,
27
33
  applyViewport,
@@ -106,6 +112,7 @@ interface TabApi {
106
112
  opts?: { waitUntil?: "load" | "domcontentloaded" | "networkidle0" | "networkidle2" },
107
113
  ): Promise<void>;
108
114
  observe(opts?: { includeAll?: boolean; viewportOnly?: boolean }): Promise<Observation>;
115
+ ariaSnapshot(selector?: string, opts?: AriaSnapshotOptions): Promise<string>;
109
116
  screenshot(opts?: ScreenshotOptions): Promise<ScreenshotResult>;
110
117
  extract(format?: ReadableFormat): Promise<string>;
111
118
  click(selector: string): Promise<void>;
@@ -128,6 +135,7 @@ interface TabApi {
128
135
  opts?: { timeout?: number },
129
136
  ): Promise<HTTPResponse>;
130
137
  id(n: number): Promise<ElementHandle>;
138
+ ref(id: string): Promise<ElementHandle>;
131
139
  }
132
140
 
133
141
  function normalizeSelector(selector: string): string {
@@ -792,6 +800,28 @@ export class WorkerCore {
792
800
  );
793
801
  }),
794
802
  observe: opts => op("tab.observe()", quickOpMs, sig => this.#collectObservation({ ...opts, signal: sig })),
803
+ ariaSnapshot: (selector, opts) =>
804
+ op(
805
+ selector ? `tab.ariaSnapshot(${JSON.stringify(selector)})` : "tab.ariaSnapshot()",
806
+ quickOpMs,
807
+ async sig => {
808
+ let root: ElementHandle | null = null;
809
+ if (selector) {
810
+ root = (await untilAborted(sig, () =>
811
+ page.$(normalizeSelector(selector)),
812
+ )) as ElementHandle | null;
813
+ if (!root)
814
+ throw new ToolError(
815
+ `tab.ariaSnapshot: selector ${JSON.stringify(selector)} matched no element`,
816
+ );
817
+ }
818
+ try {
819
+ return await untilAborted(sig, () => captureAriaSnapshot(page, root, opts));
820
+ } finally {
821
+ await root?.dispose().catch(() => undefined);
822
+ }
823
+ },
824
+ ),
795
825
  screenshot: opts =>
796
826
  op(describeScreenshot(opts), quickOpMs, sig =>
797
827
  this.#captureScreenshot(session, displays, screenshots, sig, opts),
@@ -815,25 +845,50 @@ export class WorkerCore {
815
845
  }),
816
846
  click: selector =>
817
847
  op(`tab.click(${JSON.stringify(selector)})`, INF, async sig => {
848
+ if (parseAriaRefSelector(selector) !== null) {
849
+ const handle = await this.#resolveAriaRef(selector);
850
+ try {
851
+ await untilAborted(sig, () => handle.click());
852
+ } finally {
853
+ await handle.dispose().catch(() => undefined);
854
+ }
855
+ return;
856
+ }
818
857
  const resolved = normalizeSelector(selector);
819
858
  if (resolved.startsWith("text/")) await clickQueryHandlerText(page, resolved, timeoutMs, sig);
820
859
  else await untilAborted(sig, () => page.locator(resolved).setTimeout(timeoutMs).click());
821
860
  }),
822
861
  type: (selector, text) =>
823
862
  op(`tab.type(${JSON.stringify(selector)})`, INF, async sig => {
824
- const handle = (await untilAborted(sig, () =>
825
- page.locator(normalizeSelector(selector)).setTimeout(timeoutMs).waitHandle(),
826
- )) as ElementHandle;
863
+ const handle = await this.#resolveActionHandle(selector, timeoutMs, sig);
827
864
  try {
828
865
  await untilAborted(sig, () => handle.type(text, { delay: 0 }));
829
866
  } finally {
830
- await handle.dispose();
867
+ await handle.dispose().catch(() => undefined);
831
868
  }
832
869
  }),
833
870
  fill: (selector, value) =>
834
- op(`tab.fill(${JSON.stringify(selector)})`, INF, sig =>
835
- untilAborted(sig, () => page.locator(normalizeSelector(selector)).setTimeout(timeoutMs).fill(value)),
836
- ),
871
+ op(`tab.fill(${JSON.stringify(selector)})`, INF, async sig => {
872
+ if (parseAriaRefSelector(selector) !== null) {
873
+ const handle = await this.#resolveAriaRef(selector);
874
+ try {
875
+ await untilAborted(sig, () =>
876
+ handle.evaluate(el => {
877
+ const node = el as unknown as { value?: string; focus?: () => void };
878
+ node.focus?.();
879
+ if ("value" in node) node.value = "";
880
+ }),
881
+ );
882
+ await untilAborted(sig, () => handle.type(value, { delay: 0 }));
883
+ } finally {
884
+ await handle.dispose().catch(() => undefined);
885
+ }
886
+ return;
887
+ }
888
+ await untilAborted(sig, () =>
889
+ page.locator(normalizeSelector(selector)).setTimeout(timeoutMs).fill(value),
890
+ );
891
+ }),
837
892
  press: (key, opts) =>
838
893
  op(`tab.press(${JSON.stringify(key)})`, INF, async sig => {
839
894
  const selector = opts?.selector;
@@ -844,13 +899,8 @@ export class WorkerCore {
844
899
  op("tab.scroll()", INF, sig => untilAborted(sig, () => page.mouse.wheel({ deltaX, deltaY }))),
845
900
  drag: (from, to) => op("tab.drag()", INF, sig => this.#drag(from, to, sig)),
846
901
  waitFor: selector =>
847
- op(
848
- `tab.waitFor(${JSON.stringify(selector)})`,
849
- INF,
850
- async sig =>
851
- (await untilAborted(sig, () =>
852
- page.locator(normalizeSelector(selector)).setTimeout(timeoutMs).waitHandle(),
853
- )) as ElementHandle,
902
+ op(`tab.waitFor(${JSON.stringify(selector)})`, INF, sig =>
903
+ this.#resolveActionHandle(selector, timeoutMs, sig),
854
904
  ),
855
905
  evaluate: (fn, ...args) =>
856
906
  op("tab.evaluate()", INF, sig =>
@@ -862,9 +912,7 @@ export class WorkerCore {
862
912
  ) as never,
863
913
  scrollIntoView: selector =>
864
914
  op(`tab.scrollIntoView(${JSON.stringify(selector)})`, INF, async sig => {
865
- const handle = (await untilAborted(sig, () =>
866
- page.locator(normalizeSelector(selector)).setTimeout(timeoutMs).waitHandle(),
867
- )) as ElementHandle;
915
+ const handle = await this.#resolveActionHandle(selector, timeoutMs, sig);
868
916
  try {
869
917
  await untilAborted(sig, () =>
870
918
  handle.evaluate(el => {
@@ -889,6 +937,7 @@ export class WorkerCore {
889
937
  waitForResponse: (pattern, opts) =>
890
938
  op("tab.waitForResponse()", INF, sig => this.#waitForResponse(pattern, opts?.timeout ?? timeoutMs, sig)),
891
939
  id: id => this.#resolveCachedHandle(id),
940
+ ref: id => this.#resolveAriaRef(id),
892
941
  };
893
942
  }
894
943
 
@@ -1188,6 +1237,29 @@ export class WorkerCore {
1188
1237
  }
1189
1238
  return handle;
1190
1239
  }
1240
+
1241
+ async #resolveAriaRef(id: string): Promise<ElementHandle> {
1242
+ const ref = parseAriaRefSelector(id) ?? id.trim();
1243
+ const handle = await resolveAriaRefHandle(this.#requirePage(), ref);
1244
+ if (!handle) {
1245
+ throw new ToolError(
1246
+ `Unknown ARIA ref ${JSON.stringify(ref)}. Run tab.ariaSnapshot() to refresh refs (they renumber each snapshot).`,
1247
+ );
1248
+ }
1249
+ return handle;
1250
+ }
1251
+
1252
+ /**
1253
+ * Resolve a selector to an ElementHandle for handle-based actions. An
1254
+ * `aria-ref=eN` selector resolves against the latest ariaSnapshot's refs
1255
+ * (main world); anything else goes through the normal locator wait.
1256
+ */
1257
+ async #resolveActionHandle(selector: string, timeoutMs: number, sig: AbortSignal): Promise<ElementHandle> {
1258
+ if (parseAriaRefSelector(selector) !== null) return this.#resolveAriaRef(selector);
1259
+ return (await untilAborted(sig, () =>
1260
+ this.#requirePage().locator(normalizeSelector(selector)).setTimeout(timeoutMs).waitHandle(),
1261
+ )) as ElementHandle;
1262
+ }
1191
1263
  #clearElementCache(): void {
1192
1264
  if (this.#elementCache.size === 0) {
1193
1265
  this.#elementCounter = 0;
@@ -16,6 +16,11 @@ import { ToolAbortError, ToolError, throwIfAborted } from "./tool-errors";
16
16
  import { toolResult } from "./tool-result";
17
17
  import { clampTimeout } from "./tool-timeouts";
18
18
 
19
+ export {
20
+ type AriaSnapshotOptions,
21
+ buildAriaSnapshotScript,
22
+ parseAriaRefSelector,
23
+ } from "./browser/aria/aria-snapshot";
19
24
  export { cmuxSnapshotToObservation, mapWaitUntil, resolveCmuxKind, serializeEval } from "./browser/cmux/rpc";
20
25
  export { CmuxSocketClient } from "./browser/cmux/socket-client";
21
26
  export { extractReadableFromHtml, type ReadableFormat, type ReadableResult } from "./browser/readable";
package/src/tools/find.ts CHANGED
@@ -101,8 +101,7 @@ interface FindTarget {
101
101
  export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
102
102
  readonly name = "find";
103
103
  readonly approval = "read" as const;
104
- readonly summary = "Find files and directories matching a glob pattern";
105
- readonly loadMode = "discoverable";
104
+ readonly loadMode = "essential";
106
105
  readonly label = "Find";
107
106
  readonly description: string;
108
107
  readonly parameters = findSchema;
@@ -375,7 +375,7 @@ export type ToolFactory = (session: ToolSession) => Tool | null | Promise<Tool |
375
375
  export type BuiltinToolLoadMode = "essential" | "discoverable";
376
376
 
377
377
  /** Default essential tool names when tools.essentialOverride is empty. */
378
- export const DEFAULT_ESSENTIAL_TOOL_NAMES: readonly string[] = ["read", "bash", "edit"] as const;
378
+ export const DEFAULT_ESSENTIAL_TOOL_NAMES: readonly string[] = ["read", "bash", "edit", "write", "find"] as const;
379
379
 
380
380
  /**
381
381
  * Resolve the active essential built-in tool names from settings.
@@ -1,8 +1,5 @@
1
- // Helper to generate native code string
2
- const makeNativeString = (name) =>
3
- "function " + (name || "") + "() { [native code] }";
4
-
5
- // Patch toString for common fingerprinted functions
1
+ // Register native-looking source for common fingerprinted functions without
2
+ // adding own toString properties.
6
3
  const patchedFns = [
7
4
  [window.alert, "alert"],
8
5
  [window.prompt, "prompt"],
@@ -20,44 +17,28 @@ const patchedFns = [
20
17
  ];
21
18
 
22
19
  for (const [fn, name] of patchedFns) {
23
- if (typeof fn === "function") {
24
- const nativeStr = makeNativeString(name);
25
- Object_defineProperty(fn, "toString", {
26
- value: function toString() { return nativeStr; },
27
- writable: false,
28
- configurable: true,
29
- enumerable: false,
30
- });
31
- }
20
+ patchToString(fn, name);
32
21
  }
33
22
 
34
- // Patch Object.getOwnPropertyDescriptor to return native-looking descriptors
35
- Object.getOwnPropertyDescriptor = function (obj, prop) {
36
- const descriptor = Object_getOwnPropertyDescriptor.call(this, obj, prop);
23
+ // Patch Object.getOwnPropertyDescriptor to return native-looking accessor
24
+ // source through the shared Function.prototype.toString registry.
25
+ const patchedGetOwnPropertyDescriptor = function getOwnPropertyDescriptor(obj, prop) {
26
+ const descriptor = Reflect_apply(Object_getOwnPropertyDescriptor, this, [obj, prop]);
37
27
  if (!descriptor) return descriptor;
38
28
 
39
- // Make patched descriptors look native
40
29
  if (descriptor.get && typeof descriptor.get === "function") {
41
- const getStr = makeNativeString("get " + String(prop));
42
- Object_defineProperty(descriptor.get, "toString", {
43
- value: function toString() { return getStr; },
44
- writable: false,
45
- configurable: true,
46
- enumerable: false,
47
- });
30
+ patchToString(descriptor.get, "get " + String(prop));
48
31
  }
49
32
  if (descriptor.set && typeof descriptor.set === "function") {
50
- const setStr = makeNativeString("set " + String(prop));
51
- Object_defineProperty(descriptor.set, "toString", {
52
- value: function toString() { return setStr; },
53
- writable: false,
54
- configurable: true,
55
- enumerable: false,
56
- });
33
+ patchToString(descriptor.set, "set " + String(prop));
57
34
  }
58
35
 
59
36
  return descriptor;
60
37
  };
61
-
62
- // Cleanup
63
- document.head.removeChild(iframe);
38
+ patchToString(patchedGetOwnPropertyDescriptor, "getOwnPropertyDescriptor");
39
+ Object_defineProperty(Object, "getOwnPropertyDescriptor", {
40
+ value: patchedGetOwnPropertyDescriptor,
41
+ writable: true,
42
+ configurable: true,
43
+ enumerable: false,
44
+ });
@@ -1,20 +1,80 @@
1
- const scheduleActivity = () => {
2
- const delay = 3000 + Math_random() * 4000;
3
- Window_setTimeout(() => {
4
- Object_defineProperty(document, "hidden", { get: () => false });
5
- Object_defineProperty(document, "visibilityState", {
6
- get: () => "visible",
1
+ {
2
+ const visibilityDescriptors = Object_getOwnPropertyDescriptors({
3
+ get hidden() {
4
+ return false;
5
+ },
6
+ get visibilityState() {
7
+ return "visible";
8
+ },
9
+ get webkitHidden() {
10
+ return false;
11
+ },
12
+ get webkitVisibilityState() {
13
+ return "visible";
14
+ },
15
+ });
16
+ for (const key of Object_keys(visibilityDescriptors)) {
17
+ const descriptor = visibilityDescriptors[key];
18
+ if (descriptor && typeof descriptor.get === "function") {
19
+ patchToString(descriptor.get, "get " + key);
20
+ }
21
+ }
22
+ const focusDescriptor = Object_getOwnPropertyDescriptor(
23
+ {
24
+ hasFocus() {
25
+ return document.visibilityState === "visible";
26
+ },
27
+ },
28
+ "hasFocus",
29
+ );
30
+ if (focusDescriptor && typeof focusDescriptor.value === "function") {
31
+ patchToString(focusDescriptor.value, "hasFocus");
32
+ }
33
+
34
+ const clearOwnSlot = (name) => {
35
+ const ownDescriptor = Object_getOwnPropertyDescriptor(document, name);
36
+ if (!ownDescriptor) return true;
37
+ if (ownDescriptor.configurable !== true) return false;
38
+ return Reflect_deleteProperty(document, name);
39
+ };
40
+
41
+ const inheritedSlot = (name, expectedKind) => {
42
+ let proto = Object_getPrototypeOf(document);
43
+ while (proto) {
44
+ const descriptor = Object_getOwnPropertyDescriptor(proto, name);
45
+ if (descriptor && typeof descriptor[expectedKind] === "function") {
46
+ return [proto, descriptor];
47
+ }
48
+ proto = Object_getPrototypeOf(proto);
49
+ }
50
+ };
51
+
52
+ const defineAccessor = (name) => {
53
+ if (!clearOwnSlot(name)) return;
54
+ const slot = inheritedSlot(name, "get");
55
+ if (!slot || slot[1].configurable !== true) return;
56
+
57
+ Object_defineProperty(slot[0], name, {
58
+ get: visibilityDescriptors[name].get,
59
+ enumerable: slot[1].enumerable,
60
+ configurable: true,
7
61
  });
8
- Object_defineProperty(document, "webkitVisibilityState", {
9
- get: () => "visible",
10
- });
11
- document.dispatchEvent(new Window_Event("visibilitychange"));
12
- if (Math_random() < 0.4) window.dispatchEvent(new Window_Event("focus"));
13
- document.hasFocus = () => true;
14
- if (Math_random() < 0.3) window.dispatchEvent(new Window_Event("scroll"));
15
- if (navigator.wakeLock)
16
- navigator.wakeLock.request("screen").catch(() => {});
17
- scheduleActivity();
18
- }, delay);
19
- };
20
- scheduleActivity();
62
+ };
63
+
64
+ defineAccessor("hidden");
65
+ defineAccessor("visibilityState");
66
+ defineAccessor("webkitHidden");
67
+ defineAccessor("webkitVisibilityState");
68
+
69
+ if (clearOwnSlot("hasFocus")) {
70
+ const slot = inheritedSlot("hasFocus", "value");
71
+ if (slot && slot[1].configurable === true) {
72
+ Object_defineProperty(slot[0], "hasFocus", {
73
+ value: focusDescriptor.value,
74
+ writable: slot[1].writable === true,
75
+ enumerable: slot[1].enumerable,
76
+ configurable: true,
77
+ });
78
+ }
79
+ }
80
+ }
@@ -1,11 +1,57 @@
1
- const elementDescriptor = Object_getOwnPropertyDescriptor(
2
- HTMLElement.prototype,
3
- "offsetHeight",
4
- );
5
- Object_defineProperty(HTMLDivElement.prototype, "offsetHeight", {
6
- ...elementDescriptor,
7
- get: function () {
8
- if (this.id === "modernizr") return 1;
9
- return Reflect_apply(elementDescriptor.get, this, []);
10
- },
11
- });
1
+ {
2
+ const htmlElementOffsetHeightDescriptor =
3
+ typeof HTMLElement === "undefined"
4
+ ? undefined
5
+ : Object_getOwnPropertyDescriptor(HTMLElement.prototype, "offsetHeight");
6
+ const htmlDivElementOffsetHeightDescriptor =
7
+ typeof HTMLDivElement === "undefined"
8
+ ? undefined
9
+ : Object_getOwnPropertyDescriptor(HTMLDivElement.prototype, "offsetHeight");
10
+ const offsetHeightDescriptor =
11
+ htmlDivElementOffsetHeightDescriptor || htmlElementOffsetHeightDescriptor;
12
+ const offsetHeightPrototype = htmlDivElementOffsetHeightDescriptor
13
+ ? HTMLDivElement.prototype
14
+ : htmlElementOffsetHeightDescriptor
15
+ ? HTMLElement.prototype
16
+ : undefined;
17
+ const elementIdDescriptor =
18
+ typeof Element === "undefined"
19
+ ? undefined
20
+ : Object_getOwnPropertyDescriptor(Element.prototype, "id");
21
+
22
+ if (
23
+ typeof HTMLDivElement !== "undefined" &&
24
+ offsetHeightPrototype &&
25
+ offsetHeightDescriptor &&
26
+ typeof offsetHeightDescriptor.get === "function" &&
27
+ offsetHeightDescriptor.configurable &&
28
+ elementIdDescriptor &&
29
+ typeof elementIdDescriptor.get === "function"
30
+ ) {
31
+ const offsetHeightGetter = new Window_Proxy(offsetHeightDescriptor.get, {
32
+ apply(target, thisArg, args) {
33
+ const height = Reflect_apply(target, thisArg, args);
34
+
35
+ if (
36
+ height === 0 &&
37
+ Object_getPrototypeOf(thisArg) === HTMLDivElement.prototype &&
38
+ Reflect_apply(elementIdDescriptor.get, thisArg, []) === "modernizr"
39
+ ) {
40
+ return 1;
41
+ }
42
+
43
+ return height;
44
+ },
45
+ });
46
+
47
+ patchToString(offsetHeightGetter, "get offsetHeight");
48
+
49
+ Object_defineProperty(
50
+ offsetHeightPrototype,
51
+ "offsetHeight",
52
+ Object_assign({}, offsetHeightDescriptor, {
53
+ get: offsetHeightGetter,
54
+ }),
55
+ );
56
+ }
57
+ }
@@ -1,15 +1,3 @@
1
- const makeNativeString = (name) => "function " + (name || "") + "() { [native code] }";
2
- const patchToString = (fn, name) => {
3
- if (typeof fn !== "function") return;
4
- Object_defineProperty(fn, "toString", {
5
- value: function toString() {
6
- return makeNativeString(name);
7
- },
8
- writable: false,
9
- configurable: true,
10
- enumerable: false,
11
- });
12
- };
13
1
 
14
2
  // Ensure navigator.webdriver behaves like real Chrome
15
3
  if (navigator.webdriver !== false && navigator.webdriver !== undefined) {
@@ -356,13 +344,19 @@ if (window.chrome && !("runtime" in window.chrome) && isSecureOrigin) {
356
344
  // Suppress Permission.query for automation-controlled
357
345
  if (navigator.permissions?.query) {
358
346
  if (isSecureOrigin && "Notification" in window) {
347
+ const notificationPermissionGetter = Object_getOwnPropertyDescriptor({
348
+ get permission() {
349
+ return "default";
350
+ },
351
+ }, "permission").get;
352
+ patchToString(notificationPermissionGetter, "get permission");
359
353
  Object_defineProperty(Notification, "permission", {
360
- get: () => "default",
354
+ get: notificationPermissionGetter,
361
355
  configurable: true,
362
356
  });
363
357
  } else if (!isSecureOrigin) {
364
358
  const originalQuery = navigator.permissions.query;
365
- navigator.permissions.query = function (parameters) {
359
+ const patchedPermissionsQuery = function query(parameters) {
366
360
  if (parameters?.name === "notifications") {
367
361
  const status = { state: "denied", onchange: null };
368
362
  if (typeof PermissionStatus !== "undefined") {
@@ -372,6 +366,8 @@ if (navigator.permissions?.query) {
372
366
  }
373
367
  return originalQuery.call(this, parameters);
374
368
  };
369
+ patchToString(patchedPermissionsQuery, "query");
370
+ navigator.permissions.query = patchedPermissionsQuery;
375
371
  }
376
372
  }
377
373