@wenathlan/extension 1.1.44 → 1.1.45

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.
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 3,
3
3
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnOEjO8Z0PDgQyfvawGcaO2j+o0GLCFTLNj7TkYC/Avo9l2NenMRq7gp90Nfd7E9MViv/OMcCKSYZ5unv12QPRtv31C+a5UQWDFAOP/cH5mwMd6hsayElrSoW8ta+FwFqmr9dIFkn7cQEU3YhZr4Gcbs+ycUHOxVgDA4NBKB0rQ6e9VW5LvTw0isRYUrqM+M72vKxHk9zUIYYn/LGPvottKBYi2GLr0PHSeC2UE+Shmq7vcFIXj6hDjvD4kLJ5sKoUllEcZ1TPuBcnHUQ9ndKA5iktXDQOIJCUJmi7a0YJ2PGg7fvpYfT9k0ai/qZ+pIoRfoOEwE01bPoDn7NjeYnNQIDAQAB",
4
4
  "name": "Devthink",
5
- "version": "1.1.44",
5
+ "version": "1.1.45",
6
6
  "description": "A consent-first bridge for reviewed browser-agent tasks.",
7
7
  "permissions": [
8
8
  "activeTab",
@@ -1,9 +1,96 @@
1
1
  "use strict";
2
2
  (() => {
3
+ // runtimeline.ts
4
+ var loglevels = ["error", "warn", "info", "log", "debug", "trace"];
5
+ function levelrank(level) {
6
+ return loglevels.indexOf(level);
7
+ }
8
+ function redactconsoletext(text, patterns) {
9
+ let redacted = text;
10
+ for (const pattern of patterns) {
11
+ if (!pattern) continue;
12
+ while (redacted.includes(pattern)) redacted = redacted.replace(pattern, "[redacted]");
13
+ }
14
+ return redacted;
15
+ }
16
+ function argkind(value) {
17
+ if (value === null) return "null";
18
+ if (Array.isArray(value)) return "array";
19
+ if (value instanceof Error) return "error";
20
+ switch (typeof value) {
21
+ case "string":
22
+ return "string";
23
+ case "number":
24
+ return "number";
25
+ case "boolean":
26
+ return "boolean";
27
+ case "bigint":
28
+ return "bigint";
29
+ case "symbol":
30
+ return "symbol";
31
+ case "function":
32
+ return "function";
33
+ case "undefined":
34
+ return "undefined";
35
+ default:
36
+ return "object";
37
+ }
38
+ }
39
+ function serializearg(value, depth) {
40
+ const render = (item, remaining) => {
41
+ if (item instanceof Error) return `${item.name}: ${item.message}`;
42
+ if (typeof item === "string") return item;
43
+ if (typeof item === "function") return `[function ${item.name || "anonymous"}]`;
44
+ if (typeof item === "bigint") return `${item}n`;
45
+ if (typeof item === "symbol") return item.toString();
46
+ if (item === null || item === void 0 || typeof item !== "object") return String(item);
47
+ if (remaining <= 0) {
48
+ const tag = Array.isArray(item) ? "Array" : item.constructor?.name ?? "Object";
49
+ return `[${tag}]`;
50
+ }
51
+ if (Array.isArray(item)) return `[${item.map((entry) => render(entry, remaining - 1)).join(", ")}]`;
52
+ const record = item;
53
+ return `{${Object.keys(record).map((key) => `${key}: ${render(record[key], remaining - 1)}`).join(", ")}}`;
54
+ };
55
+ return render(value, Math.max(0, depth));
56
+ }
57
+ function consolecapture(input) {
58
+ const parts = input.args.map((arg) => serializearg(arg, input.depth));
59
+ return { level: input.level, text: redactconsoletext(parts.join(" "), input.redact), argkinds: input.args.map((arg) => argkind(arg)), repeat: 1 };
60
+ }
61
+ function stackframes(stacktext) {
62
+ const frames = [];
63
+ for (const row of stacktext.split("\n")) {
64
+ const trimmed = row.trim();
65
+ if (!trimmed.startsWith("at ")) continue;
66
+ const body = trimmed.slice(3).trim();
67
+ const location2 = body.match(/\(([^()]*:\d+:\d+)\)$/) ?? body.match(/^(.*:\d+:\d+)$/);
68
+ const located = location2?.[1];
69
+ if (!located) continue;
70
+ const segments = located.split(":");
71
+ const column = Number.parseInt(segments.pop() ?? "", 10);
72
+ const lineno = Number.parseInt(segments.pop() ?? "", 10);
73
+ const url = segments.join(":");
74
+ if (!Number.isFinite(lineno) || lineno < 0) continue;
75
+ const name = body.endsWith(`(${located})`) ? body.slice(0, body.length - located.length - 2).trim() : "";
76
+ frames.push({ ...name ? { functionname: name } : {}, url, line: lineno, ...Number.isFinite(column) ? { column } : {} });
77
+ }
78
+ return frames;
79
+ }
80
+ function errorcapture(input) {
81
+ return { message: redactconsoletext(input.message, input.redact), frames: input.stacktext !== void 0 ? stackframes(input.stacktext) : [], sourceurl: input.sourceurl, line: input.line };
82
+ }
83
+ function rejectioncapture(input) {
84
+ return { reason: redactconsoletext(input.reason, input.redact), frames: input.stacktext !== void 0 ? stackframes(input.stacktext) : [] };
85
+ }
86
+ function longtaskcapture(input) {
87
+ return input.entries.filter((entry) => entry.duration >= input.threshold).map((entry) => ({ duration: Math.round(entry.duration), starttime: Math.round(entry.starttime), attributions: [...entry.attributions] }));
88
+ }
89
+
3
90
  // policy.ts
4
91
  var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts", "recordscreen", "captureaudio", "downloadimages", "callrest", "callgraphql", "sendmessage", "blockrequest", "mockresponse", "rewriteheaders", "setcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles"]);
5
92
  var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction", "capturebodies"]);
6
- var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml", "opensocket", "waitmessage", "watchrequests", "readheaders", "mapapi", "subscribesse", "longpoll", "extractapi", "readcookies"]);
93
+ var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml", "opensocket", "waitmessage", "watchrequests", "readheaders", "mapapi", "subscribesse", "longpoll", "extractapi", "readcookies", "watchconsole", "watcherrors", "watchtasks"]);
7
94
  var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
8
95
  function parseoptions(step) {
9
96
  if (step.options === void 0) return {};
@@ -2358,6 +2445,117 @@
2358
2445
  }
2359
2446
  }
2360
2447
 
2448
+ // extension/pagedebug.ts
2449
+ function debugwatchoptions(step) {
2450
+ let options = {};
2451
+ try {
2452
+ options = parseoptions(step);
2453
+ } catch {
2454
+ options = {};
2455
+ }
2456
+ const watch = options.watch && typeof options.watch === "object" && !Array.isArray(options.watch) ? options.watch : {};
2457
+ const spam = options.spam && typeof options.spam === "object" && !Array.isArray(options.spam) ? options.spam : void 0;
2458
+ const rotation = options.rotation && typeof options.rotation === "object" && !Array.isArray(options.rotation) ? options.rotation : void 0;
2459
+ return {
2460
+ window: typeof watch.window === "number" && Number.isFinite(watch.window) && watch.window >= 0 ? watch.window : 0,
2461
+ ...typeof options.level === "string" && loglevels.includes(options.level) ? { level: options.level } : {},
2462
+ depth: typeof options.depth === "number" && Number.isInteger(options.depth) && options.depth >= 1 ? options.depth : 2,
2463
+ redact: Array.isArray(options.redact) ? options.redact.filter((pattern) => typeof pattern === "string" && pattern.length > 0) : [],
2464
+ ...spam && typeof spam.pattern === "string" && typeof spam.windowsize === "number" && typeof spam.collapse === "number" ? { spam: { pattern: spam.pattern, windowsize: spam.windowsize, collapse: spam.collapse } } : {},
2465
+ ...rotation && typeof rotation.maxentries === "number" && typeof rotation.overflowtarget === "string" ? { rotation: { maxentries: rotation.maxentries, overflowtarget: rotation.overflowtarget } } : {},
2466
+ threshold: typeof options.threshold === "number" && Number.isFinite(options.threshold) && options.threshold >= 0 ? options.threshold : 0
2467
+ };
2468
+ }
2469
+ function wait4(milliseconds) {
2470
+ return new Promise((resolve) => window.setTimeout(resolve, Math.max(0, milliseconds)));
2471
+ }
2472
+ async function rundebugwatch(step) {
2473
+ const options = debugwatchoptions(step);
2474
+ if (options.window <= 0) return { ok: false, summary: "The reviewed debug watch window is absent." };
2475
+ const started = Date.now();
2476
+ const entries = [];
2477
+ const consoleentries = [];
2478
+ const errors = [];
2479
+ const rejections = [];
2480
+ const resources = [];
2481
+ const longtasks = [];
2482
+ const floor = options.level !== void 0 ? levelrank(options.level) : void 0;
2483
+ const capture = (level, source, message, at) => {
2484
+ if (floor !== void 0 && levelrank(level) > floor) return;
2485
+ entries.push({ stepid: step.id, time: at, level, source, message });
2486
+ };
2487
+ const hooks = [];
2488
+ if (step.kind === "watchconsole") {
2489
+ for (const level of loglevels) {
2490
+ const original = console[level];
2491
+ const hooked = (...args) => {
2492
+ try {
2493
+ original.apply(console, args);
2494
+ } catch {
2495
+ }
2496
+ const entry = consolecapture({ level, args, depth: options.depth, redact: options.redact });
2497
+ if (floor === void 0 || levelrank(level) <= floor) consoleentries.push(entry);
2498
+ capture(level, "console", entry.text, Date.now());
2499
+ };
2500
+ console[level] = hooked;
2501
+ hooks.push(() => {
2502
+ console[level] = original;
2503
+ });
2504
+ }
2505
+ }
2506
+ if (step.kind === "watcherrors") {
2507
+ const onerror = (event) => {
2508
+ const record = errorcapture({ message: event.message, sourceurl: event.filename, line: event.lineno, ...event.error instanceof Error ? { stacktext: event.error.stack } : {}, redact: options.redact });
2509
+ errors.push({ ...record, stepid: step.id, at: Date.now() });
2510
+ capture("error", "error", record.message, Date.now());
2511
+ };
2512
+ const onrejection = (event) => {
2513
+ const reason = event.reason instanceof Error ? `${event.reason.name}: ${event.reason.message}` : String(event.reason);
2514
+ const record = rejectioncapture({ reason, ...event.reason instanceof Error ? { stacktext: event.reason.stack } : {}, redact: options.redact });
2515
+ rejections.push({ ...record, stepid: step.id, at: Date.now() });
2516
+ capture("error", "rejection", record.reason, Date.now());
2517
+ };
2518
+ const onresource = (event) => {
2519
+ const target = event.target;
2520
+ if (!(target instanceof Element)) return;
2521
+ const element = target.tagName.toLowerCase() + (target.id ? `#${target.id}` : "");
2522
+ const sourceurl = target instanceof HTMLImageElement || target instanceof HTMLScriptElement ? target.src ?? "" : target instanceof HTMLLinkElement ? target.href ?? "" : "";
2523
+ const message = `Failed to load ${element}${sourceurl ? ` from ${sourceurl}` : ""}.`;
2524
+ resources.push({ message, element, sourceurl });
2525
+ capture("error", "resource", message, Date.now());
2526
+ };
2527
+ window.addEventListener("error", onerror, true);
2528
+ window.addEventListener("unhandledrejection", onrejection, true);
2529
+ window.addEventListener("error", onresource, true);
2530
+ hooks.push(() => {
2531
+ window.removeEventListener("error", onerror, true);
2532
+ window.removeEventListener("unhandledrejection", onrejection, true);
2533
+ window.removeEventListener("error", onresource, true);
2534
+ });
2535
+ }
2536
+ if (step.kind === "watchtasks") {
2537
+ const observer = new PerformanceObserver((list) => {
2538
+ for (const entry of list.getEntries()) {
2539
+ const detail = entry;
2540
+ const attributions = (detail.attribution ?? []).map((container) => String(container.name ?? "")).filter((name) => name.length > 0);
2541
+ longtasks.push({ stepid: step.id, duration: Math.round(detail.duration), starttime: Math.round(detail.startTime), attributions, at: Date.now() });
2542
+ }
2543
+ });
2544
+ observer.observe({ entryTypes: ["longtask"] });
2545
+ hooks.push(() => observer.disconnect());
2546
+ }
2547
+ await wait4(options.window);
2548
+ for (const detach of hooks) detach();
2549
+ if (step.kind === "watchtasks") {
2550
+ const filtered = longtaskcapture({ entries: longtasks, threshold: options.threshold });
2551
+ longtasks.length = 0;
2552
+ longtasks.push(...filtered.map((task) => ({ ...task, stepid: step.id, at: started })));
2553
+ for (const task of longtasks) capture("info", "longtask", `Long task of ${task.duration} milliseconds blocked the main thread${task.attributions.length > 0 ? ` (${task.attributions.join(", ")})` : ""}.`, task.at);
2554
+ }
2555
+ const summary = step.kind === "watchconsole" ? `Captured ${consoleentries.length} console call${consoleentries.length === 1 ? "" : "s"} at every level for the reviewed window of ${options.window} milliseconds.` : step.kind === "watcherrors" ? `Captured ${errors.length} error${errors.length === 1 ? "" : "s"}, ${rejections.length} rejection${rejections.length === 1 ? "" : "s"} and ${resources.length} resource failure${resources.length === 1 ? "" : "s"} for the reviewed window of ${options.window} milliseconds.` : `Captured ${longtasks.length} long task${longtasks.length === 1 ? "" : "s"} for the reviewed window of ${options.window} milliseconds.`;
2556
+ return { ok: true, summary, details: { entries, console: consoleentries, errors, rejections, resources, longtasks, watchwindow: options.window, depth: options.depth, derivation: "Console, error and task watching derives from page-injected listeners and the performance buffers through the scripting api; no debugger permission exists in the manifest." } };
2557
+ }
2558
+
2361
2559
  // extension/pageforms.ts
2362
2560
  function matchfield(fields, match) {
2363
2561
  const key = match.mode === "label" ? "label" : match.mode === "placeholder" ? "placeholder" : match.mode === "arialabel" ? "arialabel" : "name";
@@ -2786,7 +2984,7 @@
2786
2984
  if (source === "reviewed") return typeof value === "string" && value.trim().length > 0;
2787
2985
  return false;
2788
2986
  }
2789
- function wait4(delay) {
2987
+ function wait5(delay) {
2790
2988
  return new Promise((resolve) => window.setTimeout(resolve, delay));
2791
2989
  }
2792
2990
  function pollfor2(predicate, description, timeout) {
@@ -2900,7 +3098,7 @@
2900
3098
  for (const group of cardgroups(value)) {
2901
3099
  element.value = group;
2902
3100
  element.dispatchEvent(new Event("input", { bubbles: true }));
2903
- if (pause > 0) await wait4(pause);
3101
+ if (pause > 0) await wait5(pause);
2904
3102
  }
2905
3103
  element.dispatchEvent(new Event("change", { bubbles: true }));
2906
3104
  filled.push({ label: String(match[key] ?? ""), masked: cardmask(value) });
@@ -3077,7 +3275,7 @@
3077
3275
  if (step.kind === "paginateextract") {
3078
3276
  const nextselector = typeof options.next === "string" ? options.next : "";
3079
3277
  const pages = typeof options.pages === "number" && Number.isInteger(options.pages) && options.pages > 0 ? options.pages : 1;
3080
- const wait5 = typeof options.wait === "number" && Number.isFinite(options.wait) && options.wait > 0 ? options.wait : 0;
3278
+ const wait6 = typeof options.wait === "number" && Number.isFinite(options.wait) && options.wait > 0 ? options.wait : 0;
3081
3279
  const cursor = typeof options.cursor === "number" && Number.isInteger(options.cursor) && options.cursor > 0 ? options.cursor : 0;
3082
3280
  const grids = [];
3083
3281
  let previous = [];
@@ -3093,7 +3291,7 @@
3093
3291
  const control = root.querySelector(nextselector);
3094
3292
  if (!control) break;
3095
3293
  control.click();
3096
- const deadline = Date.now() + wait5;
3294
+ const deadline = Date.now() + wait6;
3097
3295
  let fresh = false;
3098
3296
  while (!fresh && Date.now() < deadline) {
3099
3297
  await new Promise((resolve) => window.setTimeout(resolve, Math.min(100, Math.max(16, deadline - Date.now()))));
@@ -3230,6 +3428,7 @@
3230
3428
  var observationkinds = /* @__PURE__ */ new Set(["a11ytree", "readvisible", "readertree", "readoutline", "readselection", "readopengraph", "readlang", "detectlanguage", "listshadow", "listframes"]);
3231
3429
  var detectionkinds = /* @__PURE__ */ new Set(["detectlists", "detecttables", "detectinfinitescroll", "detectvirtual", "detectlazy", "detectsticky", "detectscrolllock", "countpages", "classifypage", "fingerprintsection", "readscrollpos"]);
3232
3430
  var watchstepkinds = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "waitquiet", "readjson", "diffsnapshots", "deriveselector"]);
3431
+ var debugstepkinds = /* @__PURE__ */ new Set(["watchconsole", "watcherrors", "watchtasks"]);
3233
3432
  var navstepkinds = /* @__PURE__ */ new Set(["waitload", "waiturl", "followlink", "spanav", "spawait", "rewritequery", "setfragment", "stopnav", "prefetch", "preconnect", "printpdf"]);
3234
3433
  var formkinds = /* @__PURE__ */ new Set(["fillform", "filllabel", "fillplaceholder", "detectfields", "generatevalues", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "asksubmit", "submitform", "consentpassword", "attachfile"]);
3235
3434
  var wizardkinds = /* @__PURE__ */ new Set(["runwizard", "selectchain", "picktypeahead", "pickdate", "fillcard", "fillcode"]);
@@ -3295,6 +3494,7 @@
3295
3494
  else if (observationkinds.has(step.kind)) result = runpageobservation(step, element, rootdocument);
3296
3495
  else if (detectionkinds.has(step.kind)) result = runpagedetection(step, element, rootdocument);
3297
3496
  else if (watchstepkinds.has(step.kind)) result = runpagewatch(step, element, rootdocument);
3497
+ else if (debugstepkinds.has(step.kind)) return await rundebugwatch(step);
3298
3498
  else if (mutatingkinds.has(step.kind)) result = runpageaction(step, element);
3299
3499
  else {
3300
3500
  if (!element) return { ok: false, summary: "Action target is no longer available." };