@wenathlan/extension 1.1.45 → 1.1.46

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.
@@ -1126,6 +1126,108 @@ var sessionmemory = class {
1126
1126
  async getconsoleconsents() {
1127
1127
  return await this.adapter.get("consoleconsents") ?? [];
1128
1128
  }
1129
+ /** Stores one devtools session record with its enabled domains and detach state, replacing the previous record of its id. */
1130
+ async setcdpsession(session) {
1131
+ const records = (await this.adapter.get("cdpsessions") ?? []).filter((item) => item.id !== session.id);
1132
+ await this.adapter.set("cdpsessions", [session, ...records]);
1133
+ }
1134
+ /** Returns every stored devtools session record, newest first. */
1135
+ async getcdpsessions() {
1136
+ return await this.adapter.get("cdpsessions") ?? [];
1137
+ }
1138
+ /** Stores one raw command outcome with its duration and error class. */
1139
+ async addcdpcommand(command) {
1140
+ const records = await this.adapter.get("cdpcommands") ?? [];
1141
+ await this.adapter.set("cdpcommands", [command, ...records]);
1142
+ }
1143
+ /** Returns every stored raw command outcome, newest first. */
1144
+ async getcdpcommands() {
1145
+ return await this.adapter.get("cdpcommands") ?? [];
1146
+ }
1147
+ /** Stores one domain event rule with its match filter, replacing the previous rule of its id. */
1148
+ async setcdpeventrule(rule) {
1149
+ const records = (await this.adapter.get("cdpeventrules") ?? []).filter((item) => item.id !== rule.id);
1150
+ await this.adapter.set("cdpeventrules", [rule, ...records]);
1151
+ }
1152
+ /** Returns every stored domain event rule, newest first. */
1153
+ async getcdpeventrules() {
1154
+ return await this.adapter.get("cdpeventrules") ?? [];
1155
+ }
1156
+ /** Stores one breakpoint record with its condition and hit counter, replacing the previous record of its id. */
1157
+ async addbreakpoint(spec) {
1158
+ const records = (await this.adapter.get("breakpoints") ?? []).filter((item) => item.id !== spec.id);
1159
+ await this.adapter.set("breakpoints", [spec, ...records]);
1160
+ }
1161
+ /** Returns every stored breakpoint record, newest first. */
1162
+ async getbreakpoints() {
1163
+ return await this.adapter.get("breakpoints") ?? [];
1164
+ }
1165
+ /** Stores one pause state capture; the user configured pause retention window expires the call frames and the dom snapshot reference of the oldest captures while the pause reason and hit breakpoint survive. */
1166
+ async addpause(pause) {
1167
+ const records = await this.getpauses();
1168
+ const combined = [pause, ...records.filter((item) => item.id !== pause.id)];
1169
+ const retention = (await this.getsettings())?.pauseretention;
1170
+ if (retention === void 0) {
1171
+ await this.adapter.set("pauses", combined);
1172
+ return;
1173
+ }
1174
+ const kept = combined.slice(0, retention);
1175
+ const expired = combined.slice(retention).map((item) => {
1176
+ if (item.framesexpired === true) return item;
1177
+ const faded = { ...item, callframes: [], framesexpired: true };
1178
+ delete faded.domsnapshotid;
1179
+ return faded;
1180
+ });
1181
+ await this.adapter.set("pauses", [...kept, ...expired]);
1182
+ }
1183
+ /** Returns every stored pause state capture, newest first. */
1184
+ async getpauses() {
1185
+ return await this.adapter.get("pauses") ?? [];
1186
+ }
1187
+ /** Returns the pause state captures of one run, newest first. */
1188
+ async listpauses(runid) {
1189
+ const records = await this.getpauses();
1190
+ return records.filter((item) => item.runid === runid);
1191
+ }
1192
+ /** Stores one watch expression with its per pause values, replacing the previous expression of its id. */
1193
+ async setwatchexpression(expression) {
1194
+ const records = (await this.adapter.get("watchexpressions") ?? []).filter((item) => item.id !== expression.id);
1195
+ await this.adapter.set("watchexpressions", [expression, ...records]);
1196
+ }
1197
+ /** Returns every stored watch expression, newest first. */
1198
+ async getwatchexpressions() {
1199
+ return await this.adapter.get("watchexpressions") ?? [];
1200
+ }
1201
+ /** Stores one script override with its review provenance, replacing the previous override of its id. */
1202
+ async addscriptoverride(spec) {
1203
+ const records = (await this.adapter.get("scriptoverrides") ?? []).filter((item) => item.id !== spec.id);
1204
+ await this.adapter.set("scriptoverrides", [spec, ...records]);
1205
+ }
1206
+ /** Returns every stored script override, newest first. */
1207
+ async getscriptoverrides() {
1208
+ return await this.adapter.get("scriptoverrides") ?? [];
1209
+ }
1210
+ /** Stores one debugger consent decision per origin with the consented domain list, replacing the previous decision of its id. */
1211
+ async setdebuggergrant(grant) {
1212
+ const records = (await this.adapter.get("debuggergrants") ?? []).filter((item) => item.id !== grant.id);
1213
+ await this.adapter.set("debuggergrants", [grant, ...records]);
1214
+ }
1215
+ /** Returns every debugger consent decision, newest first. */
1216
+ async getdebuggergrants() {
1217
+ return await this.adapter.get("debuggergrants") ?? [];
1218
+ }
1219
+ /** Revokes every approved debugger consent of one origin: the revoke time stamps the records so the next attach needs a new reviewed prompt. */
1220
+ async revokedebuggergrants(origin, at) {
1221
+ const records = await this.getdebuggergrants();
1222
+ let revoked = 0;
1223
+ const updated = records.map((grant) => {
1224
+ if (grant.origin !== origin || grant.revokedat !== void 0) return grant;
1225
+ revoked += 1;
1226
+ return { ...grant, revokedat: at };
1227
+ });
1228
+ await this.adapter.set("debuggergrants", updated);
1229
+ return revoked;
1230
+ }
1129
1231
  /** Merges expired entry counts into the per run level count summary that survives the retention window. */
1130
1232
  async mergelevelsummary(runid, counts, now) {
1131
1233
  const records = await this.getlevelsummaries();
@@ -2005,6 +2107,126 @@ function ratelimitwait(state, now) {
2005
2107
  return Math.max(0, state.resetat - now);
2006
2108
  }
2007
2109
 
2110
+ // cdpbus.ts
2111
+ var cdpkinds = ["attachcdp", "detachcdp", "cdpcmd", "watchcdp", "setbreakpoint", "stepcode", "watchexpr", "overridescript"];
2112
+ var cdpdomains = ["Runtime", "Log", "Debugger", "DOM", "Network", "Page"];
2113
+ function methoddomain(method) {
2114
+ const match = /^([A-Z][A-Za-z]*)\.([a-zA-Z][A-Za-z0-9]*)$/.exec(method.trim());
2115
+ return match?.[1];
2116
+ }
2117
+ function cdpallowlistof(value) {
2118
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2119
+ const entry = value;
2120
+ const domains = Array.isArray(entry.domains) ? entry.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
2121
+ if (domains.length === 0) return void 0;
2122
+ if (entry.methods === void 0) return { domains };
2123
+ const methods = Array.isArray(entry.methods) ? entry.methods.filter((method) => typeof method === "string" && methoddomain(method) !== void 0 && domains.includes(methoddomain(method))) : [];
2124
+ if (methods.length === 0) return void 0;
2125
+ return { domains, methods };
2126
+ }
2127
+ function allowlistcovers(allowlist, method) {
2128
+ const domain = methoddomain(method);
2129
+ if (domain === void 0) return false;
2130
+ if (!allowlist.domains.includes(domain)) return false;
2131
+ if (allowlist.methods !== void 0 && !allowlist.methods.includes(method)) return false;
2132
+ return true;
2133
+ }
2134
+ function attachcdpsession(input) {
2135
+ return { id: input.id, runid: input.runid, stepid: input.stepid, tabid: input.tabid, origin: input.origin, attachedat: input.now, domains: [...new Set(input.domains)], debuggerversion: input.debuggerversion };
2136
+ }
2137
+ function detachcdpsession(session, at, userdetached = false) {
2138
+ return { ...session, detachedat: at, ...userdetached ? { userdetached: true } : {} };
2139
+ }
2140
+ function sendcdpcommand(input) {
2141
+ const domain = methoddomain(input.method);
2142
+ if (domain === void 0) return { ...input, method: input.method.trim(), domain: "", duration: input.duration, ...input.errorclass !== void 0 ? { errorclass: input.errorclass } : { errorclass: "malformedmethod" }, at: input.at };
2143
+ return { ...input, method: input.method.trim(), domain, duration: input.duration, ...input.errorclass !== void 0 ? { errorclass: input.errorclass } : {}, at: input.at };
2144
+ }
2145
+ function serializecdpcommand(queue, command) {
2146
+ return [...queue, command];
2147
+ }
2148
+ function cdpeventruleof(value) {
2149
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2150
+ const entry = value;
2151
+ const domain = typeof entry.domain === "string" && cdpdomains.includes(entry.domain) ? entry.domain : void 0;
2152
+ const event = typeof entry.event === "string" && entry.event.trim() ? entry.event.trim() : void 0;
2153
+ if (domain === void 0 || event === void 0) return void 0;
2154
+ const match = typeof entry.match === "string" && entry.match.trim() ? entry.match.trim() : void 0;
2155
+ return { domain, event, ...match !== void 0 ? { match } : {} };
2156
+ }
2157
+ function watchcdpevents(rules, events) {
2158
+ const matched = [];
2159
+ const counts = {};
2160
+ for (const domain of cdpdomains) counts[domain] = 0;
2161
+ for (const event of events) {
2162
+ for (const rule of rules) {
2163
+ if (rule.domain !== event.domain || rule.event !== event.event) continue;
2164
+ if (rule.match !== void 0 && !(event.payload ?? "").includes(rule.match)) continue;
2165
+ matched.push({ ruleid: rule.id, domain: event.domain, event: event.event, ...event.payload !== void 0 ? { payload: event.payload } : {} });
2166
+ counts[event.domain] = (counts[event.domain] ?? 0) + 1;
2167
+ }
2168
+ }
2169
+ return { matched, counts };
2170
+ }
2171
+ function breakpointinputof(value) {
2172
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2173
+ const entry = value;
2174
+ const url = typeof entry.url === "string" && entry.url.trim() ? entry.url.trim() : void 0;
2175
+ const line = typeof entry.line === "number" && Number.isInteger(entry.line) && entry.line >= 0 ? entry.line : void 0;
2176
+ if (url === void 0 || line === void 0) return void 0;
2177
+ const column = typeof entry.column === "number" && Number.isInteger(entry.column) && entry.column >= 0 ? entry.column : void 0;
2178
+ const condition = typeof entry.condition === "string" && entry.condition.trim() ? entry.condition.trim() : void 0;
2179
+ return { url, line, ...column !== void 0 ? { column } : {}, ...condition !== void 0 ? { condition } : {} };
2180
+ }
2181
+ function capturepause(input) {
2182
+ return { id: input.id, runid: input.runid, stepid: input.stepid, reason: input.reason, callframes: [...input.callframes], ...input.hitbreakpoint !== void 0 ? { hitbreakpoint: input.hitbreakpoint } : {}, ...input.domsnapshotid !== void 0 ? { domsnapshotid: input.domsnapshotid } : {}, at: input.at };
2183
+ }
2184
+ function stepmodeof(value) {
2185
+ const modes = ["stepover", "stepinto", "stepout", "resume"];
2186
+ return typeof value === "string" && modes.includes(value) ? value : void 0;
2187
+ }
2188
+ function watchexpressionof(value) {
2189
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2190
+ const entry = value;
2191
+ const expression = typeof entry.expression === "string" && entry.expression.trim() ? entry.expression.trim() : void 0;
2192
+ if (expression === void 0) return void 0;
2193
+ const scope = typeof entry.scope === "string" && entry.scope.trim() ? entry.scope.trim() : "topframe";
2194
+ return { expression, scope };
2195
+ }
2196
+ function recordwatchvalue(expression, pauseid, value, at) {
2197
+ return { ...expression, values: [...expression.values, { pauseid, value, at }] };
2198
+ }
2199
+ function overrideinputof(value) {
2200
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2201
+ const entry = value;
2202
+ const urlpattern = typeof entry.urlpattern === "string" && entry.urlpattern.trim() ? entry.urlpattern.trim() : void 0;
2203
+ const source = typeof entry.source === "string" ? entry.source : void 0;
2204
+ if (urlpattern === void 0 || source === void 0 || source.trim().length === 0) return void 0;
2205
+ return { urlpattern, source };
2206
+ }
2207
+ function teardownplanof(value) {
2208
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2209
+ const entry = value;
2210
+ const revertsteps = Array.isArray(entry.revertsteps) ? entry.revertsteps.filter((step) => typeof step === "string" && step.trim().length > 0) : [];
2211
+ const policy = entry.resumepolicy;
2212
+ if (revertsteps.length === 0) return void 0;
2213
+ if (policy !== void 0 && policy !== "resume" && policy !== "pause" && policy !== "ask") return void 0;
2214
+ return { revertsteps, resumepolicy: policy ?? "ask" };
2215
+ }
2216
+ function teardowncdpsession(input) {
2217
+ const revertedbreakpoints = input.breakpoints.filter((spec) => spec.revertedat === void 0).map((spec) => spec.id);
2218
+ const revertedoverrides = input.overrides.filter((spec) => spec.revertedat === void 0).map((spec) => spec.id);
2219
+ const resumepolicy = input.userdetached ? "pause" : input.plan?.resumepolicy ?? "ask";
2220
+ return {
2221
+ session: detachcdpsession(input.session, input.at, input.userdetached),
2222
+ revertedbreakpoints,
2223
+ revertedoverrides,
2224
+ resumepolicy,
2225
+ paused: input.userdetached || resumepolicy === "pause",
2226
+ keepsalive: input.userdetached
2227
+ };
2228
+ }
2229
+
2008
2230
  // netauth.ts
2009
2231
  function oauthflowof(value) {
2010
2232
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
@@ -2143,7 +2365,7 @@ ${file.content}\r
2143
2365
  // runtimeline.ts
2144
2366
  var timelinekinds = ["watchconsole", "watcherrors", "watchtasks"];
2145
2367
  var loglevels = ["error", "warn", "info", "log", "debug", "trace"];
2146
- var timelinesources = ["console", "error", "rejection", "resource", "longtask", "network"];
2368
+ var timelinesources = ["console", "error", "rejection", "resource", "longtask", "network", "cdp"];
2147
2369
  function attachtimeline(input) {
2148
2370
  return { runid: input.runid, origin: input.origin, stepids: [...input.stepids], attachedat: input.now, entries: [] };
2149
2371
  }
@@ -2238,9 +2460,9 @@ function consolediff(input) {
2238
2460
  }
2239
2461
 
2240
2462
  // policy.ts
2241
- 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"]);
2242
- 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"]);
2243
- 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"]);
2463
+ 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", "attachcdp", "detachcdp", "cdpcmd", "overridescript"]);
2464
+ 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", "setbreakpoint", "stepcode", "watchexpr"]);
2465
+ 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", "watchcdp"]);
2244
2466
  var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
2245
2467
  var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
2246
2468
  var targetactions = /* @__PURE__ */ new Set(["inspect", "focus", "click", "type", "scroll", "select", "hover", "clickdeep", "rightclick", "doubleclick", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "highlight", "setattribute", "removeattribute", "waitfor", "shiftclick", "typetime", "appendtext", "setvalue", "typeedit", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "expanddetails", "verifyvisible", "verifyenabled", "pierceshadow", "deriveselector", "fingerprintsection", "submitform", "retryform", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcode", "consentpassword", "scrapetable", "paginateextract", "shotelement", "captureframe", "shotcanvas"]);
@@ -2257,6 +2479,7 @@ var socketactions = /* @__PURE__ */ new Set(["opensocket", "sendmessage", "waitm
2257
2479
  var netwatchactions = /* @__PURE__ */ new Set(["watchrequests", "readheaders", "capturebodies", "mapapi", "extractapi"]);
2258
2480
  var controlactions = /* @__PURE__ */ new Set(["blockrequest", "mockresponse", "rewriteheaders", "setcookies", "readcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles"]);
2259
2481
  var debugactions = /* @__PURE__ */ new Set(["watchconsole", "watcherrors", "watchtasks"]);
2482
+ var cdpactions = /* @__PURE__ */ new Set(["attachcdp", "detachcdp", "cdpcmd", "watchcdp", "setbreakpoint", "stepcode", "watchexpr", "overridescript"]);
2260
2483
  var credentialheaders = /* @__PURE__ */ new Set(["authorization", "proxy-authorization", "cookie", "cookie2", "set-cookie", "api-key", "x-api-key", "x-auth-token", "x-session-token", "proxy-authorization"]);
2261
2484
  var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
2262
2485
  var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
@@ -2275,6 +2498,9 @@ function hostpattern(origin) {
2275
2498
  function isdebugkind(kind) {
2276
2499
  return debugactions.has(kind);
2277
2500
  }
2501
+ function iscdpkind(kind) {
2502
+ return cdpactions.has(kind);
2503
+ }
2278
2504
  function actionrisk(kind) {
2279
2505
  if (!allowedactions.has(kind)) throw new Error("Unsupported browser action.");
2280
2506
  if (sensitiveactions.has(kind)) return "sensitive";
@@ -3575,6 +3801,127 @@ function rotationruleof(value) {
3575
3801
  if (maxentries === void 0 || overflowtarget === void 0) return void 0;
3576
3802
  return { maxentries, overflowtarget };
3577
3803
  }
3804
+ function debuggate(session, tabid2, origin, now) {
3805
+ if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the devtools protocol step." };
3806
+ if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot run a devtools protocol step." };
3807
+ if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot run a devtools protocol step." };
3808
+ if (session.tabid !== tabid2) return { allowed: false, reason: `The devtools protocol step needs the run tab ${session.tabid} and refuses tab ${tabid2}.` };
3809
+ if (!origingranted(session, origin)) return { allowed: false, reason: `The devtools protocol step on ${origin} needs the session origin grants first.` };
3810
+ return { allowed: true };
3811
+ }
3812
+ function debuggerconsentcovers(origin, domains, grants) {
3813
+ const needed = [...new Set(domains)];
3814
+ const covering = grants.find((grant) => grant.origin === origin && grant.approved === true && grant.revokedat === void 0 && needed.every((domain) => grant.domains.includes(domain)));
3815
+ if (covering) return { allowed: true };
3816
+ if (grants.some((grant) => grant.origin === origin && grant.revokedat !== void 0)) return { allowed: false, reason: `The debugger consent on ${origin} was revoked; approve a new prompt before the devtools protocol runs again.` };
3817
+ return { allowed: false, reason: `The devtools protocol on ${origin} needs the reviewed debugger consent for ${needed.join(", ")} first; approve the prompt with the domain allowlist shown in the review panel.` };
3818
+ }
3819
+ function validatebreakpointcondition(condition) {
3820
+ const expression = condition.trim();
3821
+ if (expression.length === 0) return { allowed: false, reason: "The breakpoint condition must not be empty." };
3822
+ if (/(?<![=!<>])=(?!=)/.test(expression)) return { allowed: false, reason: "Breakpoint conditions refuse assignment because the reviewed grammar is comparison only." };
3823
+ if (/[A-Za-z_$][\w$]*\s*\(/.test(expression)) return { allowed: false, reason: "Breakpoint conditions refuse calls because the reviewed grammar is comparison only." };
3824
+ const literal = /^(?:-?\d+(?:\.\d+)?|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|true|false|null)$/;
3825
+ const tokens = expression.match(/(?:[A-Za-z_$][\w$]*|-?\d+(?:\.\d+)?|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|===|!==|==|!=|>=|<=|&&|\|\||[!.<>()+\-*\/%])/g);
3826
+ if (tokens === null || tokens.join("") !== expression.replace(/\s+/g, "")) return { allowed: false, reason: "The breakpoint condition must use the reviewed expression grammar of member chains, literals, comparisons, logic operators, negation and parentheses." };
3827
+ const identifierlike = /^(?:true|false|null)$/;
3828
+ for (const token of tokens) {
3829
+ if (literal.test(token) || identifierlike.test(token)) continue;
3830
+ if (["===", "!==", "==", "!=", ">=", "<=", "&&", "||", "!", ".", "(", ")", "<", ">", "+", "-", "*", "/", "%"].includes(token)) continue;
3831
+ if (/^[A-Za-z_$][\w$]*$/.test(token)) continue;
3832
+ return { allowed: false, reason: `The token ${token} of the breakpoint condition stays outside the reviewed expression grammar.` };
3833
+ }
3834
+ return { allowed: true };
3835
+ }
3836
+ function breakpointbudgetallowed(active, ceiling) {
3837
+ if (ceiling === void 0) return { allowed: true };
3838
+ if (typeof ceiling !== "number" || !Number.isInteger(ceiling) || ceiling < 0) return { allowed: false, reason: "The reviewed breakpoint ceiling must be zero or a positive integer of user configured value with no code ceiling." };
3839
+ if (active >= ceiling) return { allowed: false, reason: `The run already holds ${active} active breakpoint${active === 1 ? "" : "s"} and the reviewed breakpoint ceiling is ${ceiling}; revert one or review a wider ceiling.` };
3840
+ return { allowed: true };
3841
+ }
3842
+ function breakpointceilingof(settings) {
3843
+ return settings?.breakpointceiling;
3844
+ }
3845
+ function validatecdpgrammar(step, options) {
3846
+ const kind = step.kind;
3847
+ if (kind === "attachcdp") {
3848
+ if (!Array.isArray(options.domains) || options.domains.length === 0 || !options.domains.every((domain) => typeof domain === "string" && cdpdomains.includes(domain))) return { allowed: false, reason: `The attach needs a non-empty enabled domain list of the reviewed domain grammar: ${cdpdomains.join(", ")}.` };
3849
+ if (teardownplanof(options.teardown) === void 0) return { allowed: false, reason: "Every attach needs a reviewed teardown plan with its revert steps and resume policy before approval." };
3850
+ if (options.allowlist !== void 0) {
3851
+ const allowlist = cdpallowlistof(options.allowlist);
3852
+ if (!allowlist || !allowlist.domains.every((domain) => options.domains.includes(domain))) return { allowed: false, reason: "The reviewed method allowlist must stay inside the enabled domains of the attach." };
3853
+ }
3854
+ const budgetcheck = debugwaitbudgetallowed(typeof options.wait === "number" ? options.wait : void 0, void 0);
3855
+ if (!budgetcheck.allowed) return budgetcheck;
3856
+ return { allowed: true };
3857
+ }
3858
+ if (kind === "detachcdp") return { allowed: true };
3859
+ if (kind === "cdpcmd") {
3860
+ const command = options.command && typeof options.command === "object" && !Array.isArray(options.command) ? options.command : void 0;
3861
+ if (!command || typeof command.method !== "string" || methoddomain(command.method) === void 0) return { allowed: false, reason: "The raw command needs a reviewed method of the Domain.method form." };
3862
+ if (command.params !== void 0 && (typeof command.params !== "object" || Array.isArray(command.params))) return { allowed: false, reason: "The raw command params must be a JSON object." };
3863
+ if (command.resultpath !== void 0 && typeof command.resultpath !== "string") return { allowed: false, reason: "The reviewed result path must be a dotted path string." };
3864
+ return { allowed: true };
3865
+ }
3866
+ if (kind === "watchcdp") {
3867
+ if (!Array.isArray(options.events) || options.events.length === 0 || !options.events.every((rule) => cdpeventruleof(rule) !== void 0)) return { allowed: false, reason: "The event watch needs a non-empty reviewed list of domain event rules of the reviewed domain grammar." };
3868
+ let watchwindow;
3869
+ if (options.watch !== void 0) {
3870
+ const watch = options.watch;
3871
+ if (!watch || typeof watch !== "object" || Array.isArray(watch)) return { allowed: false, reason: "The reviewed event watch window must be an object." };
3872
+ const reviewed = watch;
3873
+ if (reviewed.window !== void 0) {
3874
+ if (typeof reviewed.window !== "number" || !Number.isFinite(reviewed.window) || reviewed.window < 0) return { allowed: false, reason: "The reviewed event watch window must be zero or a positive number of milliseconds." };
3875
+ watchwindow = reviewed.window;
3876
+ }
3877
+ }
3878
+ if (watchwindow === void 0) return { allowed: false, reason: "The event watch needs a reviewed lifetime window before any domain event is observed." };
3879
+ const budgetcheck = debugwaitbudgetallowed(watchwindow, typeof options.wait === "number" ? options.wait : void 0);
3880
+ if (!budgetcheck.allowed) return budgetcheck;
3881
+ return { allowed: true };
3882
+ }
3883
+ if (kind === "setbreakpoint") {
3884
+ const breakpoint = breakpointinputof(options.breakpoint);
3885
+ if (!breakpoint) return { allowed: false, reason: "The breakpoint needs a reviewed script url and a zero based line." };
3886
+ if (!ishttpsurl(breakpoint.url)) return { allowed: false, reason: "The breakpoint script url must be a reviewed HTTPS url." };
3887
+ if (breakpoint.condition !== void 0) {
3888
+ const conditioncheck = validatebreakpointcondition(breakpoint.condition);
3889
+ if (!conditioncheck.allowed) return conditioncheck;
3890
+ }
3891
+ return { allowed: true };
3892
+ }
3893
+ if (kind === "stepcode") {
3894
+ if (stepmodeof(options.mode) === void 0) return { allowed: false, reason: "The step code mode must be one of stepover, stepinto, stepout or resume." };
3895
+ return { allowed: true };
3896
+ }
3897
+ if (kind === "watchexpr") {
3898
+ if (watchexpressionof(options.expression) === void 0) return { allowed: false, reason: "The watch expression needs the reviewed expression text." };
3899
+ if (options.reviewed !== true) return { allowed: false, reason: "Watch expressions must be reviewed before evaluation; set the explicit reviewed flag on the step." };
3900
+ return { allowed: true };
3901
+ }
3902
+ if (kind === "overridescript") {
3903
+ const override = overrideinputof(options.override);
3904
+ if (!override) return { allowed: false, reason: "The script override needs a reviewed url pattern and its full fixture source." };
3905
+ if (patternorigin(override.urlpattern) === void 0) return { allowed: false, reason: "Script overrides without a named https origin pattern are refused." };
3906
+ if (options.reviewed !== true) return { allowed: false, reason: "The full fixture source must be reviewed before the script override runs; set the explicit reviewed flag on the step." };
3907
+ return { allowed: true };
3908
+ }
3909
+ return { allowed: true };
3910
+ }
3911
+ function planallowlist(steps) {
3912
+ const attach = steps.find((step) => step.kind === "attachcdp");
3913
+ if (!attach) return void 0;
3914
+ let options = {};
3915
+ try {
3916
+ options = parseoptions(attach);
3917
+ } catch {
3918
+ options = {};
3919
+ }
3920
+ const domains = Array.isArray(options.domains) ? options.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
3921
+ if (domains.length === 0) return void 0;
3922
+ const gated = cdpallowlistof(options.allowlist);
3923
+ return { domains, ...gated?.methods !== void 0 ? { methods: gated.methods } : {} };
3924
+ }
3578
3925
  function controltarget(step) {
3579
3926
  let options = {};
3580
3927
  try {
@@ -3988,6 +4335,10 @@ function validatestep(step, origin) {
3988
4335
  const timelinecheck = validatetimelinegrammar(step, options);
3989
4336
  if (!timelinecheck.allowed) return timelinecheck;
3990
4337
  }
4338
+ if (iscdpkind(step.kind)) {
4339
+ const cdpcheck = validatecdpgrammar(step, options);
4340
+ if (!cdpcheck.allowed) return cdpcheck;
4341
+ }
3991
4342
  if (step.kind === "tabcreate") {
3992
4343
  if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
3993
4344
  if (options.window !== void 0 && (typeof options.window !== "number" || !Number.isInteger(options.window) || options.window < 0)) return { allowed: false, reason: "The reviewed target window id must be a non-negative integer." };
@@ -4093,6 +4444,46 @@ function canexecute(input) {
4093
4444
  const timelinegatecheck = timelinegate(input.session, input.tabid, input.origin, now);
4094
4445
  if (!timelinegatecheck.allowed) return timelinegatecheck;
4095
4446
  }
4447
+ if (iscdpkind(input.step.kind)) {
4448
+ const debuggatecheck = debuggate(input.session, input.tabid, input.origin, now);
4449
+ if (!debuggatecheck.allowed) return debuggatecheck;
4450
+ if (!input.plan) return { allowed: false, reason: "The devtools protocol steps need an approved plan." };
4451
+ const allowlist = planallowlist(input.plan.steps);
4452
+ if (input.step.kind !== "attachcdp") {
4453
+ if (allowlist === void 0) return { allowed: false, reason: "The devtools protocol step needs the attachcdp step of the same plan with its enabled domains first." };
4454
+ if (input.step.kind === "cdpcmd") {
4455
+ let cdpoptions2 = {};
4456
+ try {
4457
+ cdpoptions2 = parseoptions(input.step);
4458
+ } catch {
4459
+ cdpoptions2 = {};
4460
+ }
4461
+ const command = cdpoptions2.command && typeof cdpoptions2.command === "object" && !Array.isArray(cdpoptions2.command) ? cdpoptions2.command : void 0;
4462
+ const method = typeof command?.method === "string" ? command.method : "";
4463
+ if (methoddomain(method) === void 0 || !allowlistcovers(allowlist, method)) return { allowed: false, reason: `The raw command ${method || ""} stays outside the enabled domain allowlist of the plan attach; review the attach domains or the method gates.` };
4464
+ }
4465
+ }
4466
+ let cdpoptions = {};
4467
+ try {
4468
+ cdpoptions = parseoptions(input.step);
4469
+ } catch {
4470
+ cdpoptions = {};
4471
+ }
4472
+ if (input.step.kind === "setbreakpoint") {
4473
+ const breakpoint = breakpointinputof(cdpoptions.breakpoint);
4474
+ if (breakpoint) {
4475
+ const targetgate = origincheck(input.session, breakpoint.url);
4476
+ if (!targetgate.allowed) return targetgate;
4477
+ }
4478
+ }
4479
+ if (input.step.kind === "overridescript") {
4480
+ const override = overrideinputof(cdpoptions.override);
4481
+ if (override) {
4482
+ const targetgate = origincheck(input.session, override.urlpattern);
4483
+ if (!targetgate.allowed) return targetgate;
4484
+ }
4485
+ }
4486
+ }
4096
4487
  if (iscontrolkind(input.step.kind)) {
4097
4488
  const controlgate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: "control the network" });
4098
4489
  if (!controlgate.allowed) return controlgate;
@@ -4326,9 +4717,15 @@ function recordtimeline(progress, planid, stepid, entry, now) {
4326
4717
  const outcome = { stepid, ok: true, summary: `Captured ${entry.entries} timeline entr${entry.entries === 1 ? "y" : "ies"} with ${entry.collapsed} collapsed repeat${entry.collapsed === 1 ? "" : "s"}, ${entry.errors} error${entry.errors === 1 ? "" : "s"}, ${entry.rejections} rejection${entry.rejections === 1 ? "" : "s"} and ${entry.longtasks} long task${entry.longtasks === 1 ? "" : "s"}.`, details: { timeline: entry }, at: now };
4327
4718
  return recordoutcome(base, planid, outcome, now);
4328
4719
  }
4720
+ function recordcdp(progress, planid, stepid, entry, now) {
4721
+ const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
4722
+ const summary = entry.family === "command" ? `The reviewed ${entry.method ?? "raw"} command of the ${entry.domain ?? "unknown"} domain returned in ${entry.duration ?? 0} millisecond${(entry.duration ?? 0) === 1 ? "" : "s"}${entry.errorclass !== void 0 ? ` with the ${entry.errorclass} error class` : ""}.` : `The devtools ${entry.family} step ran${entry.domain !== void 0 ? ` on the ${entry.domain} domain` : ""}${entry.events !== void 0 ? ` and matched ${entry.events} event${entry.events === 1 ? "" : "s"}` : ""}${entry.hits !== void 0 ? ` with ${entry.hits} hit${entry.hits === 1 ? "" : "s"}` : ""}${entry.frames !== void 0 ? ` capturing ${entry.frames} call frame${entry.frames === 1 ? "" : "s"}` : ""}.`;
4723
+ const outcome = { stepid, ok: entry.errorclass === void 0, summary, details: { cdp: entry }, at: now };
4724
+ return recordoutcome(base, planid, outcome, now);
4725
+ }
4329
4726
 
4330
4727
  // version.ts
4331
- var packageversion = "1.1.45";
4728
+ var packageversion = "1.1.46";
4332
4729
 
4333
4730
  // types.ts
4334
4731
  var protocolversion = packageversion;
@@ -4353,6 +4750,20 @@ function parseproposal(value, origin, grants) {
4353
4750
  const expiresat = typeof planinput.expiresat === "number" ? planinput.expiresat : createdat + 10 * 60 * 1e3;
4354
4751
  if (expiresat <= createdat) throw new Error("Plan expiry must be in the future.");
4355
4752
  const planwindow = expiresat - createdat;
4753
+ const attachinput = stepsinput.map((input) => record(input)).find((candidate) => candidate.kind === "attachcdp");
4754
+ let planallowlist2;
4755
+ if (attachinput !== void 0) {
4756
+ const attachoptions = (() => {
4757
+ try {
4758
+ return parseoptions({ id: "attach", kind: "attachcdp", summary: "attach", risk: "sensitive", ...typeof attachinput.options === "string" ? { options: attachinput.options } : {} });
4759
+ } catch {
4760
+ return {};
4761
+ }
4762
+ })();
4763
+ const domains = Array.isArray(attachoptions.domains) ? attachoptions.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
4764
+ const gated = cdpallowlistof(attachoptions.allowlist);
4765
+ planallowlist2 = domains.length > 0 ? { domains, ...gated?.methods !== void 0 ? { methods: gated.methods } : {} } : void 0;
4766
+ }
4356
4767
  const steps = stepsinput.map((input, index) => {
4357
4768
  const candidate = record(input);
4358
4769
  const kind = text(candidate.kind, `step ${index + 1} kind`);
@@ -4403,6 +4814,36 @@ function parseproposal(value, origin, grants) {
4403
4814
  if (!granted) throw new Error(`The ${step.kind} capture of ${origin} targets an origin outside the grants.`);
4404
4815
  if (debugoptions.level !== void 0 && !loglevels.includes(debugoptions.level)) throw new Error(`The reviewed level floor must be one of ${loglevels.join(", ")}.`);
4405
4816
  }
4817
+ if (iscdpkind(step.kind)) {
4818
+ const granted = covered.some((pattern) => {
4819
+ try {
4820
+ return new URL(origin).origin === new URL(pattern).origin;
4821
+ } catch {
4822
+ return false;
4823
+ }
4824
+ });
4825
+ if (!granted) throw new Error(`The ${step.kind} step of ${origin} targets an origin outside the grants.`);
4826
+ let cdpoptions = {};
4827
+ try {
4828
+ cdpoptions = parseoptions(step);
4829
+ } catch {
4830
+ cdpoptions = {};
4831
+ }
4832
+ if (step.kind === "attachcdp") {
4833
+ if (teardownplanof(cdpoptions.teardown) === void 0) throw new Error("Attach steps without a reviewed teardown plan are refused.");
4834
+ const domains = Array.isArray(cdpoptions.domains) ? cdpoptions.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
4835
+ if (domains.length === 0) throw new Error("Attach steps need a non-empty enabled domain list of the reviewed domain grammar.");
4836
+ const gated = cdpallowlistof(cdpoptions.allowlist);
4837
+ if (cdpoptions.allowlist !== void 0 && (!gated || !gated.domains.every((domain) => domains.includes(domain)))) throw new Error("The reviewed method allowlist must stay inside the enabled domains of the attach.");
4838
+ }
4839
+ if (step.kind === "cdpcmd") {
4840
+ const command = cdpoptions.command && typeof cdpoptions.command === "object" && !Array.isArray(cdpoptions.command) ? cdpoptions.command : void 0;
4841
+ const method = typeof command?.method === "string" ? command.method : "";
4842
+ if (methoddomain(method) === void 0) throw new Error("Raw commands need a reviewed method of the Domain.method form.");
4843
+ if (planallowlist2 === void 0) throw new Error("Raw command steps need the attachcdp step of the same plan with its enabled domains first.");
4844
+ if (!allowlistcovers(planallowlist2, method)) throw new Error(`The raw command ${method} stays outside the enabled domain allowlist of the plan attach.`);
4845
+ }
4846
+ }
4406
4847
  const evaluation = validatestep(step, origin);
4407
4848
  if (!evaluation.allowed) throw new Error(evaluation.reason);
4408
4849
  const target = outboundtarget(step);
@@ -4477,7 +4918,7 @@ function requestbody(input) {
4477
4918
  return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation, capabilities: input.capabilities });
4478
4919
  }
4479
4920
  function outcomeresponse(input) {
4480
- return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {}, ...input.timeline ? { timeline: input.timeline } : {} });
4921
+ return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {}, ...input.timeline ? { timeline: input.timeline } : {}, ...input.cdp ? { cdp: input.cdp } : {} });
4481
4922
  }
4482
4923
  function mapresponse(input) {
4483
4924
  return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, map: input.map });
@@ -4579,6 +5020,19 @@ function timelinereport(input) {
4579
5020
  function consolediffreport(input) {
4580
5021
  return { version: protocolversion, diff: input.diff };
4581
5022
  }
5023
+ function cdpreport(input) {
5024
+ const overrides = input.overrides.map((spec) => {
5025
+ const { source, ...metadata } = spec;
5026
+ void source;
5027
+ return metadata;
5028
+ });
5029
+ const grants = input.grants.map((grant) => {
5030
+ const { prompt, ...metadata } = grant;
5031
+ void prompt;
5032
+ return metadata;
5033
+ });
5034
+ return { version: protocolversion, sessions: input.sessions, commands: input.commands, events: input.events, breakpoints: input.breakpoints, pauses: input.pauses, watches: input.watches, overrides, grants };
5035
+ }
4582
5036
 
4583
5037
  // capture.ts
4584
5038
  var capturekinds = ["shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet"];
@@ -6267,7 +6721,7 @@ function stepoptions2(step) {
6267
6721
  }
6268
6722
  async function refreshcapabilities() {
6269
6723
  const report = await readcapabilities();
6270
- const withmedia = { ...report, captures: [...capturekinds], media: [...mediakinds], http: [...httpkinds], netwatch: [...socketkinds, ...netwatchkinds], control: [...controlkinds], debug: [...timelinekinds] };
6724
+ const withmedia = { ...report, captures: [...capturekinds], media: [...mediakinds], http: [...httpkinds], netwatch: [...socketkinds, ...netwatchkinds], control: [...controlkinds], debug: [...timelinekinds, ...cdpkinds] };
6271
6725
  await memory.setcapabilities(withmedia);
6272
6726
  return withmedia;
6273
6727
  }
@@ -6701,6 +7155,11 @@ async function tracktabupdate(tabid2, changeinfo) {
6701
7155
  await audit("timeline", `Watcher ${id} detached when the run tab navigated to ${url} at ${detached.at}; every page hook of the destroyed context is gone with it.`, { sessionid: session.id });
6702
7156
  activetimelinewatchers.delete(id);
6703
7157
  }
7158
+ for (const [runid, active] of [...activecdpsessions.entries()]) {
7159
+ if (active.session.detachedat !== void 0 || active.session.tabid !== tabid2) continue;
7160
+ await detachcdpforrun(runid, `the run tab navigated to ${url}`).catch(() => {
7161
+ });
7162
+ }
6704
7163
  return;
6705
7164
  }
6706
7165
  const buffer = navbuffers.get(tabid2) ?? [];
@@ -9460,6 +9919,234 @@ async function executetimelinestep(step, session, plan, tabid2, origin) {
9460
9919
  await audit("timeline", `Captured ${stored.length} timeline entr${stored.length === 1 ? "y" : "ies"} of ${origin} for the reviewed window of ${watchwindow} milliseconds${collapsed > 0 ? ` with ${collapsed} collapsed repeat${collapsed === 1 ? "" : "s"}` : ""}${errorids.length > 0 ? `, ${errorids.length} error${errorids.length === 1 ? "" : "s"}` : ""}${rejectionids.length > 0 ? `, ${rejectionids.length} rejection${rejectionids.length === 1 ? "" : "s"}` : ""}${longtasks.length > 0 ? ` and ${longtasks.length} long task${longtasks.length === 1 ? "" : "s"}` : ""}; console, error and task watching derives from page-injected listeners and the performance buffers.`, extra);
9461
9920
  return { ok: Boolean(output.ok), summary: output.summary, details: { ...output.details ?? {}, timeline: { entries: stored.length, levels: counts, collapsed }, entries: stored, errorids, rejectionids, longtasks: longtasks.map((task) => ({ ...task, blocking: blocking.blocking })), ...flagged.length > 0 ? { flagged } : {}, ...rotationtarget ? { rotation: rotationtarget } : {} } };
9462
9921
  }
9922
+ var cdpderivation = "devthink instrumented harness 1.1.46 (no chrome.debugger permission)";
9923
+ var activecdpsessions = /* @__PURE__ */ new Map();
9924
+ async function detachcdpforrun(runid, reason, userdetached = false) {
9925
+ const active = activecdpsessions.get(runid);
9926
+ if (!active) return;
9927
+ const at = Date.now();
9928
+ const decision = teardowncdpsession({ session: active.session, breakpoints: active.breakpoints, overrides: active.overrides, plan: active.teardown, userdetached, at });
9929
+ for (const id of decision.revertedbreakpoints) {
9930
+ const spec = active.breakpoints.find((candidate) => candidate.id === id);
9931
+ if (!spec) continue;
9932
+ const reverted = { ...spec, revertedat: at };
9933
+ await memory.addbreakpoint(reverted).catch(() => void 0);
9934
+ await audit("debugger", `Reverted the breakpoint ${id} of ${spec.url}:${spec.line} with ${spec.hits} hit${spec.hits === 1 ? "" : "s"} at ${reason}${spec.condition !== void 0 ? "; the condition stays reviewed in the record" : ""}.`, { stepid: spec.stepid }).catch(() => void 0);
9935
+ }
9936
+ for (const id of decision.revertedoverrides) {
9937
+ const spec = active.overrides.find((candidate) => candidate.id === id);
9938
+ if (!spec) continue;
9939
+ const reverted = { ...spec, revertedat: at };
9940
+ await memory.addscriptoverride(reverted).catch(() => void 0);
9941
+ await audit("debugger", `Reverted the script override ${id} of ${spec.urlpattern} with ${spec.hits} applied evaluation${spec.hits === 1 ? "" : "s"} at ${reason}; the fixture source never enters the audit trail.`, { stepid: spec.stepid }).catch(() => void 0);
9942
+ }
9943
+ for (const rule of active.rules) {
9944
+ if (rule.closedat !== void 0) continue;
9945
+ await memory.setcdpeventrule({ ...rule, closedat: at }).catch(() => void 0);
9946
+ }
9947
+ await memory.setcdpsession(decision.session).catch(() => void 0);
9948
+ await audit("debugger", `Session ${active.session.id} of ${active.session.origin} detached at ${reason} with the reviewed domains ${active.session.domains.join(", ")} enabled${decision.keepsalive ? "; the session record stays alive for review because the user detached the debugger" : ""}.`, { sessionid: active.session.id, planid: runid, stepid: active.session.stepid }).catch(() => void 0);
9949
+ if (!userdetached) {
9950
+ const revoked = await memory.revokedebuggergrants(active.session.origin, at).catch(() => 0);
9951
+ if (revoked > 0) await audit("debugger", `Revoked ${revoked} debugger consent record${revoked === 1 ? "" : "s"} of ${active.session.origin} at ${reason}; the next attach needs a new reviewed prompt.`, { planid: runid }).catch(() => void 0);
9952
+ }
9953
+ if (decision.paused) await audit("pause", `The devtools teardown at ${reason} paused the run for review; the resume policy is ${decision.resumepolicy}.`, { planid: runid }).catch(() => void 0);
9954
+ activecdpsessions.delete(runid);
9955
+ await refreshbadge().catch(() => void 0);
9956
+ }
9957
+ function cdpstateof(runid, session, teardown) {
9958
+ const existing = activecdpsessions.get(runid);
9959
+ if (existing) return existing;
9960
+ const created = { session, queue: [], rules: [], breakpoints: [], overrides: [], ...teardown !== void 0 ? { teardown } : {}, cancelled: false };
9961
+ activecdpsessions.set(runid, created);
9962
+ return created;
9963
+ }
9964
+ function pauseframes(entry) {
9965
+ if (!Array.isArray(entry)) return [];
9966
+ return entry.flatMap((frame) => {
9967
+ if (!frame || typeof frame !== "object") return [];
9968
+ const record2 = frame;
9969
+ if (typeof record2.url !== "string" || typeof record2.line !== "number") return [];
9970
+ return [{ url: record2.url, line: record2.line, ...typeof record2.column === "number" ? { column: record2.column } : {}, ...typeof record2.functionname === "string" ? { functionname: record2.functionname } : {} }];
9971
+ });
9972
+ }
9973
+ async function executecdpstep(step, session, plan, tabid2, origin) {
9974
+ const options = stepoptions2(step);
9975
+ const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
9976
+ const debuggatecheck = debuggate(session, tabid2, origin, Date.now());
9977
+ if (!debuggatecheck.allowed) throw new Error(debuggatecheck.reason ?? "The devtools protocol step stays outside the debug gate.");
9978
+ const grants = await memory.getdebuggergrants();
9979
+ const domains = Array.isArray(options.domains) ? options.domains.filter((domain) => typeof domain === "string") : [];
9980
+ if (step.kind === "attachcdp") {
9981
+ const consent = debuggerconsentcovers(origin, domains, grants);
9982
+ if (!consent.allowed) {
9983
+ const pending = grants.find((grant) => grant.origin === origin && grant.approved === void 0 && grant.domains.join(",") === domains.join(","));
9984
+ if (!pending) {
9985
+ const record2 = { id: randomid(), prompt: `Debugger attach on ${origin} with the reviewed domains ${domains.join(", ")} enabled for run ${plan.id}.`, origin, domains, consentedat: Date.now() };
9986
+ await memory.setdebuggergrant(record2);
9987
+ await refreshbadge();
9988
+ }
9989
+ throw new Error(`${consent.reason} The prompt is open in the review panel with the domain allowlist shown; approve it and run the step again.`);
9990
+ }
9991
+ } else {
9992
+ const consent = debuggerconsentcovers(origin, planallowlist(plan.steps)?.domains ?? [], grants);
9993
+ if (!consent.allowed) throw new Error(consent.reason ?? "The reviewed debugger consent is missing.");
9994
+ }
9995
+ const active = activecdpsessions.get(plan.id);
9996
+ if (step.kind !== "attachcdp" && (!active || active.session.detachedat !== void 0)) throw new Error("No attached devtools session covers this run; run the attachcdp step first.");
9997
+ if (step.kind === "attachcdp") {
9998
+ const teardown = teardownplanof(options.teardown);
9999
+ const sessionid = randomid();
10000
+ const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The attach returned no result." };
10001
+ if (!output.ok) return output;
10002
+ const record2 = attachcdpsession({ id: sessionid, runid: plan.id, stepid: step.id, tabid: tabid2, origin, domains, now: Date.now(), debuggerversion: cdpderivation });
10003
+ cdpstateof(plan.id, record2, teardown);
10004
+ await memory.setcdpsession(record2);
10005
+ await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "attach", domains: domains.length }, Date.now()));
10006
+ await audit("debugger", `Attached the devtools session ${sessionid} to the run tab ${tabid2} of ${origin} with the reviewed domains ${domains.join(", ")} enabled and the debugger version ${cdpderivation}; the teardown plan reverts ${teardown?.revertsteps.length ?? 0} step${(teardown?.revertsteps.length ?? 0) === 1 ? "" : "s"} with the ${teardown?.resumepolicy ?? "ask"} resume policy.`, extra);
10007
+ await refreshbadge();
10008
+ return { ok: true, summary: output.summary, details: { ...output.details ?? {}, session: record2, cdp: { sessionid, state: "attached", commandids: [] } } };
10009
+ }
10010
+ if (step.kind === "detachcdp") {
10011
+ if (!active) throw new Error("No attached devtools session covers this run.");
10012
+ const before = { breakpoints: active.breakpoints.filter((spec) => spec.revertedat === void 0).length, overrides: active.overrides.filter((spec) => spec.revertedat === void 0).length };
10013
+ await detachcdpforrun(plan.id, "the reviewed detachcdp step");
10014
+ await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "detach", hits: before.breakpoints + before.overrides }, Date.now()));
10015
+ return { ok: true, summary: `Detached the devtools session ${active.session.id} cleanly after reverting ${before.breakpoints} breakpoint${before.breakpoints === 1 ? "" : "s"} and ${before.overrides} override${before.overrides === 1 ? "" : "s"}; the detach is confirmed.`, details: { detached: true, ...before, cdp: { sessionid: active.session.id, state: "detached", commandids: active.queue.map((command) => command.id) } } };
10016
+ }
10017
+ if (step.kind === "cdpcmd") {
10018
+ if (!active) throw new Error("No attached devtools session covers this run.");
10019
+ const command = options.command && typeof options.command === "object" && !Array.isArray(options.command) ? options.command : {};
10020
+ const method = typeof command.method === "string" ? command.method : "";
10021
+ const planlist = planallowlist(plan.steps);
10022
+ const allowlist = { domains: active.session.domains, ...planlist?.methods !== void 0 ? { methods: planlist.methods } : {} };
10023
+ if (!allowlistcovers(allowlist, method)) throw new Error(`The raw command ${method} stays outside the enabled domain allowlist of the run attach.`);
10024
+ const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The command returned no result." };
10025
+ const duration = typeof output.details?.duration === "number" ? output.details.duration : 0;
10026
+ const errorclass = typeof output.details?.errorclass === "string" ? output.details.errorclass : output.ok ? void 0 : "protocolerror";
10027
+ const record2 = sendcdpcommand({ id: randomid(), sessionid: active.session.id, runid: plan.id, stepid: step.id, method, ...command.params && typeof command.params === "object" && !Array.isArray(command.params) ? { params: command.params } : {}, ...typeof command.resultpath === "string" ? { resultpath: command.resultpath } : {}, duration, ...errorclass !== void 0 ? { errorclass } : {}, at: Date.now() });
10028
+ active.queue = serializecdpcommand(active.queue, record2);
10029
+ await memory.addcdpcommand(record2);
10030
+ await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "command", method, domain: record2.domain, duration, ...errorclass !== void 0 ? { errorclass } : {} }, Date.now()));
10031
+ const pausedetails = output.details?.paused;
10032
+ let pauseid;
10033
+ if (pausedetails && typeof pausedetails === "object") {
10034
+ pauseid = randomid();
10035
+ const pause = capturepause({ id: pauseid, runid: plan.id, stepid: step.id, reason: typeof pausedetails.reason === "string" ? pausedetails.reason : "breakpoint", callframes: pauseframes(pausedetails.frames), ...typeof pausedetails.hitbreakpoint === "string" ? { hitbreakpoint: pausedetails.hitbreakpoint } : {}, at: Date.now() });
10036
+ await memory.addpause(pause);
10037
+ await audit("debugger", `The run paused on breakpoint ${pause.hitbreakpoint ?? "unknown"} with ${pause.callframes.length} call frame${pause.callframes.length === 1 ? "" : "s"} captured.`, extra);
10038
+ }
10039
+ await audit("debugger", `Sent the reviewed command ${method} of the ${record2.domain} domain to session ${active.session.id}; it returned in ${duration} millisecond${duration === 1 ? "" : "s"}${errorclass !== void 0 ? ` with the ${errorclass} error class` : ""}; the params never enter the audit trail.`, extra);
10040
+ await refreshbadge();
10041
+ if (!output.ok) return { ok: false, summary: output.summary, details: { ...output.details ?? {}, command: record2, cdp: { sessionid: active.session.id, state: "attached", commandids: active.queue.map((item) => item.id) } } };
10042
+ return { ok: true, summary: output.summary, details: { ...output.details ?? {}, command: record2, ...pauseid !== void 0 ? { pauseid } : {}, cdp: { sessionid: active.session.id, state: "attached", commandids: active.queue.map((item) => item.id) } } };
10043
+ }
10044
+ if (step.kind === "watchcdp") {
10045
+ if (!active) throw new Error("No attached devtools session covers this run.");
10046
+ const rules = [];
10047
+ for (const rule of Array.isArray(options.events) ? options.events : []) {
10048
+ const parsed = rule && typeof rule === "object" && !Array.isArray(rule) ? rule : void 0;
10049
+ const normalized = parsed !== void 0 ? await Promise.resolve(parsed) : void 0;
10050
+ const base = normalized !== void 0 ? { domain: String(normalized.domain ?? ""), event: String(normalized.event ?? ""), ...typeof normalized.match === "string" ? { match: normalized.match } : {} } : void 0;
10051
+ if (!base || !base.domain || !base.event) continue;
10052
+ const record2 = { id: randomid(), sessionid: active.session.id, runid: plan.id, stepid: step.id, domain: base.domain, event: base.event, ...base.match !== void 0 ? { match: base.match } : {}, events: 0, registeredat: Date.now() };
10053
+ rules.push(record2);
10054
+ active.rules.push(record2);
10055
+ await memory.setcdpeventrule(record2);
10056
+ }
10057
+ if (rules.length === 0) throw new Error("The event watch needs at least one reviewed domain event rule.");
10058
+ const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The event watch returned no result." };
10059
+ const observed = detailarray(output.details, "events").flatMap((event) => {
10060
+ if (!event || typeof event !== "object") return [];
10061
+ const record2 = event;
10062
+ if (typeof record2.domain !== "string" || typeof record2.event !== "string") return [];
10063
+ return [{ domain: record2.domain, event: record2.event, ...typeof record2.payload === "string" ? { payload: record2.payload } : {} }];
10064
+ });
10065
+ const matched = watchcdpevents(rules, observed);
10066
+ for (const rule of active.rules) {
10067
+ const count = matched.matched.filter((item) => item.ruleid === rule.id).length;
10068
+ if (count > 0) rule.events += count;
10069
+ await memory.setcdpeventrule({ ...rule });
10070
+ }
10071
+ for (const item of matched.matched) {
10072
+ await memory.addtimelineentry({ id: randomid(), runid: plan.id, stepid: step.id, time: Date.now(), level: "info", source: "cdp", message: `${item.domain}.${item.event}${item.payload !== void 0 ? `: ${item.payload}` : ""}` });
10073
+ }
10074
+ await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "watch", events: matched.matched.length, ...rules[0] !== void 0 ? { domain: rules[0].domain } : {} }, Date.now()));
10075
+ await audit("debugger", `Watched ${rules.length} reviewed domain event rule${rules.length === 1 ? "" : "s"} of session ${active.session.id} for the reviewed window and forwarded ${matched.matched.length} matched event${matched.matched.length === 1 ? "" : "s"} into the run timeline; the subscription closes at run end or on step cancel.`, extra);
10076
+ await refreshbadge();
10077
+ return { ok: Boolean(output.ok), summary: output.summary, details: { ...output.details ?? {}, eventcounts: matched.counts, matched: matched.matched, cdp: { sessionid: active.session.id, state: "attached", commandids: [] } } };
10078
+ }
10079
+ if (step.kind === "setbreakpoint") {
10080
+ if (!active) throw new Error("No attached devtools session covers this run.");
10081
+ const input = breakpointinputof(options.breakpoint);
10082
+ if (!input) throw new Error("The breakpoint input is absent.");
10083
+ const settings = await memory.getsettings();
10084
+ const budgetcheck = breakpointbudgetallowed(active.breakpoints.filter((spec) => spec.revertedat === void 0).length, breakpointceilingof(settings));
10085
+ if (!budgetcheck.allowed) throw new Error(budgetcheck.reason ?? "The breakpoint ceiling refused the registration.");
10086
+ const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The breakpoint registration returned no result." };
10087
+ if (!output.ok) return output;
10088
+ const registered = output.details?.breakpoint;
10089
+ const id = typeof registered?.id === "string" ? registered.id : randomid();
10090
+ const record2 = { id, runid: plan.id, stepid: step.id, url: input.url, line: input.line, ...input.column !== void 0 ? { column: input.column } : {}, ...input.condition !== void 0 ? { condition: input.condition } : {}, hits: 0, registeredat: Date.now() };
10091
+ active.breakpoints.push(record2);
10092
+ await memory.addbreakpoint(record2);
10093
+ await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "breakpoint", hits: 0 }, Date.now()));
10094
+ await audit("debugger", `Registered the reviewed breakpoint ${id} at ${input.url}:${input.line}${input.condition !== void 0 ? ` under the reviewed condition` : ""} of session ${active.session.id}; the run pauses when an instrumented probe of the run hits it.`, extra);
10095
+ return { ok: true, summary: output.summary, details: { ...output.details ?? {}, breakpoint: record2, cdp: { sessionid: active.session.id, state: "attached", commandids: [] } } };
10096
+ }
10097
+ if (step.kind === "stepcode") {
10098
+ if (!active) throw new Error("No attached devtools session covers this run.");
10099
+ const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The step returned no result." };
10100
+ const pausedetails = output.details?.pausestate;
10101
+ let pauseid;
10102
+ if (pausedetails && typeof pausedetails === "object") {
10103
+ pauseid = randomid();
10104
+ const domversion = await memory.nextobservationversion().catch(() => void 0);
10105
+ const pause = capturepause({ id: pauseid, runid: plan.id, stepid: step.id, reason: typeof pausedetails.reason === "string" ? pausedetails.reason : "step", callframes: pauseframes(pausedetails.frames), ...typeof pausedetails.hitbreakpoint === "string" ? { hitbreakpoint: pausedetails.hitbreakpoint } : {}, ...domversion !== void 0 ? { domsnapshotid: String(domversion) } : {}, at: Date.now() });
10106
+ await memory.addpause(pause);
10107
+ }
10108
+ await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "step", frames: pauseframes(pausedetails?.frames).length }, Date.now()));
10109
+ await audit("debugger", `Stepped the paused probe of session ${active.session.id} with the ${stepmodeof(options.mode) ?? "reviewed"} mode and captured the pause state${pauseid !== void 0 ? ` as ${pauseid} with the dom snapshot through the page bridge` : ""}.`, extra);
10110
+ return { ok: Boolean(output.ok), summary: output.summary, details: { ...output.details ?? {}, ...pauseid !== void 0 ? { pauseid } : {}, cdp: { sessionid: active.session.id, state: "attached", commandids: [] } } };
10111
+ }
10112
+ if (step.kind === "watchexpr") {
10113
+ if (!active) throw new Error("No attached devtools session covers this run.");
10114
+ const input = watchexpressionof(options.expression);
10115
+ if (!input) throw new Error("The watch expression input is absent.");
10116
+ const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The watch expression returned no result." };
10117
+ const stored = (await memory.getwatchexpressions()).find((item) => item.stepid === step.id);
10118
+ const base = stored ?? { id: randomid(), runid: plan.id, stepid: step.id, expression: input.expression, scope: input.scope, reviewed: true, values: [], at: Date.now() };
10119
+ if (output.ok) {
10120
+ const value = typeof output.details?.value === "string" ? output.details.value : "";
10121
+ const pauses = await memory.listpauses(plan.id);
10122
+ const pauseid = pauses[0]?.id ?? "nopause";
10123
+ const updated = recordwatchvalue(base, pauseid, value, Date.now());
10124
+ await memory.setwatchexpression(updated);
10125
+ await memory.addtimelineentry({ id: randomid(), runid: plan.id, stepid: step.id, time: Date.now(), level: "debug", source: "cdp", message: `Watch expression ${input.expression} evaluated to ${value} at pause ${pauseid} in the ${input.scope} scope.` });
10126
+ }
10127
+ await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "watch", events: base.values.length }, Date.now()));
10128
+ await audit("debugger", `Evaluated the reviewed watch expression of step ${step.id} at the pause in the ${input.scope} scope; the value stores with its pause scope in the timeline and the expression text stays reviewed.`, extra);
10129
+ return { ok: Boolean(output.ok), summary: output.summary, details: { ...output.details ?? {}, cdp: { sessionid: active.session.id, state: "attached", commandids: [] } } };
10130
+ }
10131
+ if (step.kind === "overridescript") {
10132
+ if (!active) throw new Error("No attached devtools session covers this run.");
10133
+ const override = options.override && typeof options.override === "object" && !Array.isArray(options.override) ? options.override : void 0;
10134
+ const urlpattern = typeof override?.urlpattern === "string" ? override.urlpattern : "";
10135
+ const source = typeof override?.source === "string" ? override.source : "";
10136
+ if (!urlpattern || !source) throw new Error("The script override input is absent.");
10137
+ const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The override returned no result." };
10138
+ if (!output.ok) return output;
10139
+ const applied = output.details?.override;
10140
+ const id = typeof applied?.id === "string" ? applied.id : randomid();
10141
+ const record2 = { id, runid: plan.id, stepid: step.id, urlpattern, source, reviewed: true, reviewedat: Date.now(), hits: 0, appliedat: Date.now() };
10142
+ active.overrides.push(record2);
10143
+ await memory.addscriptoverride(record2);
10144
+ await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "override", hits: 0 }, Date.now()));
10145
+ await audit("debugger", `Applied the reviewed script override ${id} of ${urlpattern} on new document evaluation through the page instrumentation; the fixture reverts at run end and the source never enters the audit trail.`, extra);
10146
+ return { ok: true, summary: output.summary, details: { ...output.details ?? {}, override: { id, urlpattern, appliedat: record2.appliedat, reviewed: true }, cdp: { sessionid: active.session.id, state: "attached", commandids: [] } } };
10147
+ }
10148
+ return { ok: false, summary: "The devtools step is not part of the instrumented family." };
10149
+ }
9463
10150
  var activerules = /* @__PURE__ */ new Map();
