@wenathlan/extension 1.1.60 → 1.1.61
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.
- package/README.md +4 -3
- package/dist/immutablelog.d.ts +73 -0
- package/dist/immutablelog.d.ts.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +534 -1
- package/dist/index.js.map +4 -4
- package/dist/maskinputs.d.ts +52 -0
- package/dist/maskinputs.d.ts.map +1 -0
- package/dist/memory.d.ts +65 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/originpolicy.d.ts +150 -0
- package/dist/originpolicy.d.ts.map +1 -0
- package/dist/policy.d.ts +56 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +76 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/types.d.ts +146 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +715 -51
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/pagebridge.js +91 -28
- package/extension/dist/pagebridge.js.map +3 -3
- package/extension/dist/popup.html +1 -1
- package/extension/dist/popup.js +39 -0
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.html +1 -1
- package/extension/dist/sidepanel.js +279 -0
- package/extension/dist/sidepanel.js.map +4 -4
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
|
@@ -2078,6 +2078,73 @@ function exportrunstate(states, now) {
|
|
|
2078
2078
|
};
|
|
2079
2079
|
}
|
|
2080
2080
|
|
|
2081
|
+
// immutablelog.ts
|
|
2082
|
+
async function sha2562(payload) {
|
|
2083
|
+
const bytes = new TextEncoder().encode(payload);
|
|
2084
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
2085
|
+
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
2086
|
+
}
|
|
2087
|
+
function entrybody(entry) {
|
|
2088
|
+
return JSON.stringify({ id: entry.id, runid: entry.runid, kind: entry.kind, summary: entry.summary, origin: entry.origin, ...entry.stepid !== void 0 ? { stepid: entry.stepid } : {}, at: entry.at });
|
|
2089
|
+
}
|
|
2090
|
+
async function entryhashof(input) {
|
|
2091
|
+
return { previous: input.previous, current: await sha2562(`${input.previous}
|
|
2092
|
+
${entrybody(input.entry)}`), algorithm: "sha-256" };
|
|
2093
|
+
}
|
|
2094
|
+
async function logentryof(input) {
|
|
2095
|
+
if (input.summary.trim() === "") throw new Error("The log entry needs its summary in plain language.");
|
|
2096
|
+
if (input.origin.trim() === "") throw new Error("The log entry needs its origin provenance.");
|
|
2097
|
+
const entry = { id: input.id ?? randomid(), runid: input.runid, kind: input.kind, summary: input.summary, origin: input.origin, ...input.stepid !== void 0 ? { stepid: input.stepid } : {}, at: input.at };
|
|
2098
|
+
return { ...entry, hash: await entryhashof({ previous: input.previous, entry }) };
|
|
2099
|
+
}
|
|
2100
|
+
function openrunlog(input) {
|
|
2101
|
+
if (input.runid.trim() === "" || input.sessionid.trim() === "") throw new Error("The run log needs its run and session ids.");
|
|
2102
|
+
return { runid: input.runid, sessionid: input.sessionid, entries: [], updatedat: input.now };
|
|
2103
|
+
}
|
|
2104
|
+
function lasthashof(log) {
|
|
2105
|
+
const entry = log.entries[log.entries.length - 1];
|
|
2106
|
+
return entry === void 0 ? "0".repeat(64) : entry.hash.current;
|
|
2107
|
+
}
|
|
2108
|
+
async function appendlogentry(input) {
|
|
2109
|
+
if (input.log.seal !== void 0) throw new Error(`The run log of ${input.log.runid} sealed at ${input.log.seal.sealedat} and accepts no append; the seal is terminal.`);
|
|
2110
|
+
const entry = await logentryof({ ...input.id !== void 0 ? { id: input.id } : {}, runid: input.log.runid, kind: input.kind, summary: input.summary, origin: input.origin, ...input.stepid !== void 0 ? { stepid: input.stepid } : {}, at: input.at, previous: lasthashof(input.log) });
|
|
2111
|
+
return { ...input.log, entries: [...input.log.entries, entry], updatedat: input.at };
|
|
2112
|
+
}
|
|
2113
|
+
async function sealrunlog(log, now) {
|
|
2114
|
+
if (log.seal !== void 0) throw new Error(`The run log of ${log.runid} already sealed at ${log.seal.sealedat}; the seal is terminal.`);
|
|
2115
|
+
if (log.entries.length === 0) throw new Error("The run log seals at completion with at least one entry.");
|
|
2116
|
+
const sealhash = await entryhashof({ previous: lasthashof(log), entry: { id: `seal:${log.runid}`, runid: log.runid, kind: "seal", summary: `The run ${log.runid} completed and the log sealed with ${log.entries.length} entries.`, origin: log.entries[log.entries.length - 1]?.origin ?? log.runid, at: now } });
|
|
2117
|
+
const seal = { runid: log.runid, entries: log.entries.length, sealhash, sealedat: now };
|
|
2118
|
+
return { log: { ...log, seal, updatedat: now }, seal };
|
|
2119
|
+
}
|
|
2120
|
+
async function verifylogchain(entries) {
|
|
2121
|
+
let previous = "0".repeat(64);
|
|
2122
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
2123
|
+
const entry = entries[index];
|
|
2124
|
+
if (entry === void 0) continue;
|
|
2125
|
+
if (entry.hash.previous !== previous) return { valid: false, brokenat: index, reason: `The chain link of entry ${index} carries the previous hash ${entry.hash.previous} while its predecessor hashes to ${previous}; the chain reports tamper evidence.` };
|
|
2126
|
+
const expected = await entryhashof({ previous, entry: { id: entry.id, runid: entry.runid, kind: entry.kind, summary: entry.summary, origin: entry.origin, ...entry.stepid !== void 0 ? { stepid: entry.stepid } : {}, at: entry.at } });
|
|
2127
|
+
if (entry.hash.current !== expected.current) return { valid: false, brokenat: index, reason: `The entry hash of entry ${index} matches neither its body nor its predecessor hash; the chain reports tamper evidence.` };
|
|
2128
|
+
previous = entry.hash.current;
|
|
2129
|
+
}
|
|
2130
|
+
return { valid: true, reason: `The hash chain of ${entries.length} entr${entries.length === 1 ? "y" : "ies"} verifies from the genesis hash to the last entry.` };
|
|
2131
|
+
}
|
|
2132
|
+
async function readverifiedlog(log) {
|
|
2133
|
+
const verification = await verifylogchain(log.entries);
|
|
2134
|
+
if (!verification.valid) return { ok: false, entries: [], reason: verification.reason };
|
|
2135
|
+
return { ok: true, entries: [...log.entries], reason: verification.reason };
|
|
2136
|
+
}
|
|
2137
|
+
async function chainreportof(log) {
|
|
2138
|
+
const verification = await verifylogchain(log.entries);
|
|
2139
|
+
if (!verification.valid) return { runid: log.runid, valid: false, entries: log.entries.length, ...verification.brokenat !== void 0 ? { brokenat: verification.brokenat } : {}, reason: verification.reason };
|
|
2140
|
+
return { runid: log.runid, valid: true, entries: log.entries.length, reason: verification.reason, ...log.seal !== void 0 ? { sealhash: log.seal.sealhash.current, sealedat: log.seal.sealedat } : {} };
|
|
2141
|
+
}
|
|
2142
|
+
async function exportlogchain(log) {
|
|
2143
|
+
const read = await readverifiedlog(log);
|
|
2144
|
+
if (!read.ok) return { runid: log.runid, entries: 0, chainvalid: false, reason: read.reason, log: [] };
|
|
2145
|
+
return { runid: log.runid, entries: read.entries.length, chainvalid: true, reason: read.reason, ...log.seal !== void 0 ? { sealhash: log.seal.sealhash.current, sealedat: log.seal.sealedat } : {}, log: read.entries };
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2081
2148
|
// memory.ts
|
|
2082
2149
|
var sessionmemory = class {
|
|
2083
2150
|
constructor(adapter) {
|
|
@@ -4537,6 +4604,139 @@ var sessionmemory = class {
|
|
|
4537
4604
|
async exportrunstates() {
|
|
4538
4605
|
return exportrunstate(await this.listrunstates(), Date.now());
|
|
4539
4606
|
}
|
|
4607
|
+
/**
|
|
4608
|
+
* Security part one persistence of the 1.1.61 family.
|
|
4609
|
+
* The trust boundary records live here: the per origin automation allowlist scoped per profile workspace with one exact origin per entry, the per site originprofiles with their kind grants and denials, the active consentwindows with their expiry timestamps that expire closed past their boundary, the mid run revokerun events with the halted step ids that stay visible for later consent prompts, the fresh class consents per origin, the mask rules for field shapes per origin, and the sealed immutable run logs with their final hash.
|
|
4610
|
+
* The run log store exposes no update or delete path: appends land whole, the seal closes a log with its final hash and the read path verifies the chain before returning a single entry so a broken link refuses the read.
|
|
4611
|
+
* The adapter seam keeps every accessor a one line storage delegation so a future append only backend replaces the adapter only; the current storage areas offer no append only hardware, so the honest derivation is the hash chain that makes any rewrite detectable at read time.
|
|
4612
|
+
*/
|
|
4613
|
+
/** Replaces the per origin automation allowlist of the profile workspaces; every entry carries one exact origin with no wildcard expansion. */
|
|
4614
|
+
async setautomationallowlist(entries) {
|
|
4615
|
+
return this.adapter.set("automationallowlist", entries);
|
|
4616
|
+
}
|
|
4617
|
+
/** Returns the per origin automation allowlist entries, oldest grant first. */
|
|
4618
|
+
async getautomationallowlist() {
|
|
4619
|
+
return await this.adapter.get("automationallowlist") ?? [];
|
|
4620
|
+
}
|
|
4621
|
+
/** Adds one exact origin to the automation allowlist of a profile workspace; a duplicate origin keeps its first grant. */
|
|
4622
|
+
async addallowlistorigin(entry) {
|
|
4623
|
+
const entries = await this.getautomationallowlist();
|
|
4624
|
+
if (entries.some((candidate) => candidate.origin === entry.origin && candidate.profileid === entry.profileid)) return;
|
|
4625
|
+
await this.setautomationallowlist([...entries, entry]);
|
|
4626
|
+
}
|
|
4627
|
+
/** Removes one origin from the automation allowlist; the denydefault posture refuses the origin again after the removal. */
|
|
4628
|
+
async removeallowlistorigin(origin, profileid) {
|
|
4629
|
+
await this.setautomationallowlist((await this.getautomationallowlist()).filter((entry) => !(entry.origin === origin && entry.profileid === profileid)));
|
|
4630
|
+
}
|
|
4631
|
+
/** Replaces the per site origin profiles with their kind grants and denials; one profile per origin. */
|
|
4632
|
+
async setoriginprofiles(profiles) {
|
|
4633
|
+
return this.adapter.set("originprofiles", profiles);
|
|
4634
|
+
}
|
|
4635
|
+
/** Returns the stored per site origin profiles, oldest update first. */
|
|
4636
|
+
async getoriginprofiles() {
|
|
4637
|
+
return await this.adapter.get("originprofiles") ?? [];
|
|
4638
|
+
}
|
|
4639
|
+
/** Upserts one origin profile: a profile of the same origin replaces its grants and denials while a new origin joins the list. */
|
|
4640
|
+
async saveoriginprofile(profile) {
|
|
4641
|
+
const profiles = await this.getoriginprofiles();
|
|
4642
|
+
await this.setoriginprofiles(profiles.some((candidate) => candidate.origin === profile.origin) ? profiles.map((candidate) => candidate.origin === profile.origin ? profile : candidate) : [...profiles, profile]);
|
|
4643
|
+
}
|
|
4644
|
+
/** Replaces the consent windows; active windows keep their expiry timestamps and closed windows stay for the audit trail. */
|
|
4645
|
+
async setconsentwindows(windows) {
|
|
4646
|
+
return this.adapter.set("consentwindows", windows);
|
|
4647
|
+
}
|
|
4648
|
+
/** Returns the stored consent windows, newest start first. */
|
|
4649
|
+
async getconsentwindows() {
|
|
4650
|
+
return await this.adapter.get("consentwindows") ?? [];
|
|
4651
|
+
}
|
|
4652
|
+
/** Expires every consent window past its duration boundary: the closed windows keep their records while their grants bind no step anymore. */
|
|
4653
|
+
async expireconsentwindows(now) {
|
|
4654
|
+
const windows = await this.getconsentwindows();
|
|
4655
|
+
const expired = windows.map((window2) => window2.state === "active" && now >= window2.expiresat ? { ...window2, state: "closed", closedat: now } : window2);
|
|
4656
|
+
await this.setconsentwindows(expired);
|
|
4657
|
+
return expired;
|
|
4658
|
+
}
|
|
4659
|
+
/** Records one mid run revocation with its halted step ids; the history stays visible for later consent prompts. */
|
|
4660
|
+
async addrevocation(event) {
|
|
4661
|
+
await this.adapter.set("revocations", [event, ...await this.adapter.get("revocations") ?? []].slice(0, 500));
|
|
4662
|
+
}
|
|
4663
|
+
/** Returns the recorded mid run revocations with their halted step ids, newest first. */
|
|
4664
|
+
async getrevocations() {
|
|
4665
|
+
return await this.adapter.get("revocations") ?? [];
|
|
4666
|
+
}
|
|
4667
|
+
/** Replaces the fresh class consents per origin. */
|
|
4668
|
+
async setclassconsents(consents) {
|
|
4669
|
+
return this.adapter.set("classconsents", consents);
|
|
4670
|
+
}
|
|
4671
|
+
/** Returns the fresh class consents per origin, newest grant first. */
|
|
4672
|
+
async getclassconsents() {
|
|
4673
|
+
return await this.adapter.get("classconsents") ?? [];
|
|
4674
|
+
}
|
|
4675
|
+
/** Records one fresh class consent per origin; the prompt of one class never widens another class. */
|
|
4676
|
+
async addclassconsent(consent) {
|
|
4677
|
+
const consents = (await this.getclassconsents()).filter((candidate) => !(candidate.origin === consent.origin && candidate.sensitiveclass === consent.sensitiveclass));
|
|
4678
|
+
await this.setclassconsents([consent, ...consents]);
|
|
4679
|
+
}
|
|
4680
|
+
/** Replaces the mask rules for sensitive field shapes per origin. */
|
|
4681
|
+
async setmaskrules(rules) {
|
|
4682
|
+
return this.adapter.set("maskrules", rules);
|
|
4683
|
+
}
|
|
4684
|
+
/** Returns the stored mask rules for sensitive field shapes per origin, oldest rule first. */
|
|
4685
|
+
async getmaskrules() {
|
|
4686
|
+
return await this.adapter.get("maskrules") ?? [];
|
|
4687
|
+
}
|
|
4688
|
+
/** Adds one mask rule for field shapes, optionally scoped to one origin. */
|
|
4689
|
+
async addmaskrule(rule) {
|
|
4690
|
+
await this.setmaskrules([...await this.getmaskrules(), rule]);
|
|
4691
|
+
}
|
|
4692
|
+
/** Removes one mask rule by its id. */
|
|
4693
|
+
async removemaskrule(id) {
|
|
4694
|
+
await this.setmaskrules((await this.getmaskrules()).filter((rule) => rule.id !== id));
|
|
4695
|
+
}
|
|
4696
|
+
/** Stores the whole run log of one run: the append lands in one storage transaction so the entries and their chain links persist together. */
|
|
4697
|
+
async setimmutablelog(log) {
|
|
4698
|
+
return this.adapter.set(`immutablelog:${log.runid}`, log);
|
|
4699
|
+
}
|
|
4700
|
+
/** Returns the stored run log of one run; an absent log returns undefined. */
|
|
4701
|
+
async getimmutablelog(runid) {
|
|
4702
|
+
return this.adapter.get(`immutablelog:${runid}`);
|
|
4703
|
+
}
|
|
4704
|
+
/** Lists the stored run logs, oldest update first, with the sealed logs carrying their final hash. */
|
|
4705
|
+
async listimmutablelogs() {
|
|
4706
|
+
const index = await this.adapter.get("immutablelogindex") ?? [];
|
|
4707
|
+
const logs = [];
|
|
4708
|
+
for (const runid of index) {
|
|
4709
|
+
const log = await this.getimmutablelog(runid);
|
|
4710
|
+
if (log) logs.push(log);
|
|
4711
|
+
}
|
|
4712
|
+
return logs.sort((one, two) => one.updatedat - two.updatedat);
|
|
4713
|
+
}
|
|
4714
|
+
/** Stores the run log index entry of one run so the log listing reads every stored log. */
|
|
4715
|
+
async trackimmutablelog(runid) {
|
|
4716
|
+
const index = await this.adapter.get("immutablelogindex") ?? [];
|
|
4717
|
+
if (!index.includes(runid)) await this.adapter.set("immutablelogindex", [...index, runid]);
|
|
4718
|
+
}
|
|
4719
|
+
/** Exports the verified log chain of one run for the audit file: the read path verifies the whole hash chain first and a broken link refuses the export with no entries served. */
|
|
4720
|
+
async exportverifiedrunlog(runid) {
|
|
4721
|
+
const log = await this.getimmutablelog(runid);
|
|
4722
|
+
if (!log) throw new Error(`No run log exists for the run ${runid}.`);
|
|
4723
|
+
return exportlogchain(log);
|
|
4724
|
+
}
|
|
4725
|
+
/** Expires the sealed run logs past the user configured retention: the entries reduce to their chain summaries while the seal hash always survives. */
|
|
4726
|
+
async expireimmutablelogs(retention, now) {
|
|
4727
|
+
const logs = await this.listimmutablelogs();
|
|
4728
|
+
if (retention === void 0) return logs;
|
|
4729
|
+
const kept = [];
|
|
4730
|
+
for (const log of logs) {
|
|
4731
|
+
if (log.seal !== void 0 && now - log.seal.sealedat > retention) {
|
|
4732
|
+
const summary = { runid: log.runid, sessionid: log.sessionid, entries: [], seal: { ...log.seal, entries: log.seal.entries }, updatedat: now };
|
|
4733
|
+
await this.setimmutablelog(summary);
|
|
4734
|
+
} else {
|
|
4735
|
+
kept.push(log);
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
return kept;
|
|
4739
|
+
}
|
|
4540
4740
|
};
|
|
4541
4741
|
function mediakindof(record2) {
|
|
4542
4742
|
if ("pages" in record2) return "pdf";
|
|
@@ -4580,6 +4780,130 @@ function randomid() {
|
|
|
4580
4780
|
return crypto.randomUUID();
|
|
4581
4781
|
}
|
|
4582
4782
|
|
|
4783
|
+
// originpolicy.ts
|
|
4784
|
+
function exactorigin(origin, entry) {
|
|
4785
|
+
return origin.trim() !== "" && origin === entry;
|
|
4786
|
+
}
|
|
4787
|
+
function wildcardentry(entry) {
|
|
4788
|
+
return entry.includes("*") || entry.includes("://*.") || entry.trim() === "" || entry.trim() === "https://" || entry.trim() === "http://";
|
|
4789
|
+
}
|
|
4790
|
+
function allowlistcheck(input) {
|
|
4791
|
+
if (input.origin.trim() === "") return { allowed: false, reason: "The step needs the exact origin it targets." };
|
|
4792
|
+
for (const entry of input.allowlist) {
|
|
4793
|
+
if (wildcardentry(entry.origin)) return { allowed: false, reason: `The allowlist entry ${entry.origin} carries a wildcard; every grant binds to one exact origin with no wildcard expansion.` };
|
|
4794
|
+
}
|
|
4795
|
+
const scoped = input.profileid === void 0 ? input.allowlist : input.allowlist.filter((entry) => entry.profileid === input.profileid);
|
|
4796
|
+
const granted = scoped.some((entry) => exactorigin(input.origin, entry.origin));
|
|
4797
|
+
if (granted) return { allowed: true, reason: `The origin ${input.origin} sits inside the automation allowlist the user granted.` };
|
|
4798
|
+
if (input.sessionorigin !== void 0 && exactorigin(input.origin, input.sessionorigin)) return { allowed: true, reason: `The active tab grant covers ${input.origin} as exactly one explicit single origin grant.` };
|
|
4799
|
+
return { allowed: false, reason: `The denydefault posture refuses ${input.origin} because the origin sits absent from the automation allowlist; grant the origin first.` };
|
|
4800
|
+
}
|
|
4801
|
+
function originprofileof(input) {
|
|
4802
|
+
if (input.origin.trim() === "") throw new Error("The origin profile needs its exact origin.");
|
|
4803
|
+
return { profileid: input.profileid ?? randomid(), origin: input.origin, grants: [...input.grants ?? []], denials: [...input.denials ?? []], createdat: input.now, updatedat: input.now };
|
|
4804
|
+
}
|
|
4805
|
+
function profilekind(input) {
|
|
4806
|
+
if (input.profile.grants.includes(input.kind) && input.decision === "grant") return input.profile;
|
|
4807
|
+
if (input.profile.denials.includes(input.kind) && input.decision === "deny") return input.profile;
|
|
4808
|
+
const grants = input.decision === "grant" ? [.../* @__PURE__ */ new Set([...input.profile.grants, input.kind])] : input.profile.grants.filter((kind) => kind !== input.kind);
|
|
4809
|
+
const denials = input.decision === "deny" ? [.../* @__PURE__ */ new Set([...input.profile.denials, input.kind])] : input.profile.denials.filter((kind) => kind !== input.kind);
|
|
4810
|
+
return { ...input.profile, grants, denials, updatedat: input.now };
|
|
4811
|
+
}
|
|
4812
|
+
function profilegrade(input) {
|
|
4813
|
+
if (!input.sensitive) return { allowed: true, consult: false, reason: `The ${input.kind} kind grades non-sensitive and the origin profile needs no consult.` };
|
|
4814
|
+
if (input.profile === void 0) return { allowed: true, consult: true, reason: `No origin profile exists for the ${input.kind} kind, so the fresh class consent gate alone routes the sensitive step.` };
|
|
4815
|
+
if (input.profile.denials.includes(input.kind)) return { allowed: false, consult: true, reason: `The origin profile of ${input.profile.origin} denies the ${input.kind} kind; a denied kind never runs on that origin.` };
|
|
4816
|
+
if (input.profile.grants.includes(input.kind)) return { allowed: true, consult: true, reason: `The origin profile of ${input.profile.origin} grants the ${input.kind} kind the user reviewed.` };
|
|
4817
|
+
return { allowed: true, consult: true, reason: `The origin profile of ${input.profile.origin} carries no ${input.kind} decision, so the fresh class consent gate alone routes the sensitive step.` };
|
|
4818
|
+
}
|
|
4819
|
+
function stepoptions(step) {
|
|
4820
|
+
if (!step.options) return {};
|
|
4821
|
+
try {
|
|
4822
|
+
const parsed = JSON.parse(step.options);
|
|
4823
|
+
return Boolean(parsed) && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
4824
|
+
} catch {
|
|
4825
|
+
return {};
|
|
4826
|
+
}
|
|
4827
|
+
}
|
|
4828
|
+
var paymentkinds = /* @__PURE__ */ new Set(["fillcard", "fillcode"]);
|
|
4829
|
+
var credentialkinds = /* @__PURE__ */ new Set(["consentpassword", "saveapikey", "handleauth", "authflow"]);
|
|
4830
|
+
var deletekinds = /* @__PURE__ */ new Set(["discardtab", "closepattern", "clearcookies", "removeattribute", "cleanupartifacts"]);
|
|
4831
|
+
var publishkinds = /* @__PURE__ */ new Set(["postform", "postfiles", "sendmessage", "submitform", "submitsearch", "writeclipboard"]);
|
|
4832
|
+
var defaultsensitivekinds = /* @__PURE__ */ new Set(["attachfile", "uploadfile", "uploadfiles", "downloadfile", "downloadimages", "batchdownload", "pausedownload", "resumedownload", "quarantinedownload", "evaluate"]);
|
|
4833
|
+
function sensitiveclassesof(step) {
|
|
4834
|
+
const options = stepoptions(step);
|
|
4835
|
+
const fields = Array.isArray(options.fields) ? options.fields.filter((item) => Boolean(item) && typeof item === "object") : [];
|
|
4836
|
+
const names = [...fields.map((field) => typeof field.name === "string" ? field.name : ""), typeof options.field === "string" ? options.field : "", typeof options.target === "string" ? options.target : ""].map((name) => name.toLowerCase());
|
|
4837
|
+
const carries = (shape) => names.some((name) => name.includes(shape));
|
|
4838
|
+
const classes = /* @__PURE__ */ new Set();
|
|
4839
|
+
if (paymentkinds.has(step.kind) || carries("card") || carries("cvc") || carries("cvv")) classes.add("payment");
|
|
4840
|
+
const credentialshape = carries("password") || carries("token") || carries("secret") || carries("apikey") || carries("passphrase");
|
|
4841
|
+
const submits = step.kind === "submitform" || step.kind === "postform" || step.kind === "submitsearch" || step.kind === "fillform" || step.kind === "filllabel" || step.kind === "fillplaceholder";
|
|
4842
|
+
if (credentialkinds.has(step.kind) || submits && credentialshape) classes.add("credential");
|
|
4843
|
+
if (deletekinds.has(step.kind)) classes.add("delete");
|
|
4844
|
+
if (publishkinds.has(step.kind) || step.kind === "callrest" || step.kind === "callgraphql") {
|
|
4845
|
+
const verb = typeof options.method === "string" ? options.method.trim().toUpperCase() : typeof options.verb === "string" ? options.verb.trim().toUpperCase() : "";
|
|
4846
|
+
if (step.kind === "callrest" || step.kind === "callgraphql") {
|
|
4847
|
+
if (verb !== "" && !["GET", "HEAD", "OPTIONS"].includes(verb)) classes.add("publish");
|
|
4848
|
+
} else classes.add("publish");
|
|
4849
|
+
}
|
|
4850
|
+
const bydefault = defaultsensitivekinds.has(step.kind);
|
|
4851
|
+
const list = [...classes];
|
|
4852
|
+
if (list.length === 0 && !bydefault) return { classes: [], bydefault: false, sensitive: false, reason: `The ${step.kind} kind carries no sensitive class and no default sensitive grade.` };
|
|
4853
|
+
return { classes: list, bydefault, sensitive: true, reason: `The ${step.kind} kind grades sensitive${list.length > 0 ? ` through the ${list.join(", ")} class${list.length === 1 ? "" : "es"}` : ""}${bydefault ? " by default" : ""}.` };
|
|
4854
|
+
}
|
|
4855
|
+
function classconsentcovers(consents, origin, sensitiveclass, now) {
|
|
4856
|
+
return consents.some((consent) => consent.origin === origin && consent.sensitiveclass === sensitiveclass && consent.grantedat <= now && (consent.expiresat === void 0 || now < consent.expiresat));
|
|
4857
|
+
}
|
|
4858
|
+
function missingclassconsents(input) {
|
|
4859
|
+
const missing = input.classes.filter((kind) => !classconsentcovers(input.consents, input.origin, kind, input.now));
|
|
4860
|
+
if (missing.length > 0) return { needed: true, missing, reason: `The sensitive classes ${missing.join(", ")} need one fresh consent prompt each on ${input.origin}.` };
|
|
4861
|
+
if (input.bydefault && input.classes.length === 0) return { needed: true, missing: [], reason: `The ${input.origin} step grades sensitive by default and needs its fresh consent window prompt.` };
|
|
4862
|
+
return { needed: false, missing: [], reason: `The fresh class consents of ${input.origin} cover every class the step names.` };
|
|
4863
|
+
}
|
|
4864
|
+
function openconsentwindow(input) {
|
|
4865
|
+
if (input.sessionid.trim() === "" || input.origin.trim() === "") throw new Error("The consent window needs its session and its exact origin.");
|
|
4866
|
+
if (!Number.isFinite(input.duration) || input.duration <= 0) throw new Error("The consent window needs its duration as a positive user value; no window defaults to unlimited.");
|
|
4867
|
+
return { id: input.id ?? randomid(), sessionid: input.sessionid, origin: input.origin, startedat: input.now, duration: input.duration, expiresat: input.now + input.duration, boundary: input.boundary?.trim() !== "" && input.boundary !== void 0 ? input.boundary : `${input.duration} milliseconds the user chose`, kinds: [...new Set(input.kinds)], state: "active" };
|
|
4868
|
+
}
|
|
4869
|
+
function consentwindowstate(window2, now) {
|
|
4870
|
+
if (window2.state === "closed" || now >= window2.expiresat) return { state: "expired", remaining: 0, reason: `The consent window of ${window2.origin} closed at its ${window2.boundary} boundary; the run suspends until a new explicit prompt renews it.` };
|
|
4871
|
+
return { state: "active", remaining: window2.expiresat - now, reason: `The consent window of ${window2.origin} stays active with ${window2.expiresat - now} milliseconds left of its ${window2.boundary} boundary.` };
|
|
4872
|
+
}
|
|
4873
|
+
function windowgatesstep(input) {
|
|
4874
|
+
if (input.window === void 0) return { allowed: false, suspended: false, reason: `No active consent window covers ${input.origin}; the consent prompt opens one before any step dispatches.` };
|
|
4875
|
+
if (input.window.sessionid !== input.sessionid) return { allowed: false, suspended: false, reason: `The consent window scopes to the session ${input.window.sessionid} only and never widens to another session.` };
|
|
4876
|
+
if (input.window.origin !== input.origin) return { allowed: false, suspended: false, reason: `The consent window scopes to the origin ${input.window.origin} only and never widens to another origin.` };
|
|
4877
|
+
const state = consentwindowstate(input.window, input.now);
|
|
4878
|
+
if (state.state === "expired") return { allowed: false, suspended: true, reason: state.reason };
|
|
4879
|
+
return { allowed: true, suspended: false, reason: state.reason };
|
|
4880
|
+
}
|
|
4881
|
+
function renewconsentwindow(input) {
|
|
4882
|
+
const closed = input.window.state === "active" ? { ...input.window, state: "closed", closedat: input.now } : input.window;
|
|
4883
|
+
const renewed = openconsentwindow({ sessionid: input.window.sessionid, origin: input.window.origin, duration: input.duration, kinds: input.kinds.length > 0 ? input.kinds : input.window.kinds, now: input.now });
|
|
4884
|
+
return { renewed, closed };
|
|
4885
|
+
}
|
|
4886
|
+
function revokerun(input) {
|
|
4887
|
+
if (input.sessionid.trim() === "" || input.runid.trim() === "") throw new Error("The revocation needs its session and run ids.");
|
|
4888
|
+
if (input.actor.trim() === "") throw new Error("The revocation names the acting user.");
|
|
4889
|
+
const halted = [...input.pendingstepid !== void 0 ? [input.pendingstepid] : [], ...input.queuedstepids ?? []];
|
|
4890
|
+
if (halted.length === 0) throw new Error("The revocation halts at least the pending step of the run.");
|
|
4891
|
+
return { id: input.id ?? randomid(), sessionid: input.sessionid, runid: input.runid, haltedstepids: halted, actor: input.actor, reason: input.reason?.trim() !== "" && input.reason !== void 0 ? input.reason : "The user revoked the consent mid run.", at: input.now };
|
|
4892
|
+
}
|
|
4893
|
+
function scopegrantof(input) {
|
|
4894
|
+
if (input.origin.trim() === "") throw new Error("The consent scope needs its exact origin.");
|
|
4895
|
+
if (input.kinds.length === 0) throw new Error("The consent scope names the kinds it covers.");
|
|
4896
|
+
if (input.boundary.trim() === "") throw new Error("The consent scope names its boundary; no grant defaults to unlimited.");
|
|
4897
|
+
return { origin: input.origin, kinds: [...new Set(input.kinds)], boundary: input.boundary, grantedat: input.now };
|
|
4898
|
+
}
|
|
4899
|
+
function deniedevidenceof(input) {
|
|
4900
|
+
return { origin: input.origin, kind: input.kind, reason: input.reason, at: input.now };
|
|
4901
|
+
}
|
|
4902
|
+
function consentprompttext(input) {
|
|
4903
|
+
const label = input.classes.length > 0 ? `the ${input.classes.join(" and ")} class${input.classes.length === 1 ? "" : "es"}` : "a sensitive by default grade";
|
|
4904
|
+
return `Allow the ${input.kind} step on ${input.origin} graded as ${label} for ${input.duration} milliseconds? The consent window closes at that boundary; no grant ever defaults to unlimited.`;
|
|
4905
|
+
}
|
|
4906
|
+
|
|
4583
4907
|
// environments.ts
|
|
4584
4908
|
var offloadfamilies = [
|
|
4585
4909
|
{ task: "htmlsnapshot", kinds: ["readhtml", "parsehtml", "readertree", "readoutline", "classifypage"] },
|
|
@@ -9563,6 +9887,42 @@ function sandboxorigingate(input) {
|
|
|
9563
9887
|
function environmentrequirements() {
|
|
9564
9888
|
return environmentrequirementsof([...allowedactions]);
|
|
9565
9889
|
}
|
|
9890
|
+
function automationallowlistgate(input) {
|
|
9891
|
+
const verdict = allowlistcheck({ origin: input.origin, allowlist: input.allowlist, ...input.session !== void 0 ? { sessionorigin: input.session.origin } : {} });
|
|
9892
|
+
if (!verdict.allowed) return { allowed: false, reason: verdict.reason };
|
|
9893
|
+
return { allowed: true, reason: verdict.reason };
|
|
9894
|
+
}
|
|
9895
|
+
function originprofilegate(input) {
|
|
9896
|
+
const verdict = profilegrade(input);
|
|
9897
|
+
if (!verdict.allowed) return { allowed: false, reason: verdict.reason };
|
|
9898
|
+
return { allowed: true, reason: verdict.reason };
|
|
9899
|
+
}
|
|
9900
|
+
function consentwindowgate(input) {
|
|
9901
|
+
if (!input.sensitive) return { allowed: true, reason: "The non-sensitive step rides the session origin grants and needs no consent window of its own." };
|
|
9902
|
+
const verdict = windowgatesstep({ window: input.window, sessionid: input.sessionid, origin: input.origin, now: input.now });
|
|
9903
|
+
if (!verdict.allowed) return { allowed: false, reason: verdict.reason };
|
|
9904
|
+
return { allowed: true, reason: verdict.reason };
|
|
9905
|
+
}
|
|
9906
|
+
function revokerungate(input) {
|
|
9907
|
+
if (input.revocation === void 0) return { allowed: true, reason: "No revocation halted the run; the steps keep their reviewed order." };
|
|
9908
|
+
if (input.revocation.sessionid !== input.sessionid) return { allowed: true, reason: "The revocation belongs to another session and halts nothing here." };
|
|
9909
|
+
if (input.revocation.runid !== input.runid) return { allowed: true, reason: "The revocation belongs to another run and halts nothing here." };
|
|
9910
|
+
return { allowed: false, reason: `The revocation of ${input.revocation.actor} halted the pending step and ${Math.max(0, input.revocation.haltedstepids.length - 1)} queued step${Math.max(0, input.revocation.haltedstepids.length - 1) === 1 ? "" : "s"} without executing them: ${input.revocation.haltedstepids.join(", ")}.` };
|
|
9911
|
+
}
|
|
9912
|
+
function sensitiveclassgate(input) {
|
|
9913
|
+
if (!input.sensitive) return { allowed: true, reason: "The step carries no sensitive class and needs no fresh consent prompt." };
|
|
9914
|
+
const verdict = missingclassconsents({ origin: input.origin, classes: input.classes, bydefault: input.bydefault, consents: input.consents, now: input.now });
|
|
9915
|
+
if (verdict.needed) return { allowed: false, reason: verdict.reason };
|
|
9916
|
+
return { allowed: true, reason: verdict.reason };
|
|
9917
|
+
}
|
|
9918
|
+
function consentdurationvalid(duration) {
|
|
9919
|
+
if (!Number.isFinite(duration) || duration <= 0) return { allowed: false, reason: "The consent window duration stays a positive user value in milliseconds; no grant ever defaults to unlimited." };
|
|
9920
|
+
return { allowed: true, reason: `The consent window duration ${duration} milliseconds stays the user configured boundary the prompt names.` };
|
|
9921
|
+
}
|
|
9922
|
+
function logreadgate(input) {
|
|
9923
|
+
if (!input.valid) return { allowed: false, reason: input.brokenat !== void 0 ? `The log chain breaks at entry ${input.brokenat}; the audit accessor refuses the read of a forged record.` : "The log chain fails its verification; the audit accessor refuses the read of a forged record." };
|
|
9924
|
+
return { allowed: true, reason: "The log chain verifies from the genesis hash to the last entry; the audit accessor serves the entries." };
|
|
9925
|
+
}
|
|
9566
9926
|
|
|
9567
9927
|
// progress.ts
|
|
9568
9928
|
function emptyprogress(planid, now) {
|
|
@@ -9748,9 +10108,68 @@ function recordtoolcall(progress, planid, stepid, entry, now) {
|
|
|
9748
10108
|
const outcome = { stepid, ok: entry.ok, summary: `The ${entry.tool} tool call of the client ${entry.clientid} ${entry.ok ? "ran behind the consent gates" : `was refused${entry.code !== void 0 ? ` with the ${entry.code} error` : ""}`}.`, details: { tool: entry }, at: now };
|
|
9749
10109
|
return recordoutcome(base, planid, outcome, now);
|
|
9750
10110
|
}
|
|
10111
|
+
function recorddenied(progress, planid, stepid, entry, now) {
|
|
10112
|
+
const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
|
|
10113
|
+
const outcome = { stepid, ok: false, summary: `The ${entry.kind} step on ${entry.origin} was denied: ${entry.reason}`, details: { denied: entry }, at: now };
|
|
10114
|
+
return recordoutcome(base, planid, outcome, now);
|
|
10115
|
+
}
|
|
10116
|
+
function recordrevocation(progress, planid, stepid, entry, now) {
|
|
10117
|
+
const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
|
|
10118
|
+
const outcome = { stepid, ok: false, summary: `The run halted at the step ${stepid}${entry.haltedstepids.length > 1 ? ` with ${entry.haltedstepids.length - 1} queued step${entry.haltedstepids.length === 2 ? "" : "s"} rolled back` : ""}: ${entry.reason}`, details: { revoked: entry }, at: now };
|
|
10119
|
+
return recordoutcome(base, planid, outcome, now);
|
|
10120
|
+
}
|
|
10121
|
+
|
|
10122
|
+
// maskinputs.ts
|
|
10123
|
+
var defaultmaskshapes = ["password", "token", "card", "secret"];
|
|
10124
|
+
var maskmarker = "[redacted]";
|
|
10125
|
+
function fieldshapekind(name) {
|
|
10126
|
+
const lowered = name.toLowerCase();
|
|
10127
|
+
if (lowered.includes("password") || lowered.includes("passwd") || lowered.includes("pwd") || lowered.includes("passphrase")) return "password";
|
|
10128
|
+
if (lowered.includes("token") || lowered.includes("apikey") || lowered.includes("api_key") || lowered.includes("auth") || lowered.includes("bearer")) return "token";
|
|
10129
|
+
if (lowered.includes("card") || lowered.includes("cvc") || lowered.includes("cvv") || lowered.includes("expiry") || lowered.includes("pan")) return "card";
|
|
10130
|
+
if (lowered.includes("secret")) return "secret";
|
|
10131
|
+
return void 0;
|
|
10132
|
+
}
|
|
10133
|
+
function shapesof(input) {
|
|
10134
|
+
const shapes = new Set(defaultmaskshapes);
|
|
10135
|
+
for (const shape of input.settings?.maskshapes ?? []) if (shape.trim() !== "") shapes.add(shape.trim().toLowerCase());
|
|
10136
|
+
for (const rule of input.rules) {
|
|
10137
|
+
const scoped = rule.origin === void 0 || rule.origin === "" || input.origin !== void 0 && rule.origin === input.origin;
|
|
10138
|
+
if (scoped) {
|
|
10139
|
+
for (const shape of rule.shapes) if (shape.trim() !== "") shapes.add(shape.trim().toLowerCase());
|
|
10140
|
+
}
|
|
10141
|
+
}
|
|
10142
|
+
return [...shapes];
|
|
10143
|
+
}
|
|
10144
|
+
function maskingfield(name, shapes) {
|
|
10145
|
+
if (fieldshapekind(name) !== void 0) return true;
|
|
10146
|
+
const lowered = name.toLowerCase();
|
|
10147
|
+
return shapes.some((shape) => shape !== "" && lowered.includes(shape));
|
|
10148
|
+
}
|
|
10149
|
+
function maskvalue(value) {
|
|
10150
|
+
return value === "" ? "" : maskmarker;
|
|
10151
|
+
}
|
|
10152
|
+
function maskfield(input) {
|
|
10153
|
+
return maskingfield(input.name, input.shapes) ? maskvalue(input.value) : input.value;
|
|
10154
|
+
}
|
|
10155
|
+
function maskrecord(record2, shapes) {
|
|
10156
|
+
const masked = {};
|
|
10157
|
+
for (const [key, value] of Object.entries(record2)) {
|
|
10158
|
+
if (typeof value === "string") {
|
|
10159
|
+
const sibling = record2.name;
|
|
10160
|
+
masked[key] = key === "value" && typeof sibling === "string" ? maskfield({ name: sibling, value, shapes }) : maskfield({ name: key, value, shapes });
|
|
10161
|
+
} else if (Array.isArray(value)) masked[key] = value.map((item) => Boolean(item) && typeof item === "object" && !Array.isArray(item) ? maskrecord(item, shapes) : item);
|
|
10162
|
+
else if (Boolean(value) && typeof value === "object") masked[key] = maskrecord(value, shapes);
|
|
10163
|
+
else masked[key] = value;
|
|
10164
|
+
}
|
|
10165
|
+
return masked;
|
|
10166
|
+
}
|
|
10167
|
+
function maskexport(record2, shapes) {
|
|
10168
|
+
return maskrecord(record2, shapes);
|
|
10169
|
+
}
|
|
9751
10170
|
|
|
9752
10171
|
// version.ts
|
|
9753
|
-
var packageversion = "1.1.
|
|
10172
|
+
var packageversion = "1.1.61";
|
|
9754
10173
|
|
|
9755
10174
|
// types.ts
|
|
9756
10175
|
var protocolversion = packageversion;
|
|
@@ -11227,7 +11646,7 @@ async function readcapabilities() {
|
|
|
11227
11646
|
]);
|
|
11228
11647
|
return { tabs, downloads, clipboardread, clipboardwrite, reportedat: Date.now() };
|
|
11229
11648
|
}
|
|
11230
|
-
function
|
|
11649
|
+
function stepoptions2(step) {
|
|
11231
11650
|
if (!step.options) return {};
|
|
11232
11651
|
try {
|
|
11233
11652
|
const parsed = JSON.parse(step.options);
|
|
@@ -11240,7 +11659,7 @@ function tabid(step) {
|
|
|
11240
11659
|
return Number.parseInt(step.value ?? "", 10);
|
|
11241
11660
|
}
|
|
11242
11661
|
async function runbrowseraction(step, sessiontabid, windowid) {
|
|
11243
|
-
const options =
|
|
11662
|
+
const options = stepoptions2(step);
|
|
11244
11663
|
switch (step.kind) {
|
|
11245
11664
|
case "tablist": {
|
|
11246
11665
|
const tabs = await chrome.tabs.query({});
|
|
@@ -13912,7 +14331,7 @@ function extensionpage(sender) {
|
|
|
13912
14331
|
async function audit(kind, summary, extra = {}) {
|
|
13913
14332
|
await memory.addaudi({ id: randomid(), kind, at: Date.now(), summary, ...extra });
|
|
13914
14333
|
}
|
|
13915
|
-
function
|
|
14334
|
+
function stepoptions3(step) {
|
|
13916
14335
|
try {
|
|
13917
14336
|
return parseoptions(step);
|
|
13918
14337
|
} catch {
|
|
@@ -14119,6 +14538,67 @@ async function closeplanrun(planid, sessionid) {
|
|
|
14119
14538
|
await closeoffscreendocument(planid);
|
|
14120
14539
|
await audit("environment", `The run state of the plan ${planid} closed at its terminal state and the keepalive port released.`, { ...sessionid !== "" ? { sessionid } : {}, planid });
|
|
14121
14540
|
}
|
|
14541
|
+
async function appendrunevent(kind, summary, session, origin, stepid) {
|
|
14542
|
+
if (!session) return;
|
|
14543
|
+
const now = Date.now();
|
|
14544
|
+
let log = await memory.getimmutablelog(session.id);
|
|
14545
|
+
if (!log) {
|
|
14546
|
+
log = openrunlog({ runid: session.id, sessionid: session.id, now });
|
|
14547
|
+
await memory.trackimmutablelog(session.id);
|
|
14548
|
+
}
|
|
14549
|
+
log = await appendlogentry({ log, kind, summary, origin, ...stepid !== void 0 ? { stepid } : {}, at: now });
|
|
14550
|
+
await memory.setimmutablelog(log);
|
|
14551
|
+
}
|
|
14552
|
+
async function securitystepgate(step, session, origin, settings) {
|
|
14553
|
+
const classification = sensitiveclassesof(step);
|
|
14554
|
+
if (!session) return { allowed: true, suspended: false, reason: "The step runs behind the session review chain; a sessionless preview never dispatches.", classification };
|
|
14555
|
+
const now = Date.now();
|
|
14556
|
+
const allowverdict = automationallowlistgate({ origin, allowlist: await memory.getautomationallowlist(), session });
|
|
14557
|
+
if (!allowverdict.allowed) return { allowed: false, suspended: false, reason: allowverdict.reason ?? "", classification };
|
|
14558
|
+
const profile = (await memory.getoriginprofiles()).find((candidate) => candidate.origin === origin);
|
|
14559
|
+
const profileverdict = originprofilegate({ profile, kind: step.kind, sensitive: classification.sensitive });
|
|
14560
|
+
if (!profileverdict.allowed) return { allowed: false, suspended: false, reason: profileverdict.reason ?? "", classification };
|
|
14561
|
+
const windows = await memory.expireconsentwindows(now);
|
|
14562
|
+
const window2 = windows.find((candidate) => candidate.state === "active" && candidate.sessionid === session.id && candidate.origin === origin);
|
|
14563
|
+
const windowverdict = consentwindowgate({ window: window2, sessionid: session.id, origin, sensitive: classification.sensitive, now });
|
|
14564
|
+
if (!windowverdict.allowed) return { allowed: false, suspended: windowverdict.reason?.includes("suspends") ?? false, reason: windowverdict.reason ?? "", classification };
|
|
14565
|
+
const consentverdict = sensitiveclassgate({ origin, classes: classification.classes, bydefault: classification.bydefault, sensitive: classification.sensitive, consents: await memory.getclassconsents(), now });
|
|
14566
|
+
if (!consentverdict.allowed) return { allowed: false, suspended: false, reason: `${classification.reason} ${consentverdict.reason ?? ""}`, classification };
|
|
14567
|
+
const plan = await memory.getplan();
|
|
14568
|
+
const revocation = plan === void 0 ? void 0 : (await memory.getrevocations()).find((candidate) => candidate.sessionid === session.id && candidate.runid === plan.id);
|
|
14569
|
+
const revokeverdict = revokerungate({ revocation, sessionid: session.id, runid: plan?.id ?? "" });
|
|
14570
|
+
if (!revokeverdict.allowed) return { allowed: false, suspended: false, reason: revokeverdict.reason ?? "", classification };
|
|
14571
|
+
return { allowed: true, suspended: false, reason: `${classification.reason} ${allowverdict.reason ?? ""} ${windowverdict.reason ?? ""} ${consentverdict.reason ?? ""}`, classification };
|
|
14572
|
+
}
|
|
14573
|
+
async function sealsessionrunlog(sessionid) {
|
|
14574
|
+
const log = await memory.getimmutablelog(sessionid);
|
|
14575
|
+
if (!log || log.seal !== void 0 || log.entries.length === 0) return;
|
|
14576
|
+
const sealed = await sealrunlog(log, Date.now());
|
|
14577
|
+
await memory.setimmutablelog(sealed.log);
|
|
14578
|
+
await audit("seal", `The immutable run log sealed at completion with ${sealed.log.entries.length} entries and the final hash ${sealed.seal.sealhash.current}.`, { sessionid });
|
|
14579
|
+
}
|
|
14580
|
+
async function securityviewof() {
|
|
14581
|
+
const now = Date.now();
|
|
14582
|
+
const settings = await memory.getsettings();
|
|
14583
|
+
const session = await memory.getsession();
|
|
14584
|
+
const logs = await memory.listimmutablelogs();
|
|
14585
|
+
const chain = [];
|
|
14586
|
+
for (const log of logs) chain.push(await chainreportof(log));
|
|
14587
|
+
return {
|
|
14588
|
+
allowlist: await memory.getautomationallowlist(),
|
|
14589
|
+
profiles: await memory.getoriginprofiles(),
|
|
14590
|
+
windows: await memory.expireconsentwindows(now),
|
|
14591
|
+
consents: await memory.getclassconsents(),
|
|
14592
|
+
revocations: await memory.getrevocations(),
|
|
14593
|
+
maskrules: await memory.getmaskrules(),
|
|
14594
|
+
chain,
|
|
14595
|
+
posture: "denydefault",
|
|
14596
|
+
...session ? { sessionorigin: session.origin } : {},
|
|
14597
|
+
...settings?.consentduration !== void 0 ? { promptduration: settings.consentduration } : {},
|
|
14598
|
+
...settings?.logretention !== void 0 ? { logretention: settings.logretention } : {},
|
|
14599
|
+
...settings?.maskshapes !== void 0 ? { maskshapes: settings.maskshapes } : {}
|
|
14600
|
+
};
|
|
14601
|
+
}
|
|
14122
14602
|
async function executeisolatedevaluate(step, tabid2, origin) {
|
|
14123
14603
|
const injection = isolatedinjection(step);
|
|
14124
14604
|
const result = await chrome.scripting.executeScript({ target: { tabId: tabid2 }, world: "ISOLATED", func: (code, args, expectedorigin) => {
|
|
@@ -14138,7 +14618,7 @@ async function executeisolatedevaluate(step, tabid2, origin) {
|
|
|
14138
14618
|
return result[0]?.result ?? { ok: false, summary: "The isolated world returned no result." };
|
|
14139
14619
|
}
|
|
14140
14620
|
async function executesandboxrender(step, session, plan, origin) {
|
|
14141
|
-
const options =
|
|
14621
|
+
const options = stepoptions3(step);
|
|
14142
14622
|
const markup = typeof options.markup === "string" ? options.markup : "";
|
|
14143
14623
|
const sourceorigin = typeof options.sourceorigin === "string" ? options.sourceorigin : origin;
|
|
14144
14624
|
const settings = await memory.getsettings();
|
|
@@ -14172,7 +14652,7 @@ async function offloadparsetoworker(step, output, session, plan, origin) {
|
|
|
14172
14652
|
const ready = await ensureoffscreendocument(runid);
|
|
14173
14653
|
if (!ready) return { output, turnaround: void 0 };
|
|
14174
14654
|
const payload = JSON.stringify({ summary: output?.summary ?? "", details: output?.details ?? {} });
|
|
14175
|
-
const request = workerrequestof({ id: randomid(), runid, stepid: step.id, kind: step.kind, payload, options:
|
|
14655
|
+
const request = workerrequestof({ id: randomid(), runid, stepid: step.id, kind: step.kind, payload, options: stepoptions3(step), sentat: Date.now() });
|
|
14176
14656
|
const started = Date.now();
|
|
14177
14657
|
let answer;
|
|
14178
14658
|
try {
|
|
@@ -14263,7 +14743,14 @@ async function startsession() {
|
|
|
14263
14743
|
const { tab, origin } = await activecontext();
|
|
14264
14744
|
const session = { id: randomid(), tabid: tab.id, origin, startedat: Date.now(), expiresat: Date.now() + sessionduration, grants: [origin] };
|
|
14265
14745
|
await memory.setsession(session);
|
|
14746
|
+
await memory.addallowlistorigin({ origin, profileid: runstateprofile, grantedat: session.startedat });
|
|
14747
|
+
const scope = scopegrantof({ origin, kinds: ["observe"], boundary: `the session expiry at ${new Date(session.expiresat).toISOString()}`, now: session.startedat });
|
|
14748
|
+
let runlog = openrunlog({ runid: session.id, sessionid: session.id, now: session.startedat });
|
|
14749
|
+
runlog = await appendlogentry({ log: runlog, kind: "grant", summary: `The session started for ${origin} with the consentscope grant: the origin ${scope.origin}, the ${scope.kinds.join(", ")} kinds of the observation baseline and the boundary ${scope.boundary}; the active tab counts as exactly one explicit single origin grant.`, origin, at: session.startedat });
|
|
14750
|
+
await memory.trackimmutablelog(session.id);
|
|
14751
|
+
await memory.setimmutablelog(runlog);
|
|
14266
14752
|
await audit("session", `Session started for ${origin}.`, { sessionid: session.id });
|
|
14753
|
+
await audit("grant", `The consentscope grant of ${origin} was written into the immutable log with the boundary ${scope.boundary}.`, { sessionid: session.id });
|
|
14267
14754
|
const policy = await memory.getdialogpolicy();
|
|
14268
14755
|
if (policy) {
|
|
14269
14756
|
try {
|
|
@@ -14368,7 +14855,7 @@ function stepauditkind(step, ok) {
|
|
|
14368
14855
|
return ok ? "action" : "error";
|
|
14369
14856
|
}
|
|
14370
14857
|
function resolvedinnerstep(step, plan) {
|
|
14371
|
-
const options =
|
|
14858
|
+
const options = stepoptions3(step);
|
|
14372
14859
|
if (typeof options.stepid === "string" && options.stepid.trim()) {
|
|
14373
14860
|
return plan.steps.find((candidate) => candidate.id === options.stepid) ?? null;
|
|
14374
14861
|
}
|
|
@@ -14377,7 +14864,7 @@ function resolvedinnerstep(step, plan) {
|
|
|
14377
14864
|
async function executekeyhold(step, session, plan, tabid2, origin) {
|
|
14378
14865
|
const output = await dispatchpagestep(step, tabid2, origin, plan);
|
|
14379
14866
|
if (!output?.ok) return output ?? { ok: false, summary: "The key hold was not delivered." };
|
|
14380
|
-
const options =
|
|
14867
|
+
const options = stepoptions3(step);
|
|
14381
14868
|
const holdid = typeof options.holdid === "string" && options.holdid.trim() ? options.holdid : randomid();
|
|
14382
14869
|
const modifiers = Array.isArray(options.modifiers) ? options.modifiers.filter((item) => typeof item === "string") : [];
|
|
14383
14870
|
const hold = { holdid, key: step.value ?? "", ...modifiers.length > 0 ? { modifiers } : {}, tabid: tabid2, stepid: step.id, pressedat: Date.now() };
|
|
@@ -14410,7 +14897,7 @@ async function executedismissdialog(step, session, plan, tabid2, origin) {
|
|
|
14410
14897
|
return { ok: true, summary: `Dialog handler armed${answer} for the next confirm, alert or prompt.` };
|
|
14411
14898
|
}
|
|
14412
14899
|
async function executeretryaction(step, session, plan, tabid2, origin) {
|
|
14413
|
-
const rule =
|
|
14900
|
+
const rule = stepoptions3(step).retryrule;
|
|
14414
14901
|
const inner = resolvedinnerstep(step, plan);
|
|
14415
14902
|
if (!inner) throw new Error("The reviewed wrapper step could not be resolved.");
|
|
14416
14903
|
const innergate = validatestep(inner, origin);
|
|
@@ -14446,8 +14933,8 @@ async function executeenterframe(step, plan, tabid2, origin) {
|
|
|
14446
14933
|
if (!inner) throw new Error("The reviewed frame wrapper step could not be resolved.");
|
|
14447
14934
|
const innergate = validatestep(inner, origin);
|
|
14448
14935
|
if (!innergate.allowed) throw new Error(`The wrapped step is not allowed: ${innergate.reason}`);
|
|
14449
|
-
const options =
|
|
14450
|
-
const inneroptions = inner.options ?
|
|
14936
|
+
const options = stepoptions3(step);
|
|
14937
|
+
const inneroptions = inner.options ? stepoptions3(inner) : void 0;
|
|
14451
14938
|
const derived = { ...step, options: JSON.stringify({ ...options, kind: inner.kind, ...inner.target ? { target: inner.target } : {}, ...inner.value ? { value: inner.value } : {}, ...inneroptions ? { options: inneroptions } : {} }) };
|
|
14452
14939
|
return dispatchpagestep(derived, tabid2, origin, plan);
|
|
14453
14940
|
}
|
|
@@ -14459,7 +14946,7 @@ function detailarray(details, key) {
|
|
|
14459
14946
|
return Array.isArray(value) ? value : [];
|
|
14460
14947
|
}
|
|
14461
14948
|
async function executediffsnapshots(step, session, plan, tabid2, origin) {
|
|
14462
|
-
const options =
|
|
14949
|
+
const options = stepoptions3(step);
|
|
14463
14950
|
const versions = Array.isArray(options.versions) ? options.versions.filter((item) => typeof item === "number") : [];
|
|
14464
14951
|
const baseversion = versions[0];
|
|
14465
14952
|
const targetversion = versions[1];
|
|
@@ -14482,7 +14969,7 @@ async function executediffsnapshots(step, session, plan, tabid2, origin) {
|
|
|
14482
14969
|
return { ok: Boolean(output.ok), summary: output.summary, details: { ...output.details ?? {}, versions: [baseversion, targetversion] } };
|
|
14483
14970
|
}
|
|
14484
14971
|
async function executewatchstep(step, session, plan, tabid2, origin) {
|
|
14485
|
-
const options =
|
|
14972
|
+
const options = stepoptions3(step);
|
|
14486
14973
|
const watchid = typeof options.watchid === "string" && options.watchid.trim() ? options.watchid : randomid();
|
|
14487
14974
|
const scopes = Array.isArray(options.scopes) ? options.scopes.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
14488
14975
|
const events = Array.isArray(options.events) ? options.events.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
@@ -15164,7 +15651,7 @@ function commandtabids(step, options) {
|
|
|
15164
15651
|
return listed.length > 0 ? listed : single;
|
|
15165
15652
|
}
|
|
15166
15653
|
async function executetabscommand(step, session, plan, sessiontabid) {
|
|
15167
|
-
const options =
|
|
15654
|
+
const options = stepoptions3(step);
|
|
15168
15655
|
const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
|
|
15169
15656
|
const windowid = step.value && /^\d+$/.test(step.value) ? Number.parseInt(step.value, 10) : 0;
|
|
15170
15657
|
const layoutgate = layoutmutationgranted(session, Date.now());
|
|
@@ -15440,7 +15927,7 @@ async function executetabscommand(step, session, plan, sessiontabid) {
|
|
|
15440
15927
|
}
|
|
15441
15928
|
}
|
|
15442
15929
|
async function executesaveprofiles(step, session, origin) {
|
|
15443
|
-
const options =
|
|
15930
|
+
const options = stepoptions3(step);
|
|
15444
15931
|
const record2 = parseformrecord(options.formrecord);
|
|
15445
15932
|
const name = typeof options.name === "string" ? options.name : "";
|
|
15446
15933
|
if (!name || !record2) throw new Error("A reviewed profile name and form record are required.");
|
|
@@ -15460,7 +15947,7 @@ async function executeasksubmit(step, session, plan, tabid2, origin) {
|
|
|
15460
15947
|
return { ok: true, summary: `Asksubmit prompt opened for form ${ticket.form || "the reviewed form"}; the submission waits for your approval.`, details: { ticket, values } };
|
|
15461
15948
|
}
|
|
15462
15949
|
async function executesubmitform(step, session, plan, tabid2, origin) {
|
|
15463
|
-
const consentref = typeof
|
|
15950
|
+
const consentref = typeof stepoptions3(step).consentref === "string" ? stepoptions3(step).consentref : "";
|
|
15464
15951
|
const ticket = (await memory.gettickets()).find((item) => item.approved === true && (item.consentref === consentref || item.id === consentref));
|
|
15465
15952
|
if (!ticket) throw new Error("No approved asksubmit ticket matches the reviewed consent ref; approve the submission in the review panel first.");
|
|
15466
15953
|
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The form submission returned no result." };
|
|
@@ -15484,7 +15971,7 @@ async function executeretryform(step, session, plan, tabid2, origin) {
|
|
|
15484
15971
|
return { ok: Boolean(output?.ok), summary: output?.summary ?? "The retried submission returned no result.", details: { attempts, windows, ok: Boolean(output?.ok) } };
|
|
15485
15972
|
}
|
|
15486
15973
|
async function executeconsentpassword(step, session, plan, tabid2, origin) {
|
|
15487
|
-
const consentref = typeof
|
|
15974
|
+
const consentref = typeof stepoptions3(step).consentref === "string" ? stepoptions3(step).consentref : "";
|
|
15488
15975
|
const gate = passwordconsentgranted(step);
|
|
15489
15976
|
if (!gate.allowed) throw new Error(gate.reason ?? "A password fill requires a reviewed consent ref.");
|
|
15490
15977
|
const output = await dispatchpagestep(step, tabid2, origin, plan);
|
|
@@ -15496,7 +15983,7 @@ async function executeattachfile(step, session, plan, tabid2, origin) {
|
|
|
15496
15983
|
const artifacts = await memory.getartifacts();
|
|
15497
15984
|
const artifact = artifacts.find((item) => item.name === name || item.id === name);
|
|
15498
15985
|
if (!artifact) throw new Error(`No generated artifact named ${name} exists in the run store yet.`);
|
|
15499
|
-
const derived = { ...step, options: JSON.stringify({ ...
|
|
15986
|
+
const derived = { ...step, options: JSON.stringify({ ...stepoptions3(step), artifact: artifact.id, artifactname: artifact.name }) };
|
|
15500
15987
|
const output = await dispatchpagestep(derived, tabid2, origin, plan);
|
|
15501
15988
|
await audit("fill", `Artifact ${artifact.name} of kind ${artifact.kind} attached to the reviewed file input inside the form submission.`, { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id });
|
|
15502
15989
|
return { ...output ?? { ok: false, summary: "The artifact attachment returned no result." }, details: { ...output?.details ?? {}, artifact } };
|
|
@@ -15529,7 +16016,7 @@ async function executeformstep(step, session, plan, tabid2, origin) {
|
|
|
15529
16016
|
return executecaptchahandoff(step, session, plan, tabid2, origin);
|
|
15530
16017
|
case "fillcode": {
|
|
15531
16018
|
const stored = await memory.getcodevalue();
|
|
15532
|
-
const source = typeof
|
|
16019
|
+
const source = typeof stepoptions3(step).source === "string" ? stepoptions3(step).source : "";
|
|
15533
16020
|
const derived = stored !== void 0 && source === "reviewed" ? { ...step, value: stored } : step;
|
|
15534
16021
|
const output = await dispatchpagestep(derived, tabid2, origin, plan);
|
|
15535
16022
|
await audit("fill", `One time code typed from the reviewed source ${source}${stored !== void 0 ? " through the consent gated code entry" : ""}.`, { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id });
|
|
@@ -15600,7 +16087,7 @@ async function storeexport(stepid, datasetvalue, format, delimiter, session, pla
|
|
|
15600
16087
|
return artifact;
|
|
15601
16088
|
}
|
|
15602
16089
|
async function executedatastep(step, session, plan, tabid2, origin) {
|
|
15603
|
-
const options =
|
|
16090
|
+
const options = stepoptions3(step);
|
|
15604
16091
|
const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
|
|
15605
16092
|
switch (step.kind) {
|
|
15606
16093
|
case "scrapetable": {
|
|
@@ -15830,7 +16317,7 @@ async function verifyonerecord(record2, expected, extra) {
|
|
|
15830
16317
|
return { ok: verification.ok, summary: verification.summary, details: { verification: { ...verification.matches, state: record2.state, path: record2.path, checksum: record2.checksum, bytes: record2.bytes } } };
|
|
15831
16318
|
}
|
|
15832
16319
|
async function executefilesstep(step, session, plan, tabid2, origin) {
|
|
15833
|
-
const options =
|
|
16320
|
+
const options = stepoptions3(step);
|
|
15834
16321
|
const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
|
|
15835
16322
|
switch (step.kind) {
|
|
15836
16323
|
case "batchdownload": {
|
|
@@ -16059,7 +16546,7 @@ reconcilmimefilter().catch(() => {
|
|
|
16059
16546
|
});
|
|
16060
16547
|
var stitchprogress = /* @__PURE__ */ new Map();
|
|
16061
16548
|
function stepcaptureoptions(step) {
|
|
16062
|
-
return captureoptionsof(
|
|
16549
|
+
return captureoptionsof(stepoptions3(step).capture);
|
|
16063
16550
|
}
|
|
16064
16551
|
async function blobtodataurl(blob) {
|
|
16065
16552
|
const buffer = new Uint8Array(await blob.arrayBuffer());
|
|
@@ -16183,7 +16670,7 @@ async function encodecanvas(width, height, draw, options) {
|
|
|
16183
16670
|
return canvasdataurl(canvas, options.format, options.quality);
|
|
16184
16671
|
}
|
|
16185
16672
|
async function capturenamefor(step, plan, kind, format) {
|
|
16186
|
-
const naming =
|
|
16673
|
+
const naming = stepoptions3(step).naming;
|
|
16187
16674
|
const rule = naming && typeof naming === "object" && !Array.isArray(naming) ? naming : { run: true, step: true, sequence: true, kind: true };
|
|
16188
16675
|
const counters = (await memory.getcapturecounters()).find((item) => item.taskid === plan.id);
|
|
16189
16676
|
const advanced = advancecounter(counters?.counters ?? {}, step.id);
|
|
@@ -16252,7 +16739,7 @@ async function executecapturestep(step, session, plan, tabid2, origin) {
|
|
|
16252
16739
|
return { ok: true, summary: `Captured the visible viewport at ${record2.width} by ${record2.height} pixels.`, details: { capture: { id: record2.id, kind: record2.kind, format: record2.format, width: record2.width, height: record2.height, bytes: (record2.bytes ?? "").length, exporttarget: stored.routed.target } } };
|
|
16253
16740
|
}
|
|
16254
16741
|
if (step.kind === "shotfullpage") {
|
|
16255
|
-
const rawoptions =
|
|
16742
|
+
const rawoptions = stepoptions3(step);
|
|
16256
16743
|
const settle2 = typeof rawoptions.settle === "number" ? rawoptions.settle : 150;
|
|
16257
16744
|
const overlap = typeof rawoptions.overlap === "number" ? rawoptions.overlap : 0;
|
|
16258
16745
|
const wait = typeof rawoptions.wait === "number" ? rawoptions.wait : void 0;
|
|
@@ -16289,7 +16776,7 @@ async function executecapturestep(step, session, plan, tabid2, origin) {
|
|
|
16289
16776
|
}
|
|
16290
16777
|
if (step.kind === "shotelement") {
|
|
16291
16778
|
const selector = step.target ?? "";
|
|
16292
|
-
const settle2 = typeof
|
|
16779
|
+
const settle2 = typeof stepoptions3(step).settle === "number" ? stepoptions3(step).settle : 150;
|
|
16293
16780
|
const measured = await bridgecall(tabid2, "measurepage");
|
|
16294
16781
|
const targetinfo = await bridgecall(tabid2, "elementrect", selector);
|
|
16295
16782
|
if (!targetinfo.ok || !targetinfo.rect) throw new Error(targetinfo.summary);
|
|
@@ -16330,7 +16817,7 @@ async function executecapturestep(step, session, plan, tabid2, origin) {
|
|
|
16330
16817
|
}
|
|
16331
16818
|
}
|
|
16332
16819
|
if (step.kind === "shotregion") {
|
|
16333
|
-
const rawoptions =
|
|
16820
|
+
const rawoptions = stepoptions3(step);
|
|
16334
16821
|
const rect = rawoptions.regionrect;
|
|
16335
16822
|
if (!rect) throw new Error("A reviewed regionrect is required in options.");
|
|
16336
16823
|
const container = typeof rawoptions.container === "string" ? rawoptions.container : void 0;
|
|
@@ -16371,7 +16858,7 @@ async function executecapturestep(step, session, plan, tabid2, origin) {
|
|
|
16371
16858
|
}
|
|
16372
16859
|
}
|
|
16373
16860
|
if (step.kind === "contactsheet") {
|
|
16374
|
-
const rawoptions =
|
|
16861
|
+
const rawoptions = stepoptions3(step);
|
|
16375
16862
|
const elements = (Array.isArray(rawoptions.elements) ? rawoptions.elements : []).filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
16376
16863
|
const layout = rawoptions.sheet && typeof rawoptions.sheet === "object" && !Array.isArray(rawoptions.sheet) ? rawoptions.sheet : { cellsize: 240, columns: 3, label: "both" };
|
|
16377
16864
|
const measured = await bridgecall(tabid2, "measurepage");
|
|
@@ -16482,7 +16969,7 @@ async function thumbonecapture(source, directive, plan, step) {
|
|
|
16482
16969
|
return { id: randomid(), runid: plan.id, stepid: step.id, kind: "makethumbs", format, width: geometry.width, height: geometry.height, capturedat: Date.now(), bytes: dataurl, name, target: source.id };
|
|
16483
16970
|
}
|
|
16484
16971
|
async function executemediastep(step, session, plan, tabid2, origin) {
|
|
16485
|
-
const options =
|
|
16972
|
+
const options = stepoptions3(step);
|
|
16486
16973
|
const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
|
|
16487
16974
|
const gate = mediagate(session, tabid2, origin, Date.now());
|
|
16488
16975
|
if (!gate.allowed) throw new Error(gate.reason ?? "The media capture needs the active session tab grant.");
|
|
@@ -16741,7 +17228,7 @@ async function attachapikeys(names, origin) {
|
|
|
16741
17228
|
return { headers, keys: attached };
|
|
16742
17229
|
}
|
|
16743
17230
|
async function executehttpstep(step, session, plan, tabid2, origin) {
|
|
16744
|
-
const options =
|
|
17231
|
+
const options = stepoptions3(step);
|
|
16745
17232
|
const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
|
|
16746
17233
|
if (step.kind === "fetchurl") {
|
|
16747
17234
|
const request = fetchrequestof(options.fetch);
|
|
@@ -17006,7 +17493,7 @@ async function closechannelsforrun(runid) {
|
|
|
17006
17493
|
channelbuses.clear();
|
|
17007
17494
|
}
|
|
17008
17495
|
async function executesocketstep(step, session, plan, tabid2, origin) {
|
|
17009
|
-
const options =
|
|
17496
|
+
const options = stepoptions3(step);
|
|
17010
17497
|
const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
|
|
17011
17498
|
if (step.kind === "opensocket") {
|
|
17012
17499
|
const channel = channeloptionsof(options.socket);
|
|
@@ -17140,7 +17627,7 @@ async function executesocketstep(step, session, plan, tabid2, origin) {
|
|
|
17140
17627
|
throw new Error("Unsupported socket observation kind.");
|
|
17141
17628
|
}
|
|
17142
17629
|
async function executenetwatchstep(step, session, plan, tabid2, origin) {
|
|
17143
|
-
const options =
|
|
17630
|
+
const options = stepoptions3(step);
|
|
17144
17631
|
const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
|
|
17145
17632
|
if (step.kind === "watchrequests") {
|
|
17146
17633
|
const watch = options.watch && typeof options.watch === "object" && !Array.isArray(options.watch) ? options.watch : {};
|
|
@@ -17292,7 +17779,7 @@ function timelinedetail(entry, runid) {
|
|
|
17292
17779
|
return { id: randomid(), runid, stepid: record2.stepid, time: record2.time, level: record2.level, source: record2.source, message: record2.message };
|
|
17293
17780
|
}
|
|
17294
17781
|
async function executetimelinestep(step, session, plan, tabid2, origin) {
|
|
17295
|
-
const options =
|
|
17782
|
+
const options = stepoptions3(step);
|
|
17296
17783
|
const watch = options.watch && typeof options.watch === "object" && !Array.isArray(options.watch) ? options.watch : {};
|
|
17297
17784
|
const watchwindow = typeof watch.window === "number" && Number.isFinite(watch.window) && watch.window >= 0 ? watch.window : 0;
|
|
17298
17785
|
const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
|
|
@@ -17447,7 +17934,7 @@ function pauseframes(entry) {
|
|
|
17447
17934
|
});
|
|
17448
17935
|
}
|
|
17449
17936
|
async function executecdpstep(step, session, plan, tabid2, origin) {
|
|
17450
|
-
const options =
|
|
17937
|
+
const options = stepoptions3(step);
|
|
17451
17938
|
const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
|
|
17452
17939
|
const debuggatecheck = debuggate(session, tabid2, origin, Date.now());
|
|
17453
17940
|
if (!debuggatecheck.allowed) throw new Error(debuggatecheck.reason ?? "The devtools protocol step stays outside the debug gate.");
|
|
@@ -17681,7 +18168,7 @@ async function sampleheapforstep(tracker, stepid, tabid2, origin, plan) {
|
|
|
17681
18168
|
if (trend.flaggedsteps.includes(stepid)) await memory.addtimelineentry({ id: randomid(), runid: tracker.runid, stepid, time: now, level: "warn", source: "longtask", message: `Heap growth of step ${stepid} exceeds the reviewed slope of ${tracker.slope} bytes per millisecond; the step is flagged in the memory trend.` });
|
|
17682
18169
|
}
|
|
17683
18170
|
async function executeprofilestep(step, session, plan, tabid2, origin) {
|
|
17684
|
-
const options =
|
|
18171
|
+
const options = stepoptions3(step);
|
|
17685
18172
|
const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
|
|
17686
18173
|
const targets = profiletargetsof(options);
|
|
17687
18174
|
const grants = await memory.getdebuggergrants();
|
|
@@ -18010,7 +18497,7 @@ async function controlledfetch(runid, url, init, controller, window2, streamstat
|
|
|
18010
18497
|
return response;
|
|
18011
18498
|
}
|
|
18012
18499
|
async function executenetcontrolstep(step, session, plan, tabid2, origin) {
|
|
18013
|
-
const options =
|
|
18500
|
+
const options = stepoptions3(step);
|
|
18014
18501
|
const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
|
|
18015
18502
|
const ruleset = rulesetof(plan.id);
|
|
18016
18503
|
if (step.kind === "blockrequest") {
|
|
@@ -18257,7 +18744,7 @@ async function enforcewindowreview(step, session, plan) {
|
|
|
18257
18744
|
const progress = plan ? await memory.getprogress() : void 0;
|
|
18258
18745
|
const tasktabids = plan ? trackedtasktabs(progress, plan.id) : [];
|
|
18259
18746
|
const count = tasktabsinwindow(await livetabs(), windowid, tasktabids);
|
|
18260
|
-
const gate = windowclosegate(count,
|
|
18747
|
+
const gate = windowclosegate(count, stepoptions3(step).reviewed === true);
|
|
18261
18748
|
if (!gate.allowed) throw new Error(gate.reason ?? "The window close needs explicit review.");
|
|
18262
18749
|
if (session && count > 0) await audit("window", `Window ${windowid} closes while holding ${count} task tab${count === 1 ? "" : "s"} under the explicit reviewed flag.`, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {}, stepid: step.id });
|
|
18263
18750
|
}
|
|
@@ -18343,7 +18830,7 @@ async function revertemulationforrun(runid, reason, tabid2) {
|
|
|
18343
18830
|
}
|
|
18344
18831
|
}
|
|
18345
18832
|
async function executeemulationstep(step, session, plan, tabid2, origin) {
|
|
18346
|
-
const options =
|
|
18833
|
+
const options = stepoptions3(step);
|
|
18347
18834
|
const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
|
|
18348
18835
|
const revertplan = revertplanof(options.revertplan) ?? [];
|
|
18349
18836
|
const family = familyofkind(step.kind) ?? "device";
|
|
@@ -18475,7 +18962,7 @@ async function performrestore(record2, restore, session) {
|
|
|
18475
18962
|
return { restored, skippedorigins: grantscheck.skippedorigins };
|
|
18476
18963
|
}
|
|
18477
18964
|
async function executesessionstep(step, session, plan, tabid2, origin) {
|
|
18478
|
-
const options =
|
|
18965
|
+
const options = stepoptions3(step);
|
|
18479
18966
|
const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
|
|
18480
18967
|
if (step.kind === "persiststate") {
|
|
18481
18968
|
const progress = await memory.getprogress();
|
|
@@ -18620,7 +19107,7 @@ async function dispatchworkflowstep(step, context) {
|
|
|
18620
19107
|
return { ok: Boolean(output.ok), summary: output.summary, ...output.details !== void 0 ? { details: output.details } : {} };
|
|
18621
19108
|
}
|
|
18622
19109
|
async function executedelaystep(step) {
|
|
18623
|
-
const options =
|
|
19110
|
+
const options = stepoptions3({ id: step.id, kind: step.kind, summary: step.label, risk: "read", ...step.options !== void 0 ? { options: step.options } : {} });
|
|
18624
19111
|
const delay = delayof(options.delay);
|
|
18625
19112
|
const sampled = delayjitter(delay, hashseed(`${step.id}:${Date.now()}`));
|
|
18626
19113
|
const transport = await sleepreviewed(sampled, step.id);
|
|
@@ -18667,7 +19154,7 @@ async function sleepreviewed(sampled, stepid) {
|
|
|
18667
19154
|
return "timer";
|
|
18668
19155
|
}
|
|
18669
19156
|
async function executewaitelement(step, tabid2) {
|
|
18670
|
-
const options =
|
|
19157
|
+
const options = stepoptions3({ id: step.id, kind: step.kind, summary: step.label, risk: "read", ...step.target !== void 0 ? { target: step.target } : {}, ...step.options !== void 0 ? { options: step.options } : {} });
|
|
18671
19158
|
const wait = waitof(options.wait, step.target);
|
|
18672
19159
|
const startedat = Date.now();
|
|
18673
19160
|
const starttab = await chrome.tabs.get(tabid2).catch(() => void 0);
|
|
@@ -18695,7 +19182,7 @@ function waitof(value, target) {
|
|
|
18695
19182
|
return { selector, timeout, poll };
|
|
18696
19183
|
}
|
|
18697
19184
|
async function executecomputestep(step, session) {
|
|
18698
|
-
const options =
|
|
19185
|
+
const options = stepoptions3({ id: step.id, kind: step.kind, summary: step.label, risk: "read", ...step.options !== void 0 ? { options: step.options } : {} });
|
|
18699
19186
|
const expression = options.expression;
|
|
18700
19187
|
if (!expression || typeof expression !== "object") throw new Error("The compute step needs a reviewed expression.");
|
|
18701
19188
|
const scopes = runscopes(options.variables);
|
|
@@ -18704,7 +19191,7 @@ async function executecomputestep(step, session) {
|
|
|
18704
19191
|
return { ok: true, summary: `Computed ${expression.result} = ${typeof value === "string" ? `"${value}"` : String(value)} through the ${expression.operator} operator.`, details: { result: expression.result, kind: expression.resultkind, value } };
|
|
18705
19192
|
}
|
|
18706
19193
|
async function executeextractvarsstep(step, session) {
|
|
18707
|
-
const options =
|
|
19194
|
+
const options = stepoptions3({ id: step.id, kind: step.kind, summary: step.label, risk: "read", ...step.value !== void 0 ? { value: step.value } : {}, ...step.options !== void 0 ? { options: step.options } : {} });
|
|
18708
19195
|
const rule = options.rule;
|
|
18709
19196
|
if (!rule || typeof rule !== "object" || typeof rule.pattern !== "string") throw new Error("The variable extraction needs a reviewed regex rule.");
|
|
18710
19197
|
const text2 = typeof options.text === "string" ? options.text : step.value ?? "";
|
|
@@ -18717,7 +19204,7 @@ async function executeextractvarsstep(step, session) {
|
|
|
18717
19204
|
return { ok: true, summary: `Captured ${extraction.variables.length} variable${extraction.variables.length === 1 ? "" : "s"} from the reviewed text.`, details: { matched: true, variables: extraction.variables } };
|
|
18718
19205
|
}
|
|
18719
19206
|
async function executeworkflowstep(step, session, plan, tabid2, origin) {
|
|
18720
|
-
const options =
|
|
19207
|
+
const options = stepoptions3(step);
|
|
18721
19208
|
if (step.kind === "composeworkflow") {
|
|
18722
19209
|
const payload = options.workflow;
|
|
18723
19210
|
if (!payload || typeof payload !== "object" || Array.isArray(payload)) throw new Error("The workflow composition needs the reviewed workflow payload.");
|
|
@@ -18773,7 +19260,7 @@ function workflowstepofentry(value) {
|
|
|
18773
19260
|
return blockinvocationof(value);
|
|
18774
19261
|
}
|
|
18775
19262
|
async function executeworkflowrun(step, session, plan, tabid2, origin, dry) {
|
|
18776
|
-
const options =
|
|
19263
|
+
const options = stepoptions3(step);
|
|
18777
19264
|
const workflowid = typeof options.workflowid === "string" ? options.workflowid : "";
|
|
18778
19265
|
const storedrecord = await memory.getworkflowrecord(workflowid);
|
|
18779
19266
|
if (!storedrecord) throw new Error(`No composed workflow matches ${workflowid || "the reviewed id"}.`);
|
|
@@ -18936,7 +19423,7 @@ async function storetimeoutabort(run, step, message, budget) {
|
|
|
18936
19423
|
return { run: aborted, log: [entry] };
|
|
18937
19424
|
}
|
|
18938
19425
|
async function executetriggerstep(step, session, plan, tabid2, origin) {
|
|
18939
|
-
const options =
|
|
19426
|
+
const options = stepoptions3(step);
|
|
18940
19427
|
const family = triggerfamilyof(step.kind);
|
|
18941
19428
|
if (!family) throw new Error(`The ${step.kind} step is not a reviewed trigger kind.`);
|
|
18942
19429
|
const workflowid = typeof options.workflowid === "string" ? options.workflowid : "";
|
|
@@ -19146,6 +19633,22 @@ async function executeaction(step, session, plan, tabid2, origin, settings, verd
|
|
|
19146
19633
|
const capabilityverdict = offscreencapabilitygate({ environment: routing.environment, granted: offgranted });
|
|
19147
19634
|
if (!capabilityverdict.allowed) throw new Error(capabilityverdict.reason);
|
|
19148
19635
|
if (routing.fallback && plan) await audit("environment", `The ${step.kind} step ${step.id} fell back to inline parsing inside the page because the offscreen capability grant stays absent.`, { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id });
|
|
19636
|
+
const securityverdict = await securitystepgate(step, session, origin, settings);
|
|
19637
|
+
if (!securityverdict.allowed) {
|
|
19638
|
+
if (session && plan) {
|
|
19639
|
+
await memory.setprogress(recorddenied(await memory.getprogress(), plan.id, step.id, deniedevidenceof({ origin, kind: step.kind, reason: securityverdict.reason, now: Date.now() }), Date.now())).catch(() => {
|
|
19640
|
+
});
|
|
19641
|
+
await appendrunevent("deny", `The ${step.kind} step ${step.id} on ${origin} was denied without navigation: ${securityverdict.reason}`, session, origin, step.id).catch(() => {
|
|
19642
|
+
});
|
|
19643
|
+
}
|
|
19644
|
+
await audit("deny", `The ${step.kind} step ${step.id} on ${origin} was denied without navigation: ${securityverdict.reason}`, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {}, stepid: step.id });
|
|
19645
|
+
if (securityverdict.suspended && session) {
|
|
19646
|
+
await appendrunevent("expiry", `The consent window of ${origin} expired mid step and the run suspended at the step ${step.id}; a new explicit prompt renews it.`, session, origin, step.id).catch(() => {
|
|
19647
|
+
});
|
|
19648
|
+
await audit("expiry", `The consent window of ${origin} expired mid step and the run suspended at the step ${step.id}; the executor refuses to resume without a new explicit prompt.`, { sessionid: session.id, ...plan ? { planid: plan.id } : {}, stepid: step.id });
|
|
19649
|
+
}
|
|
19650
|
+
throw new Error(securityverdict.reason);
|
|
19651
|
+
}
|
|
19149
19652
|
if (session && plan && plan.state === "approved" && mode === "plan") await openplanrun(session, plan);
|
|
19150
19653
|
if (plan && plan.state === "approved") await markpendingstep(plan, step.id);
|
|
19151
19654
|
if (routing.environment === "sandboxframe") return executesandboxrender(step, session, plan, origin);
|
|
@@ -19273,10 +19776,13 @@ async function executeaction(step, session, plan, tabid2, origin, settings, verd
|
|
|
19273
19776
|
if (resolved) {
|
|
19274
19777
|
await memory.addresolution({ stepid: step.id, mode: resolved.mode, selector: resolved.selector, label: resolved.label, at: Date.now() });
|
|
19275
19778
|
}
|
|
19276
|
-
const
|
|
19779
|
+
const maskshapes = shapesof({ ...settings !== void 0 ? { settings } : {}, rules: await memory.getmaskrules(), origin });
|
|
19780
|
+
const outcome = { stepid: step.id, ok: Boolean(output?.ok), summary, environment: routing.environment, ...output?.details ? { details: maskexport(output.details, maskshapes) } : {}, at: Date.now() };
|
|
19277
19781
|
const auditkind = stepauditkind(step, Boolean(output?.ok));
|
|
19278
19782
|
await audit(auditkind, summary, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {}, stepid: step.id });
|
|
19279
19783
|
await memory.addoutcome(outcome);
|
|
19784
|
+
if (session) await appendrunevent("step", `The ${step.kind} step ${step.id} ${outcome.ok ? "completed" : "failed"} on ${origin}: ${summary}${securityverdict.classification.sensitive ? ` The step grades ${securityverdict.classification.reason}` : ""}`, session, origin, step.id).catch(() => {
|
|
19785
|
+
});
|
|
19280
19786
|
if (output?.ok && plan && mode === "plan") {
|
|
19281
19787
|
const base = await memory.getprogress();
|
|
19282
19788
|
const completed = watchwindow ? recordwatchcompletion(base, plan.id, step.id, watchwindow.startedat, watchwindow.lifetime, Date.now()) : recordstep(base, plan.id, step.id, Date.now());
|
|
@@ -19319,7 +19825,7 @@ async function previewstep(stepid) {
|
|
|
19319
19825
|
if (!step) throw new Error("Reviewed step was not found.");
|
|
19320
19826
|
const gate = canpreview({ session, plan, step, tabid: tab.id, origin });
|
|
19321
19827
|
if (!gate.allowed) throw new Error(gate.reason);
|
|
19322
|
-
if (!step.target && !
|
|
19828
|
+
if (!step.target && !stepoptions3(step).targetref) throw new Error("Only a target-based step can be previewed.");
|
|
19323
19829
|
const result = await chrome.scripting.executeScript({ target: { tabId: tab.id }, func: (action, expectedorigin) => {
|
|
19324
19830
|
const bridge = globalThis.devthinkbridge;
|
|
19325
19831
|
if (!bridge) throw new Error("Devthink page bridge is unavailable.");
|
|
@@ -19491,7 +19997,7 @@ async function handlerequest(message, sender) {
|
|
|
19491
19997
|
const livetab = session ? await chrome.tabs.get(session.tabid).catch(() => void 0) : void 0;
|
|
19492
19998
|
const waitprofile = session ? waitprofiles.find((record2) => record2.origin === session.origin) : void 0;
|
|
19493
19999
|
const livestate = { phase: livetab?.status === "loading" ? "loading" : "complete", ...navrecords[0] ? { finalurl: navrecords[0].finalurl, redirects: navrecords[0].chain } : {} };
|
|
19494
|
-
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, profileretention: runsettings?.profileretention, traceceiling: runsettings?.traceceiling, profile: profilereport({ flows: await memory.getflowmetrics(), heaps: await memory.getheaprecords(), samples: await memory.getgrowsamples(), trends: await memory.gettrends(), profiles: await memory.getcpuprofiles(), shifts: await memory.getshiftentries(), traces: await memory.gettracerecords(), sourcemaps: await memory.getsourcemaps(), consents: await memory.getsourcemapconsents() }), profileactive: activememorytrackers.size + activeprofiletargets.size, profiletargets: [...activeprofiletargets.values()].flatMap((entry) => entry.targets), socketsactive: activesockets.size, emulation: emulationreport({ ...plan && await loademulationstate(plan.id) !== void 0 ? { state: await loademulationstate(plan.id) } : {}, devices: await memory.getdevicepresets(), networks: await memory.getnetworkpresets(), locations: await memory.getlocationpresets(), agents: await memory.getagentpresets(), blackbox: await memory.getblackboxrules(), permissions: await memory.getpermissionoverrides(), consents: await memory.getlocationconsents() }), emulatedlayers: plan ? layernames(await loademulationstate(plan.id)) : [], emulationretention: runsettings?.emulationretention, 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), sessionmemory: sessionreport({ records: sessionrecords, events: await memory.getsessionevents(), folders: await memory.getsessionfolders(), diffs: await memory.getsessiondiffs(), ...autosnapshot !== void 0 ? { auto: autosnapshot.interval } : {}, ...crashed ? { crashed: true } : {} }), autosnapshotstate: autosnapshot, sessionretention: runsettings?.sessionretention, trigger: triggerlist({ rules: await memory.gettriggerules(), workflows: await memory.listworkflows(), queue: await memory.gettriggerqueue() }), triggerretention: runsettings?.triggerretention, workflow: workflowreport({ workflows: await memory.listworkflows(), runs: await memory.listworkflowruns(), templates: await memory.getsteptemplates(), ...newestworkflowrun !== void 0 ? { log: await memory.getrunlog(newestworkflowrun.id), scopes: await memory.getrunscopes(newestworkflowrun.id), provenance: await memory.getworkflowprovenance(newestworkflowrun.id), control: await memory.listcontroldecisions(newestworkflowrun.id) } : {} }), runlogretention: runsettings?.runlogretention, runhistoryretention: runsettings?.runhistoryretention, editor: editorstate({ versions: await memory.listworkflowversions(), diffs: await memory.listversiondiffs(), history: await memory.gethistory(), overrides: await memory.listsiteoverrides(), imports: (await memory.listworkflowimports()).map((entry) => ({ id: entry.id, workflowid: entry.record.id, name: entry.record.name, version: entry.record.version, steps: entry.record.steps.length, risk: entry.record.risk, importedat: entry.importedat, ...entry.filename !== void 0 ? { filename: entry.filename } : {} })), backgroundruns: await memory.getbackgroundruns(), watchdog: { ...runsettings?.watchdog !== void 0 ? { config: runsettings.watchdog } : {}, events: await memory.listwatchdogevents() } }), ...taskstate !== void 0 ? { taskstate } : {}, ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {}, mcp: await mcpstateof(), llm: await llmstateof(), swarm: await swarmstateof(), environments: await environmentviewof() };
|
|
20000
|
+
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, profileretention: runsettings?.profileretention, traceceiling: runsettings?.traceceiling, profile: profilereport({ flows: await memory.getflowmetrics(), heaps: await memory.getheaprecords(), samples: await memory.getgrowsamples(), trends: await memory.gettrends(), profiles: await memory.getcpuprofiles(), shifts: await memory.getshiftentries(), traces: await memory.gettracerecords(), sourcemaps: await memory.getsourcemaps(), consents: await memory.getsourcemapconsents() }), profileactive: activememorytrackers.size + activeprofiletargets.size, profiletargets: [...activeprofiletargets.values()].flatMap((entry) => entry.targets), socketsactive: activesockets.size, emulation: emulationreport({ ...plan && await loademulationstate(plan.id) !== void 0 ? { state: await loademulationstate(plan.id) } : {}, devices: await memory.getdevicepresets(), networks: await memory.getnetworkpresets(), locations: await memory.getlocationpresets(), agents: await memory.getagentpresets(), blackbox: await memory.getblackboxrules(), permissions: await memory.getpermissionoverrides(), consents: await memory.getlocationconsents() }), emulatedlayers: plan ? layernames(await loademulationstate(plan.id)) : [], emulationretention: runsettings?.emulationretention, 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), sessionmemory: sessionreport({ records: sessionrecords, events: await memory.getsessionevents(), folders: await memory.getsessionfolders(), diffs: await memory.getsessiondiffs(), ...autosnapshot !== void 0 ? { auto: autosnapshot.interval } : {}, ...crashed ? { crashed: true } : {} }), autosnapshotstate: autosnapshot, sessionretention: runsettings?.sessionretention, trigger: triggerlist({ rules: await memory.gettriggerules(), workflows: await memory.listworkflows(), queue: await memory.gettriggerqueue() }), triggerretention: runsettings?.triggerretention, workflow: workflowreport({ workflows: await memory.listworkflows(), runs: await memory.listworkflowruns(), templates: await memory.getsteptemplates(), ...newestworkflowrun !== void 0 ? { log: await memory.getrunlog(newestworkflowrun.id), scopes: await memory.getrunscopes(newestworkflowrun.id), provenance: await memory.getworkflowprovenance(newestworkflowrun.id), control: await memory.listcontroldecisions(newestworkflowrun.id) } : {} }), runlogretention: runsettings?.runlogretention, runhistoryretention: runsettings?.runhistoryretention, editor: editorstate({ versions: await memory.listworkflowversions(), diffs: await memory.listversiondiffs(), history: await memory.gethistory(), overrides: await memory.listsiteoverrides(), imports: (await memory.listworkflowimports()).map((entry) => ({ id: entry.id, workflowid: entry.record.id, name: entry.record.name, version: entry.record.version, steps: entry.record.steps.length, risk: entry.record.risk, importedat: entry.importedat, ...entry.filename !== void 0 ? { filename: entry.filename } : {} })), backgroundruns: await memory.getbackgroundruns(), watchdog: { ...runsettings?.watchdog !== void 0 ? { config: runsettings.watchdog } : {}, events: await memory.listwatchdogevents() } }), ...taskstate !== void 0 ? { taskstate } : {}, ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {}, mcp: await mcpstateof(), llm: await llmstateof(), swarm: await swarmstateof(), environments: await environmentviewof(), security: await securityviewof() };
|
|
19495
20001
|
}
|
|
19496
20002
|
case "capabilities":
|
|
19497
20003
|
return refreshcapabilities();
|
|
@@ -20546,7 +21052,11 @@ async function handlerequest(message, sender) {
|
|
|
20546
21052
|
});
|
|
20547
21053
|
activerecordings.delete(id);
|
|
20548
21054
|
}
|
|
20549
|
-
if (session)
|
|
21055
|
+
if (session) {
|
|
21056
|
+
await memory.setsession({ ...session, stoppedat: Date.now() });
|
|
21057
|
+
await sealsessionrunlog(session.id).catch(() => {
|
|
21058
|
+
});
|
|
21059
|
+
}
|
|
20550
21060
|
const plan = await memory.getplan();
|
|
20551
21061
|
if (plan && ["pending", "approved"].includes(plan.state)) await memory.setplan({ ...plan, state: "cancelled" });
|
|
20552
21062
|
await audit("stop", "The user stopped the browser session.", { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {} });
|
|
@@ -22538,6 +23048,160 @@ async function handlerequest(message, sender) {
|
|
|
22538
23048
|
}
|
|
22539
23049
|
throw new Error("The swarm merge request carries no merge, report, export, compare, lesson, costs, timeline, replay or snapshot action.");
|
|
22540
23050
|
}
|
|
23051
|
+
case "security": {
|
|
23052
|
+
const input2 = message;
|
|
23053
|
+
const now = Date.now();
|
|
23054
|
+
const session = await memory.getsession();
|
|
23055
|
+
const settings = await memory.getsettings();
|
|
23056
|
+
if (input2.allowlist !== void 0) {
|
|
23057
|
+
if (input2.allowlist.add !== void 0) {
|
|
23058
|
+
const origin = input2.allowlist.add.origin?.trim() ?? "";
|
|
23059
|
+
if (origin === "") throw new Error("The allowlist grant needs its exact origin.");
|
|
23060
|
+
if (wildcardentry(origin)) throw new Error("The allowlist binds every grant to one exact origin; a wildcard entry never passes.");
|
|
23061
|
+
await memory.addallowlistorigin({ origin, profileid: runstateprofile, grantedat: now });
|
|
23062
|
+
await audit("grant", `The user added the exact origin ${origin} to the automation allowlist of the profile workspace; no wildcard expansion exists.`, { ...session ? { sessionid: session.id } : {} });
|
|
23063
|
+
return { ...await securityviewof(), granted: origin };
|
|
23064
|
+
}
|
|
23065
|
+
if (input2.allowlist.remove !== void 0) {
|
|
23066
|
+
const origin = input2.allowlist.remove.origin?.trim() ?? "";
|
|
23067
|
+
await memory.removeallowlistorigin(origin, runstateprofile);
|
|
23068
|
+
await audit("revoke", `The user removed the origin ${origin} from the automation allowlist; the denydefault posture refuses the origin again.`, { ...session ? { sessionid: session.id } : {} });
|
|
23069
|
+
return { ...await securityviewof(), removed: origin };
|
|
23070
|
+
}
|
|
23071
|
+
}
|
|
23072
|
+
if (input2.profile !== void 0) {
|
|
23073
|
+
const origin = input2.profile.origin?.trim() ?? session?.origin ?? "";
|
|
23074
|
+
const kind = input2.profile.kind?.trim() ?? "";
|
|
23075
|
+
const decision = input2.profile.decision === "deny" ? "deny" : "grant";
|
|
23076
|
+
if (origin === "" || kind === "") throw new Error("The origin profile decision needs its exact origin and its action kind.");
|
|
23077
|
+
const profiles = await memory.getoriginprofiles();
|
|
23078
|
+
const existing = profiles.find((candidate) => candidate.origin === origin);
|
|
23079
|
+
const updated = profilekind({ profile: existing ?? originprofileof({ origin, now }), kind, decision, now });
|
|
23080
|
+
await memory.saveoriginprofile(updated);
|
|
23081
|
+
await audit("grant", `The origin profile of ${origin} now ${decision === "grant" ? "grants" : "denies"} the ${kind} kind the user reviewed; ${updated.grants.length} grant${updated.grants.length === 1 ? "" : "s"} and ${updated.denials.length} denial${updated.denials.length === 1 ? "" : "s"} on the origin.`, { ...session ? { sessionid: session.id } : {} });
|
|
23082
|
+
return { ...await securityviewof(), profile: updated };
|
|
23083
|
+
}
|
|
23084
|
+
if (input2.consent !== void 0) {
|
|
23085
|
+
if (input2.consent.open !== void 0) {
|
|
23086
|
+
if (!session) throw new Error("The consent window opens inside an active session.");
|
|
23087
|
+
const duration = input2.consent.open.duration ?? settings?.consentduration;
|
|
23088
|
+
if (duration === void 0) throw new Error("The consent prompt needs its duration in milliseconds; no grant ever defaults to unlimited.");
|
|
23089
|
+
const durationgate = consentdurationvalid(duration);
|
|
23090
|
+
if (!durationgate.allowed) throw new Error(durationgate.reason);
|
|
23091
|
+
const origin = input2.consent.open.origin?.trim() !== "" && input2.consent.open.origin !== void 0 ? input2.consent.open.origin.trim() : session.origin;
|
|
23092
|
+
const kinds = (input2.consent.open.kinds ?? []).map((kind) => kind.trim()).filter((kind) => kind !== "");
|
|
23093
|
+
const window2 = openconsentwindow({ sessionid: session.id, origin, duration, kinds: kinds.length > 0 ? kinds : ["observe"], now });
|
|
23094
|
+
await memory.setconsentwindows([window2, ...(await memory.getconsentwindows()).map((candidate) => candidate.sessionid === session.id && candidate.origin === origin && candidate.state === "active" ? { ...candidate, state: "closed", closedat: now } : candidate)]);
|
|
23095
|
+
await appendrunevent("grant", `The consent prompt opened the window ${window2.id} for ${origin} with the boundary ${window2.boundary}.`, session, origin).catch(() => {
|
|
23096
|
+
});
|
|
23097
|
+
await audit("grant", `The user answered the consent prompt for ${origin} with the window ${window2.id} and the boundary ${window2.boundary}; no grant ever outlives its named boundary.`, { sessionid: session.id });
|
|
23098
|
+
return { ...await securityviewof(), window: window2 };
|
|
23099
|
+
}
|
|
23100
|
+
if (input2.consent.renew !== void 0) {
|
|
23101
|
+
if (!session) throw new Error("The consent window renewal opens inside an active session.");
|
|
23102
|
+
const windowid = input2.consent.renew.windowid?.trim() ?? "";
|
|
23103
|
+
const current = (await memory.getconsentwindows()).find((candidate) => candidate.id === windowid && candidate.sessionid === session.id);
|
|
23104
|
+
if (!current) throw new Error(`No consent window ${windowid} exists for the session.`);
|
|
23105
|
+
const duration = input2.consent.renew.duration ?? settings?.consentduration;
|
|
23106
|
+
if (duration === void 0) throw new Error("The renewal prompt needs its duration in milliseconds; a renewal only runs through a new explicit prompt.");
|
|
23107
|
+
const durationgate = consentdurationvalid(duration);
|
|
23108
|
+
if (!durationgate.allowed) throw new Error(durationgate.reason);
|
|
23109
|
+
const { renewed, closed } = renewconsentwindow({ window: current, duration, kinds: current.kinds, now });
|
|
23110
|
+
await memory.setconsentwindows([renewed, closed, ...(await memory.getconsentwindows()).filter((candidate) => candidate.id !== windowid)]);
|
|
23111
|
+
await appendrunevent("grant", `The consent window of ${current.origin} renewed through a new explicit prompt with the boundary ${renewed.boundary}; the old window stays closed in the history.`, session, current.origin).catch(() => {
|
|
23112
|
+
});
|
|
23113
|
+
await audit("grant", `The user renewed the consent window of ${current.origin} through a new explicit prompt with the boundary ${renewed.boundary}.`, { sessionid: session.id });
|
|
23114
|
+
return { ...await securityviewof(), window: renewed };
|
|
23115
|
+
}
|
|
23116
|
+
if (input2.consent.classes !== void 0) {
|
|
23117
|
+
const origin = input2.consent.classes.origin?.trim() || session?.origin || "";
|
|
23118
|
+
const classes = (input2.consent.classes.classes ?? []).map((kind) => kind.trim()).filter((kind) => kind !== "");
|
|
23119
|
+
if (origin === "" || classes.length === 0) throw new Error("The fresh class consent needs its origin and its sensitive classes.");
|
|
23120
|
+
for (const classname of classes) {
|
|
23121
|
+
await memory.addclassconsent({ id: randomid(), origin, sensitiveclass: classname, grantedat: now });
|
|
23122
|
+
await audit("grant", `The user gave one fresh consent prompt for the ${classname} class on ${origin}; the prompt of one class never widens another.`, { ...session ? { sessionid: session.id } : {} });
|
|
23123
|
+
}
|
|
23124
|
+
return { ...await securityviewof(), classes };
|
|
23125
|
+
}
|
|
23126
|
+
}
|
|
23127
|
+
if (input2.revoke !== void 0) {
|
|
23128
|
+
if (!session) throw new Error("The revocation needs its active session.");
|
|
23129
|
+
const plan2 = await memory.getplan();
|
|
23130
|
+
const runid = input2.revoke.runid?.trim() || plan2?.id || "";
|
|
23131
|
+
if (runid === "") throw new Error("The revocation needs its run.");
|
|
23132
|
+
const state = await memory.getrunstate(runstateprofile);
|
|
23133
|
+
const pendingstepid = state?.pendingstepid;
|
|
23134
|
+
const progress = await memory.getprogress();
|
|
23135
|
+
const completed = new Set(progress?.planid === runid ? progress.completedsteps : []);
|
|
23136
|
+
const queued = (plan2?.steps ?? []).map((step) => step.id).filter((id) => !completed.has(id));
|
|
23137
|
+
const revocation = revokerun({ sessionid: session.id, runid, ...pendingstepid !== void 0 ? { pendingstepid } : {}, ...queued.length > 0 ? { queuedstepids: queued } : {}, actor: "user", ...input2.revoke.reason !== void 0 ? { reason: input2.revoke.reason } : {}, now });
|
|
23138
|
+
await memory.addrevocation(revocation);
|
|
23139
|
+
if (plan2 && plan2.id === runid && ["pending", "approved"].includes(plan2.state)) await memory.setplan({ ...plan2, state: "cancelled" });
|
|
23140
|
+
if (plan2 && plan2.id === runid && pendingstepid !== void 0) await memory.setprogress(recordrevocation(progress, runid, pendingstepid, { haltedstepids: revocation.haltedstepids, revokedstepid: pendingstepid, reason: revocation.reason }, now));
|
|
23141
|
+
await appendrunevent("revoke", `The user revoked the run ${runid}: the pending step ${pendingstepid ?? "none"} and every queued step halted without executing (${revocation.haltedstepids.join(", ")}).`, session, session.origin, pendingstepid).catch(() => {
|
|
23142
|
+
});
|
|
23143
|
+
await audit("revoke", `The user revoked the run ${runid} mid step: ${revocation.haltedstepids.length} step${revocation.haltedstepids.length === 1 ? "" : "s"} halted without executing, and the run log records the terminal event.`, { sessionid: session.id, planid: runid, ...pendingstepid !== void 0 ? { stepid: pendingstepid } : {} });
|
|
23144
|
+
return { ...await securityviewof(), revocation };
|
|
23145
|
+
}
|
|
23146
|
+
if (input2.mask !== void 0) {
|
|
23147
|
+
if (input2.mask.add !== void 0) {
|
|
23148
|
+
const shapes = (input2.mask.add.shapes ?? []).map((shape) => shape.trim().toLowerCase()).filter((shape) => shape !== "");
|
|
23149
|
+
if (shapes.length === 0) throw new Error("The mask rule needs its field shapes.");
|
|
23150
|
+
const rule = { id: randomid(), ...input2.mask.add.origin !== void 0 && input2.mask.add.origin.trim() !== "" ? { origin: input2.mask.add.origin.trim() } : {}, shapes, createdat: now };
|
|
23151
|
+
await memory.addmaskrule(rule);
|
|
23152
|
+
await audit("mask", `The user added the mask rule ${rule.id} for the ${shapes.join(", ")} field shape${shapes.length === 1 ? "" : "s"}${rule.origin !== void 0 ? ` scoped to ${rule.origin}` : ""}; typed values behind the shapes never reach a record.`, { ...session ? { sessionid: session.id } : {} });
|
|
23153
|
+
return { ...await securityviewof(), rule };
|
|
23154
|
+
}
|
|
23155
|
+
if (input2.mask.remove !== void 0) {
|
|
23156
|
+
const id = input2.mask.remove.id?.trim() ?? "";
|
|
23157
|
+
await memory.removemaskrule(id);
|
|
23158
|
+
await audit("mask", `The user removed the mask rule ${id}.`, { ...session ? { sessionid: session.id } : {} });
|
|
23159
|
+
return { ...await securityviewof(), removed: id };
|
|
23160
|
+
}
|
|
23161
|
+
}
|
|
23162
|
+
if (input2.read !== void 0) {
|
|
23163
|
+
const runid = input2.read.runid?.trim() || session?.id || "";
|
|
23164
|
+
const log = await memory.getimmutablelog(runid);
|
|
23165
|
+
if (!log) throw new Error(`No run log exists for the run ${runid}.`);
|
|
23166
|
+
const read = await readverifiedlog(log);
|
|
23167
|
+
const readgate = logreadgate({ valid: read.ok });
|
|
23168
|
+
if (!readgate.allowed) throw new Error(readgate.reason);
|
|
23169
|
+
await audit("consent", `The run log of ${runid} was read through the audit accessor with ${read.entries.length} verified entries: ${read.reason}`, { ...session ? { sessionid: session.id } : {}, planid: runid });
|
|
23170
|
+
return { ...await securityviewof(), log: read.entries, verification: read.reason };
|
|
23171
|
+
}
|
|
23172
|
+
if (input2.export !== void 0) {
|
|
23173
|
+
const runid = input2.export.runid?.trim() || session?.id || "";
|
|
23174
|
+
const exported = await memory.exportverifiedrunlog(runid);
|
|
23175
|
+
if (!exported.chainvalid) throw new Error(exported.reason);
|
|
23176
|
+
await audit("export", `The verified run log of ${runid} was exported as an audit file with ${exported.entries} entries${exported.sealhash !== void 0 ? ` and the seal hash ${exported.sealhash}` : ""}: ${exported.reason}`, { ...session ? { sessionid: session.id } : {}, planid: runid });
|
|
23177
|
+
return { ...await securityviewof(), export: exported };
|
|
23178
|
+
}
|
|
23179
|
+
if (input2.settings !== void 0) {
|
|
23180
|
+
const patch = { ...settings };
|
|
23181
|
+
if (input2.settings.consentduration !== void 0) {
|
|
23182
|
+
const durationgate = consentdurationvalid(input2.settings.consentduration);
|
|
23183
|
+
if (!durationgate.allowed) throw new Error(durationgate.reason);
|
|
23184
|
+
patch.consentduration = input2.settings.consentduration;
|
|
23185
|
+
}
|
|
23186
|
+
if (input2.settings.logretention !== void 0) patch.logretention = input2.settings.logretention;
|
|
23187
|
+
if (input2.settings.maskshapes !== void 0) patch.maskshapes = input2.settings.maskshapes.map((shape) => shape.trim().toLowerCase()).filter((shape) => shape !== "");
|
|
23188
|
+
await memory.setsettings(patch);
|
|
23189
|
+
await audit("configure", `The user updated the security settings: consent duration ${patch.consentduration !== void 0 ? `${patch.consentduration} milliseconds` : "the prompt asks every time"}, log retention ${patch.logretention !== void 0 ? `${patch.logretention} milliseconds` : "every sealed log stays"}, mask shapes ${patch.maskshapes?.length ?? 0} configured.`, {});
|
|
23190
|
+
return { ...await securityviewof(), configured: true };
|
|
23191
|
+
}
|
|
23192
|
+
const plan = await memory.getplan();
|
|
23193
|
+
const pending = [];
|
|
23194
|
+
if (session && plan && ["pending", "approved"].includes(plan.state)) {
|
|
23195
|
+
for (const step of plan.steps) {
|
|
23196
|
+
const classification = sensitiveclassesof(step);
|
|
23197
|
+
if (!classification.sensitive) continue;
|
|
23198
|
+
const missing = classification.classes.length > 0 ? classification.classes : [];
|
|
23199
|
+
if (missing.length === 0 && !classification.bydefault) continue;
|
|
23200
|
+
pending.push({ stepid: step.id, kind: step.kind, origin: plan.origin, prompt: consentprompttext({ origin: plan.origin, kind: step.kind, classes: classification.classes, bydefault: classification.bydefault, duration: settings?.consentduration ?? session.expiresat - now }) });
|
|
23201
|
+
}
|
|
23202
|
+
}
|
|
23203
|
+
return { ...await securityviewof(), prompts: pending };
|
|
23204
|
+
}
|
|
22541
23205
|
case "environments": {
|
|
22542
23206
|
const input2 = message;
|
|
22543
23207
|
const now = Date.now();
|
|
@@ -22982,7 +23646,7 @@ async function raiseremoteapproval(clientid, toolname, params, step) {
|
|
|
22982
23646
|
return { content: `The approval gate ${request.id} holds the ${toolname} call; it executes once the user approves it in the panel.`, payload: { approvalid: request.id, state: "pending", ...request.timeoutat !== void 0 ? { timeoutat: request.timeoutat } : {} }, iserror: false };
|
|
22983
23647
|
}
|
|
22984
23648
|
async function executelistruns(step, session) {
|
|
22985
|
-
const options =
|
|
23649
|
+
const options = stepoptions3(step);
|
|
22986
23650
|
const statefilter = typeof options.state === "string" && options.state.trim() !== "" ? options.state : void 0;
|
|
22987
23651
|
const runs = await memory.listworkflowruns();
|
|
22988
23652
|
const selected = statefilter !== void 0 ? runs.filter((run) => run.state === statefilter) : runs;
|
|
@@ -23357,7 +24021,7 @@ async function maybeautosnapshot() {
|
|
|
23357
24021
|
await audit("session", `The reviewed auto snapshot interval stopped after ${state.interval.maxsnapshots} snapshot${state.interval.maxsnapshots === 1 ? "" : "s"}; the retention window of ${state.interval.expiry} millisecond${state.interval.expiry === 1 ? "" : "s"} expires them by user choice.`, { sessionid: session.id, planid: plan.id });
|
|
23358
24022
|
return;
|
|
23359
24023
|
}
|
|
23360
|
-
const options =
|
|
24024
|
+
const options = stepoptions3(step);
|
|
23361
24025
|
const snapshot2 = snapshotplanof(options.snapshot);
|
|
23362
24026
|
if (!snapshot2) return;
|
|
23363
24027
|
const record2 = await capturesessionrecord({ ...snapshot2, ...snapshot2.auto !== void 0 ? { auto: snapshot2.auto } : {} }, session, plan.id).catch(() => void 0);
|