9464
10151
  var activeauthflows = /* @__PURE__ */ new Map();
9465
10152
  function rulesetof(runid) {
@@ -9850,11 +10537,12 @@ async function refreshbadge() {
9850
10537
  const recordingprompts = (await memory.getrecordingconsents()).filter((record2) => record2.approved === void 0).length;
9851
10538
  const fetchprompts = (await memory.getfetchconsents()).filter((consent) => consent.approved === void 0).length;
9852
10539
  const consoleprompts = (await memory.getconsoleconsents()).filter((consent) => consent.approved === void 0).length;
10540
+ const debuggerprompts = (await memory.getdebuggergrants()).filter((grant) => grant.approved === void 0).length;
9853
10541
  const observedrequests = (await memory.getexchanges()).length;
9854
10542
  const livechannels = (await memory.getchannels()).filter((channel) => channel.state === "open" || channel.state === "connecting").length;
9855
10543
  const activerulescount = [...activerules.values()].reduce((total2, ruleset) => total2 + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0);
9856
10544
  const tasktabs2 = new Set(badges.map((badge) => badge.tabid)).size;
9857
- const total = (queues?.prefetch ?? 0) + (queues?.batchopen ?? 0) + tasktabs2 + prompts + consents + quarantined + datasets + captures + media + recordingprompts + fetchprompts + consoleprompts + observedrequests + livechannels + activerulescount;
10545
+ const total = (queues?.prefetch ?? 0) + (queues?.batchopen ?? 0) + tasktabs2 + prompts + consents + quarantined + datasets + captures + media + recordingprompts + fetchprompts + consoleprompts + debuggerprompts + observedrequests + livechannels + activerulescount;
9858
10546
  await chrome.action.setBadgeText({ text: total > 0 ? String(total) : "" }).catch(() => {
9859
10547
  });
9860
10548
  }
@@ -9925,6 +10613,9 @@ async function executestep(stepid) {
9925
10613
  } else if (isdebugkind(step.kind)) {
9926
10614
  if (!session || !plan || plan.state !== "approved") throw new Error("Debugging kinds refuse to run outside an approved session plan.");
9927
10615
  output = await executetimelinestep(step, session, plan, tab.id, origin);
10616
+ } else if (iscdpkind(step.kind)) {
10617
+ if (!session || !plan || plan.state !== "approved") throw new Error("Devtools protocol kinds refuse to run outside an approved session plan.");
10618
+ output = await executecdpstep(step, session, plan, tab.id, origin);
9928
10619
  } else {
9929
10620
  if (step.target && freshcheckkinds.has(step.kind)) {
9930
10621
  const fresh = await snapshot(tab.id);
@@ -9982,6 +10673,8 @@ async function executestep(stepid) {
9982
10673
  });
9983
10674
  await revertcontrolsforrun(plan.id, "plan completion").catch(() => {
9984
10675
  });
10676
+ await detachcdpforrun(plan.id, "plan completion").catch(() => {
10677
+ });
9985
10678
  const done = { ...plan, state: "completed", completedat: Date.now() };
9986
10679
  await memory.setplan(done);
9987
10680
  await audit("complete", "Every reviewed step of the approved plan has executed.", { ...session ? { sessionid: session.id } : {}, planid: done.id });
@@ -10158,7 +10851,7 @@ async function handlerequest(message, sender) {
10158
10851
  const livetab = session ? await chrome.tabs.get(session.tabid).catch(() => void 0) : void 0;
10159
10852
  const waitprofile = session ? waitprofiles.find((record2) => record2.origin === session.origin) : void 0;
10160
10853
  const livestate = { phase: livetab?.status === "loading" ? "loading" : "complete", ...navrecords[0] ? { finalurl: navrecords[0].finalurl, redirects: navrecords[0].chain } : {} };
10161
- return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), socketsactive: activesockets.size, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {} };
10854
+ return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), cdpsessions: await memory.getcdpsessions(), cdpcommands: await memory.getcdpcommands(), cdpeventrules: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watchexpressions: await memory.getwatchexpressions(), scriptoverrides: await memory.getscriptoverrides(), debuggergrants: await memory.getdebuggergrants(), pauseretention: runsettings?.pauseretention, breakpointceiling: runsettings?.breakpointceiling, cdpattached: [...activecdpsessions.values()].filter((active) => active.session.detachedat === void 0).length, socketsactive: activesockets.size, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {} };
10162
10855
  }
10163
10856
  case "capabilities":
10164
10857
  return refreshcapabilities();
@@ -10932,6 +11625,68 @@ async function handlerequest(message, sender) {
10932
11625
  await refreshbadge();
10933
11626
  return { closed: true, id: inputclose.id ?? "" };
10934
11627
  }
11628
+ case "setpauseretention": {
11629
+ const inputretention = message;
11630
+ const settings = await memory.getsettings();
11631
+ const retention = typeof inputretention.retention === "number" && Number.isInteger(inputretention.retention) && inputretention.retention >= 0 ? inputretention.retention : void 0;
11632
+ await memory.setsettings({ ...settings, ...retention !== void 0 ? { pauseretention: retention } : {} });
11633
+ await audit("configure", `The user set the pause capture retention to ${retention === void 0 ? "keep every capture" : `${retention} capture${retention === 1 ? "" : "s"}`}; the pause reason and hit breakpoint always survive.`);
11634
+ return { pauseretention: retention };
11635
+ }
11636
+ case "setbreakpointceiling": {
11637
+ const inputceiling = message;
11638
+ const settings = await memory.getsettings();
11639
+ const ceiling = typeof inputceiling.ceiling === "number" && Number.isInteger(inputceiling.ceiling) && inputceiling.ceiling >= 0 ? inputceiling.ceiling : void 0;
11640
+ await memory.setsettings({ ...settings, ...ceiling !== void 0 ? { breakpointceiling: ceiling } : {} });
11641
+ await audit("configure", `The user set the breakpoint ceiling to ${ceiling === void 0 ? "no ceiling" : `${ceiling} breakpoint${ceiling === 1 ? "" : "s"} per run`}; an absent value never refuses a breakpoint.`);
11642
+ return { breakpointceiling: ceiling };
11643
+ }
11644
+ case "approvedebuggerconsent": {
11645
+ const inputapprove = message;
11646
+ const records = await memory.getdebuggergrants();
11647
+ const record2 = records.find((item) => item.id === inputapprove.id);
11648
+ if (!record2) throw new Error("No debugger consent prompt matches the id.");
11649
+ const decided = { ...record2, approved: true, usedat: Date.now() };
11650
+ await memory.setdebuggergrant(decided);
11651
+ await audit("debugger", `Debugger consent on ${record2.origin} approved from the review panel with the domain allowlist ${record2.domains.join(", ")} shown; the decision covers those domains only.`, { stepid: record2.id });
11652
+ await refreshbadge();
11653
+ return { approved: true, origin: record2.origin, domains: record2.domains };
11654
+ }
11655
+ case "revokedebuggerconsent": {
11656
+ const session = await memory.getsession();
11657
+ const plan = await memory.getplan();
11658
+ const origin = session?.origin;
11659
+ if (!origin) throw new Error("No active session origin covers a debugger consent revoke.");
11660
+ const revoked = await memory.revokedebuggergrants(origin, Date.now());
11661
+ if (plan) await detachcdpforrun(plan.id, "the user detached the debugger", true);
11662
+ if (session) await memory.setsession({ ...session, pausedat: session.pausedat ?? Date.now() });
11663
+ await audit("debugger", `The user detached the debugger: ${revoked} consent record${revoked === 1 ? "" : "s"} of ${origin} revoked, every breakpoint and override reverted, the session record stays alive for review and the run paused for review before continuing.`, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {} });
11664
+ await refreshbadge();
11665
+ return { revoked, paused: true };
11666
+ }
11667
+ case "revertcdpoverride": {
11668
+ const inputrevert = message;
11669
+ const plan = await memory.getplan();
11670
+ if (!plan) throw new Error("No plan is available for a script override revert.");
11671
+ const active = activecdpsessions.get(plan.id);
11672
+ const stored = (await memory.getscriptoverrides()).find((spec) => spec.id === inputrevert.id);
11673
+ if (!stored) throw new Error(`No script override matches ${inputrevert.id ?? ""}.`);
11674
+ if (stored.revertedat !== void 0) throw new Error("The script override already reverted.");
11675
+ const reverted = { ...stored, revertedat: Date.now() };
11676
+ if (active) {
11677
+ const index = active.overrides.findIndex((spec) => spec.id === stored.id);
11678
+ if (index >= 0) active.overrides[index] = reverted;
11679
+ }
11680
+ await memory.addscriptoverride(reverted);
11681
+ await audit("debugger", `Reverted the script override ${reverted.id} of ${reverted.urlpattern} from the review panel; later evaluations run the original source again and the fixture source never entered the audit trail.`, { planid: plan.id, stepid: reverted.stepid });
11682
+ await refreshbadge();
11683
+ return { reverted: true, id: reverted.id };
11684
+ }
11685
+ case "cdpreport": {
11686
+ const plan = await memory.getplan();
11687
+ if (!plan) throw new Error("No plan is available for a devtools protocol envelope.");
11688
+ return cdpreport({ sessions: await memory.getcdpsessions(), commands: await memory.getcdpcommands(), events: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watches: await memory.getwatchexpressions(), overrides: await memory.getscriptoverrides(), grants: await memory.getdebuggergrants() });
11689
+ }
10935
11690
  case "stop": {
10936
11691
  const session = await memory.getsession();
10937
11692
  for (const [id, controller] of [...activefetches.entries()]) {
@@ -10953,12 +11708,19 @@ async function handlerequest(message, sender) {
10953
11708
  });
10954
11709
  await revertcontrolsforrun(stoppedplan.id, "run cancel").catch(() => {
10955
11710
  });
11711
+ await detachcdpforrun(stoppedplan.id, "run cancel").catch(() => {
11712
+ });
10956
11713
  } else {
10957
11714
  await closechannelsforrun("none").catch(() => {
10958
11715
  });
10959
11716
  await revertcontrolsforrun("none", "run cancel").catch(() => {
10960
11717
  });
10961
11718
  }
11719
+ for (const [runid, active] of [...activecdpsessions.entries()]) {
11720
+ active.cancelled = true;
11721
+ await detachcdpforrun(runid, "run cancel").catch(() => {
11722
+ });
11723
+ }
10962
11724
  for (const [id, active] of [...activerecordings.entries()]) {
10963
11725
  const finished = finishrecording(active.record, Date.now());
10964
11726
  await memory.addmedia(finished).catch(() => {