@wenathlan/extension 1.1.60 → 1.1.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +5 -3
  2. package/dist/confirmgates.d.ts +60 -0
  3. package/dist/confirmgates.d.ts.map +1 -0
  4. package/dist/immutablelog.d.ts +73 -0
  5. package/dist/immutablelog.d.ts.map +1 -0
  6. package/dist/inboundguard.d.ts +86 -0
  7. package/dist/inboundguard.d.ts.map +1 -0
  8. package/dist/index.d.ts +10 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +1278 -1
  11. package/dist/index.js.map +4 -4
  12. package/dist/maskinputs.d.ts +52 -0
  13. package/dist/maskinputs.d.ts.map +1 -0
  14. package/dist/memory.d.ts +153 -1
  15. package/dist/memory.d.ts.map +1 -1
  16. package/dist/originpolicy.d.ts +160 -0
  17. package/dist/originpolicy.d.ts.map +1 -0
  18. package/dist/phishguard.d.ts +24 -0
  19. package/dist/phishguard.d.ts.map +1 -0
  20. package/dist/policy.d.ts +123 -1
  21. package/dist/policy.d.ts.map +1 -1
  22. package/dist/protocol.d.ts +164 -1
  23. package/dist/protocol.d.ts.map +1 -1
  24. package/dist/redactshots.d.ts +54 -0
  25. package/dist/redactshots.d.ts.map +1 -0
  26. package/dist/secretvault.d.ts +89 -0
  27. package/dist/secretvault.d.ts.map +1 -0
  28. package/dist/transparency.d.ts +46 -0
  29. package/dist/transparency.d.ts.map +1 -0
  30. package/dist/types.d.ts +316 -2
  31. package/dist/types.d.ts.map +1 -1
  32. package/dist/version.d.ts +1 -1
  33. package/extension/dist/background.js +1650 -56
  34. package/extension/dist/background.js.map +4 -4
  35. package/extension/dist/manifest.json +5 -1
  36. package/extension/dist/pagebridge.js +91 -28
  37. package/extension/dist/pagebridge.js.map +3 -3
  38. package/extension/dist/popup.html +1 -1
  39. package/extension/dist/popup.js +54 -0
  40. package/extension/dist/popup.js.map +2 -2
  41. package/extension/dist/sidepanel.html +1 -1
  42. package/extension/dist/sidepanel.js +331 -0
  43. package/extension/dist/sidepanel.js.map +4 -4
  44. package/extension/dist/transparencypage.html +24 -0
  45. package/extension/dist/transparencypage.js +156 -0
  46. package/extension/dist/transparencypage.js.map +7 -0
  47. package/extension/manifest.json +5 -1
  48. 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,302 @@ 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
+ }
4740
+ /**
4741
+ * Security part two persistence of the 1.1.62 family.
4742
+ * The protections for secrets, messages and money live here: the secretvault metadata with labels and scopes only and never values, scoped per profile workspace; the connectallow entries with their senders shipping empty by default; the ratelimit bucket state per origin and per session; the confirm gates with their resolution events and their human action provenance; the redactshot regions per origin and page template; the phishguard verdicts with their distance scores expiring past their freshness window; the permdiff records of each installed version; the safedefaults applications with their first seen origins; and the deferred command events waiting for their bucket reset.
4743
+ * The vault values never touch this seam: only metadata persists while the values stay behind the vault seam the background wires.
4744
+ */
4745
+ /** Replaces the secretvault metadata of the profile workspaces: labels, scopes, provenance and digests only, never values. */
4746
+ async setsecretvault(entries) {
4747
+ return this.adapter.set("secretvault", entries);
4748
+ }
4749
+ /** Returns the stored secretvault metadata, oldest record first; the values live behind the vault seam and never persist. */
4750
+ async getsecretvault() {
4751
+ return await this.adapter.get("secretvault") ?? [];
4752
+ }
4753
+ /** Adds one secretvault metadata record scoped to a profile workspace; a duplicate vault id keeps its first record. */
4754
+ async addsecret(entry) {
4755
+ const entries = await this.getsecretvault();
4756
+ if (entries.some((candidate) => candidate.vaultid === entry.vaultid)) return;
4757
+ await this.setsecretvault([...entries, entry]);
4758
+ }
4759
+ /** Removes one secretvault metadata record by its vault id; the background drops the value behind the seam in the same action. */
4760
+ async removesecret(vaultid) {
4761
+ await this.setsecretvault((await this.getsecretvault()).filter((entry) => entry.vaultid !== vaultid));
4762
+ }
4763
+ /** Stamps the last use of one secretvault record: the metadata notes when the vault last released its value while the value itself stays unrecorded. */
4764
+ async stampsecretuse(vaultid, at) {
4765
+ await this.setsecretvault((await this.getsecretvault()).map((entry) => entry.vaultid === vaultid ? { ...entry, lastusedat: at } : entry));
4766
+ }
4767
+ /** Replaces the connectallow entries of external senders; the list ships empty by default with user managed entries only. */
4768
+ async setconnectallow(entries) {
4769
+ return this.adapter.set("connectallow", entries);
4770
+ }
4771
+ /** Returns the stored connectallow entries, oldest add first. */
4772
+ async getconnectallow() {
4773
+ return await this.adapter.get("connectallow") ?? [];
4774
+ }
4775
+ /** Adds one connectallow entry for an external sender; a duplicate sender id keeps its first entry. */
4776
+ async addconnectallow(entry) {
4777
+ const entries = await this.getconnectallow();
4778
+ if (entries.some((candidate) => candidate.senderid === entry.senderid)) return;
4779
+ await this.setconnectallow([...entries, entry]);
4780
+ }
4781
+ /** Removes one connectallow entry by its sender id; the origincheck drops the sender again after the removal. */
4782
+ async removeconnectallow(senderid) {
4783
+ await this.setconnectallow((await this.getconnectallow()).filter((entry) => entry.senderid !== senderid));
4784
+ }
4785
+ /** Replaces the ratelimit bucket state per origin and per session: the user configured bounds and windows with their used counts. */
4786
+ async setratelimitbuckets(buckets) {
4787
+ return this.adapter.set("ratelimitbuckets", buckets);
4788
+ }
4789
+ /** Returns the stored ratelimit buckets per origin and per session. */
4790
+ async getratelimitbuckets() {
4791
+ return await this.adapter.get("ratelimitbuckets") ?? [];
4792
+ }
4793
+ /** Upserts one ratelimit bucket: a bucket of the same origin and session replaces its state while a new pair joins the list. */
4794
+ async saveratelimitbucket(bucket) {
4795
+ const buckets = await this.getratelimitbuckets();
4796
+ await this.setratelimitbuckets(buckets.some((candidate) => candidate.origin === bucket.origin && candidate.sessionid === bucket.sessionid) ? buckets.map((candidate) => candidate.origin === bucket.origin && candidate.sessionid === bucket.sessionid ? bucket : candidate) : [...buckets, bucket]);
4797
+ }
4798
+ /** Removes the ratelimit bucket of one origin and session; the origin runs without a bucket because the bounds stay user choices only. */
4799
+ async removeratelimitbucket(origin, sessionid) {
4800
+ await this.setratelimitbuckets((await this.getratelimitbuckets()).filter((bucket) => !(bucket.origin === origin && bucket.sessionid === sessionid)));
4801
+ }
4802
+ /** Replaces the confirm gates with their payloads and states; a resolved or refused gate stays terminal for the audit trail. */
4803
+ async setgates(gates) {
4804
+ return this.adapter.set("confirmgates", gates);
4805
+ }
4806
+ /** Returns the stored confirm gates, newest open first. */
4807
+ async getgates() {
4808
+ return await this.adapter.get("confirmgates") ?? [];
4809
+ }
4810
+ /** Upserts one confirm gate: a gate of the same step keeps its latest record because one gated step carries one live gate. */
4811
+ async savegate(gate) {
4812
+ const gates = await this.getgates();
4813
+ await this.setgates(gates.some((candidate) => candidate.stepid === gate.stepid && candidate.kind === gate.kind) ? gates.map((candidate) => candidate.stepid === gate.stepid && candidate.kind === gate.kind ? gate : candidate) : [gate, ...gates]);
4814
+ }
4815
+ /** Records one gate resolution event with its human action provenance; the resolution history stays visible for the audit trail. */
4816
+ async addgateresolution(resolution) {
4817
+ await this.adapter.set("gateresolutions", [resolution, ...await this.adapter.get("gateresolutions") ?? []].slice(0, 500));
4818
+ }
4819
+ /** Returns the recorded gate resolution events with their human action provenance, newest first. */
4820
+ async getgateresolutions() {
4821
+ return await this.adapter.get("gateresolutions") ?? [];
4822
+ }
4823
+ /** Replaces the redactshot regions per origin and page template. */
4824
+ async setredactregions(regions) {
4825
+ return this.adapter.set("redactregions", regions);
4826
+ }
4827
+ /** Returns the stored redactshot regions per origin and page template, oldest rule first. */
4828
+ async getredactregions() {
4829
+ return await this.adapter.get("redactregions") ?? [];
4830
+ }
4831
+ /** Adds one redactshot region, derived from a field shape or drawn by the user. */
4832
+ async addredactregion(region) {
4833
+ await this.setredactregions([...await this.getredactregions(), region]);
4834
+ }
4835
+ /** Removes one redactshot region by its id. */
4836
+ async removeredactregion(id) {
4837
+ await this.setredactregions((await this.getredactregions()).filter((region) => region.id !== id));
4838
+ }
4839
+ /** Records one phishguard verdict with its distance score; the records stay for the audit trail while the freshness window governs the live set. */
4840
+ async addphishverdict(verdict) {
4841
+ await this.adapter.set("phishverdicts", [verdict, ...(await this.adapter.get("phishverdicts") ?? []).filter((candidate) => candidate.origin !== verdict.origin)].slice(0, 500));
4842
+ }
4843
+ /** Returns the stored phishguard verdicts with their distance scores, newest first. */
4844
+ async getphishverdicts() {
4845
+ return await this.adapter.get("phishverdicts") ?? [];
4846
+ }
4847
+ /** Expires the phishguard verdicts past the user configured freshness window: the expired verdicts keep their records for the audit trail while the guard recomputes the next login step. */
4848
+ async expirephishverdicts(freshness, now) {
4849
+ const verdicts = await this.getphishverdicts();
4850
+ if (freshness === void 0) return verdicts;
4851
+ return verdicts.filter((verdict) => now - verdict.at < freshness);
4852
+ }
4853
+ /** Records one permdiff between two installed permission versions; the record of each installed update stays for the audit trail. */
4854
+ async addpermdiff(diff) {
4855
+ await this.adapter.set("permdiffs", [diff, ...await this.adapter.get("permdiffs") ?? []].slice(0, 500));
4856
+ }
4857
+ /** Returns the recorded permdiffs of each installed update, newest first. */
4858
+ async getpermdiffs() {
4859
+ return await this.adapter.get("permdiffs") ?? [];
4860
+ }
4861
+ /** Stores the last installed permission set the permdiff of the next update compares against. */
4862
+ async setlastpermissions(permissions, version) {
4863
+ await this.adapter.set("lastpermissions", { permissions, version });
4864
+ }
4865
+ /** Returns the last installed permission set with its version; an absent record returns undefined. */
4866
+ async getlastpermissions() {
4867
+ return this.adapter.get("lastpermissions");
4868
+ }
4869
+ /** Records one safedefaults application with its first seen origin; the first visit of an unknown origin stays visible. */
4870
+ async addsafedefaultapplication(application) {
4871
+ const applications = await this.adapter.get("safedefaults") ?? [];
4872
+ if (applications.some((candidate) => candidate.origin === application.origin)) return;
4873
+ await this.adapter.set("safedefaults", [...applications, application]);
4874
+ }
4875
+ /** Returns the recorded safedefaults applications with their first seen origins, oldest first. */
4876
+ async getsafedefaultapplications() {
4877
+ return await this.adapter.get("safedefaults") ?? [];
4878
+ }
4879
+ /** Records one deferred command event with the reset time it waits for. */
4880
+ async adddeferredevent(event) {
4881
+ await this.adapter.set("deferredevents", [event, ...await this.adapter.get("deferredevents") ?? []].slice(0, 500));
4882
+ }
4883
+ /** Returns the recorded deferred command events, newest first. */
4884
+ async getdeferredevents() {
4885
+ return await this.adapter.get("deferredevents") ?? [];
4886
+ }
4887
+ /** Serves the transparency data of the transparencypage in one read: every active grant with its origin, scope and boundary, every consent window ever granted with its expiry, the connectallow entries with their senders, the permdiff records of each installed update, the safedefaults applications and the secretvault metadata with labels and scopes only. */
4888
+ async gettransparencyview() {
4889
+ return {
4890
+ allowlist: await this.getautomationallowlist(),
4891
+ profiles: await this.getoriginprofiles(),
4892
+ windows: await this.getconsentwindows(),
4893
+ connectallow: await this.getconnectallow(),
4894
+ permdiffs: await this.getpermdiffs(),
4895
+ safedefaults: await this.getsafedefaultapplications(),
4896
+ vault: await this.getsecretvault(),
4897
+ gates: await this.getgates(),
4898
+ resolutions: await this.getgateresolutions(),
4899
+ deferred: await this.getdeferredevents(),
4900
+ phishverdicts: await this.getphishverdicts()
4901
+ };
4902
+ }
4540
4903
  };
4541
4904
  function mediakindof(record2) {
4542
4905
  if ("pages" in record2) return "pdf";
@@ -4580,6 +4943,130 @@ function randomid() {
4580
4943
  return crypto.randomUUID();
4581
4944
  }
4582
4945
 
4946
+ // originpolicy.ts
4947
+ function exactorigin(origin, entry) {
4948
+ return origin.trim() !== "" && origin === entry;
4949
+ }
4950
+ function wildcardentry(entry) {
4951
+ return entry.includes("*") || entry.includes("://*.") || entry.trim() === "" || entry.trim() === "https://" || entry.trim() === "http://";
4952
+ }
4953
+ function allowlistcheck(input) {
4954
+ if (input.origin.trim() === "") return { allowed: false, reason: "The step needs the exact origin it targets." };
4955
+ for (const entry of input.allowlist) {
4956
+ 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.` };
4957
+ }
4958
+ const scoped = input.profileid === void 0 ? input.allowlist : input.allowlist.filter((entry) => entry.profileid === input.profileid);
4959
+ const granted = scoped.some((entry) => exactorigin(input.origin, entry.origin));
4960
+ if (granted) return { allowed: true, reason: `The origin ${input.origin} sits inside the automation allowlist the user granted.` };
4961
+ 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.` };
4962
+ return { allowed: false, reason: `The denydefault posture refuses ${input.origin} because the origin sits absent from the automation allowlist; grant the origin first.` };
4963
+ }
4964
+ function originprofileof(input) {
4965
+ if (input.origin.trim() === "") throw new Error("The origin profile needs its exact origin.");
4966
+ return { profileid: input.profileid ?? randomid(), origin: input.origin, grants: [...input.grants ?? []], denials: [...input.denials ?? []], createdat: input.now, updatedat: input.now };
4967
+ }
4968
+ function profilekind(input) {
4969
+ if (input.profile.grants.includes(input.kind) && input.decision === "grant") return input.profile;
4970
+ if (input.profile.denials.includes(input.kind) && input.decision === "deny") return input.profile;
4971
+ const grants = input.decision === "grant" ? [.../* @__PURE__ */ new Set([...input.profile.grants, input.kind])] : input.profile.grants.filter((kind) => kind !== input.kind);
4972
+ const denials = input.decision === "deny" ? [.../* @__PURE__ */ new Set([...input.profile.denials, input.kind])] : input.profile.denials.filter((kind) => kind !== input.kind);
4973
+ return { ...input.profile, grants, denials, updatedat: input.now };
4974
+ }
4975
+ function profilegrade(input) {
4976
+ if (!input.sensitive) return { allowed: true, consult: false, reason: `The ${input.kind} kind grades non-sensitive and the origin profile needs no consult.` };
4977
+ 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.` };
4978
+ 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.` };
4979
+ 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.` };
4980
+ 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.` };
4981
+ }
4982
+ function stepoptions(step) {
4983
+ if (!step.options) return {};
4984
+ try {
4985
+ const parsed = JSON.parse(step.options);
4986
+ return Boolean(parsed) && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
4987
+ } catch {
4988
+ return {};
4989
+ }
4990
+ }
4991
+ var paymentkinds = /* @__PURE__ */ new Set(["fillcard", "fillcode"]);
4992
+ var credentialkinds = /* @__PURE__ */ new Set(["consentpassword", "saveapikey", "handleauth", "authflow"]);
4993
+ var deletekinds = /* @__PURE__ */ new Set(["discardtab", "closepattern", "clearcookies", "removeattribute", "cleanupartifacts"]);
4994
+ var publishkinds = /* @__PURE__ */ new Set(["postform", "postfiles", "sendmessage", "submitform", "submitsearch", "writeclipboard"]);
4995
+ var defaultsensitivekinds = /* @__PURE__ */ new Set(["attachfile", "uploadfile", "uploadfiles", "downloadfile", "downloadimages", "batchdownload", "pausedownload", "resumedownload", "quarantinedownload", "evaluate"]);
4996
+ function sensitiveclassesof(step) {
4997
+ const options = stepoptions(step);
4998
+ const fields = Array.isArray(options.fields) ? options.fields.filter((item) => Boolean(item) && typeof item === "object") : [];
4999
+ 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());
5000
+ const carries = (shape) => names.some((name) => name.includes(shape));
5001
+ const classes = /* @__PURE__ */ new Set();
5002
+ if (paymentkinds.has(step.kind) || carries("card") || carries("cvc") || carries("cvv")) classes.add("payment");
5003
+ const credentialshape = carries("password") || carries("token") || carries("secret") || carries("apikey") || carries("passphrase");
5004
+ const submits = step.kind === "submitform" || step.kind === "postform" || step.kind === "submitsearch" || step.kind === "fillform" || step.kind === "filllabel" || step.kind === "fillplaceholder";
5005
+ if (credentialkinds.has(step.kind) || submits && credentialshape) classes.add("credential");
5006
+ if (deletekinds.has(step.kind)) classes.add("delete");
5007
+ if (publishkinds.has(step.kind) || step.kind === "callrest" || step.kind === "callgraphql") {
5008
+ const verb = typeof options.method === "string" ? options.method.trim().toUpperCase() : typeof options.verb === "string" ? options.verb.trim().toUpperCase() : "";
5009
+ if (step.kind === "callrest" || step.kind === "callgraphql") {
5010
+ if (verb !== "" && !["GET", "HEAD", "OPTIONS"].includes(verb)) classes.add("publish");
5011
+ } else classes.add("publish");
5012
+ }
5013
+ const bydefault = defaultsensitivekinds.has(step.kind);
5014
+ const list = [...classes];
5015
+ 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.` };
5016
+ 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" : ""}.` };
5017
+ }
5018
+ function classconsentcovers(consents, origin, sensitiveclass, now) {
5019
+ return consents.some((consent) => consent.origin === origin && consent.sensitiveclass === sensitiveclass && consent.grantedat <= now && (consent.expiresat === void 0 || now < consent.expiresat));
5020
+ }
5021
+ function missingclassconsents(input) {
5022
+ const missing = input.classes.filter((kind) => !classconsentcovers(input.consents, input.origin, kind, input.now));
5023
+ if (missing.length > 0) return { needed: true, missing, reason: `The sensitive classes ${missing.join(", ")} need one fresh consent prompt each on ${input.origin}.` };
5024
+ 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.` };
5025
+ return { needed: false, missing: [], reason: `The fresh class consents of ${input.origin} cover every class the step names.` };
5026
+ }
5027
+ function openconsentwindow(input) {
5028
+ if (input.sessionid.trim() === "" || input.origin.trim() === "") throw new Error("The consent window needs its session and its exact origin.");
5029
+ 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.");
5030
+ 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" };
5031
+ }
5032
+ function consentwindowstate(window2, now) {
5033
+ 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.` };
5034
+ 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.` };
5035
+ }
5036
+ function windowgatesstep(input) {
5037
+ 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.` };
5038
+ 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.` };
5039
+ 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.` };
5040
+ const state = consentwindowstate(input.window, input.now);
5041
+ if (state.state === "expired") return { allowed: false, suspended: true, reason: state.reason };
5042
+ return { allowed: true, suspended: false, reason: state.reason };
5043
+ }
5044
+ function renewconsentwindow(input) {
5045
+ const closed = input.window.state === "active" ? { ...input.window, state: "closed", closedat: input.now } : input.window;
5046
+ 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 });
5047
+ return { renewed, closed };
5048
+ }
5049
+ function revokerun(input) {
5050
+ if (input.sessionid.trim() === "" || input.runid.trim() === "") throw new Error("The revocation needs its session and run ids.");
5051
+ if (input.actor.trim() === "") throw new Error("The revocation names the acting user.");
5052
+ const halted = [...input.pendingstepid !== void 0 ? [input.pendingstepid] : [], ...input.queuedstepids ?? []];
5053
+ if (halted.length === 0) throw new Error("The revocation halts at least the pending step of the run.");
5054
+ 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 };
5055
+ }
5056
+ function scopegrantof(input) {
5057
+ if (input.origin.trim() === "") throw new Error("The consent scope needs its exact origin.");
5058
+ if (input.kinds.length === 0) throw new Error("The consent scope names the kinds it covers.");
5059
+ if (input.boundary.trim() === "") throw new Error("The consent scope names its boundary; no grant defaults to unlimited.");
5060
+ return { origin: input.origin, kinds: [...new Set(input.kinds)], boundary: input.boundary, grantedat: input.now };
5061
+ }
5062
+ function deniedevidenceof(input) {
5063
+ return { origin: input.origin, kind: input.kind, reason: input.reason, at: input.now };
5064
+ }
5065
+ function consentprompttext(input) {
5066
+ const label = input.classes.length > 0 ? `the ${input.classes.join(" and ")} class${input.classes.length === 1 ? "" : "es"}` : "a sensitive by default grade";
5067
+ 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.`;
5068
+ }
5069
+
4583
5070
  // environments.ts
4584
5071
  var offloadfamilies = [
4585
5072
  { task: "htmlsnapshot", kinds: ["readhtml", "parsehtml", "readertree", "readoutline", "classifypage"] },
@@ -6349,6 +6836,226 @@ function consolediff(input) {
6349
6836
  return { base: input.baseid, target: input.targetid, lines, added: added.length, removed: removed.length, repeated: repeated.length, at: input.now };
6350
6837
  }
6351
6838
 
6839
+ // inboundguard.ts
6840
+ function shapeof(value) {
6841
+ if (typeof value === "string") return "string";
6842
+ if (typeof value === "number") return "number";
6843
+ if (typeof value === "boolean") return "boolean";
6844
+ if (Array.isArray(value)) return "array";
6845
+ return "object";
6846
+ }
6847
+ function schemacheck(input) {
6848
+ const errors = [];
6849
+ for (const [field, value] of Object.entries(input.command)) {
6850
+ const expected = input.schema[field];
6851
+ if (expected === void 0) {
6852
+ errors.push({ path: field, expected: "absent", found: shapeof(value), reason: `The ${field} field sits absent from the declared grammar of the command; schemastrict refuses unknown fields before dispatch.` });
6853
+ continue;
6854
+ }
6855
+ if (expected === "absent") {
6856
+ errors.push({ path: field, expected: "absent", found: shapeof(value), reason: `The ${field} field carries no value under the declared grammar; schemastrict refuses it before dispatch.` });
6857
+ continue;
6858
+ }
6859
+ if (shapeof(value) !== expected) errors.push({ path: field, expected, found: shapeof(value), reason: `The ${field} field expects a ${expected} while the command carries a ${shapeof(value)}; schemastrict refuses the shape mismatch before dispatch.` });
6860
+ }
6861
+ for (const field of input.required ?? []) {
6862
+ if (input.command[field] === void 0) errors.push({ path: field, expected: input.schema[field] ?? "string", found: "absent", reason: `The ${field} field is required by the declared grammar and the command carries no value; schemastrict refuses the incomplete command before dispatch.` });
6863
+ }
6864
+ return { valid: errors.length === 0, errors };
6865
+ }
6866
+ function origincheckof(input) {
6867
+ const sender = input.senderid ?? "an unknown sender";
6868
+ const origin = input.senderorigin ?? "";
6869
+ if (input.senderid === input.extensionid) return { accepted: true, sender, origin, reason: "The sender is this extension itself; the internal surface accepts." };
6870
+ if (input.senderid !== void 0 && input.connectallow.some((entry) => entry.senderid === input.senderid && (entry.origin === void 0 || entry.origin === origin))) {
6871
+ return { accepted: true, sender, origin, reason: `The sender ${sender} sits in the connectallow list the user manages${origin !== "" ? ` for ${origin}` : ""}; the message accepts.` };
6872
+ }
6873
+ if (input.senderid === void 0) return { accepted: false, sender, origin, reason: "The message carries no sender identity; the guard drops it before any handler runs." };
6874
+ return { accepted: false, sender, origin, reason: `The sender ${sender} sits absent from the connectallow list; the guard drops the message without handler execution.` };
6875
+ }
6876
+ function portaccept(input) {
6877
+ const verdict = origincheckof(input);
6878
+ if (!verdict.accepted) return { ...verdict, reason: `The port ${input.portname} closes at its handshake: ${verdict.reason}` };
6879
+ return { ...verdict, reason: `The port ${input.portname} accepted its handshake: ${verdict.reason}` };
6880
+ }
6881
+ function connectallowentryof(input) {
6882
+ if (input.senderid.trim() === "") throw new Error("The connectallow entry needs its sender id.");
6883
+ if (input.displayname.trim() === "") throw new Error("The connectallow entry needs its display name.");
6884
+ return { senderid: input.senderid.trim(), displayname: input.displayname.trim(), ...input.origin !== void 0 && input.origin.trim() !== "" ? { origin: input.origin.trim() } : {}, addedat: input.now };
6885
+ }
6886
+ function bucketboundsvalid(limit, window2) {
6887
+ if (!Number.isFinite(limit) || limit <= 0) return { valid: false, reason: "The ratelimit bucket limit stays a positive user value; no hidden ceiling exists." };
6888
+ if (!Number.isFinite(window2) || window2 <= 0) return { valid: false, reason: "The ratelimit bucket window stays a positive user value in milliseconds; the window reset stays the user's choice." };
6889
+ return { valid: true, reason: `The bucket bound of ${limit} commands per ${window2} milliseconds stays the user configured choice with no hidden ceiling.` };
6890
+ }
6891
+ function bucketof(input) {
6892
+ const bounds = bucketboundsvalid(input.limit, input.window);
6893
+ if (!bounds.valid) throw new Error(bounds.reason);
6894
+ return { origin: input.origin, sessionid: input.sessionid, limit: input.limit, window: input.window, used: 0, windowstartedat: input.now, resetsat: input.now + input.window };
6895
+ }
6896
+ function bucketconsume(input) {
6897
+ if (input.now >= input.bucket.resetsat) {
6898
+ const fresh = { ...input.bucket, used: 0, windowstartedat: input.now, resetsat: input.now + input.bucket.window };
6899
+ return { allowed: true, deferred: false, bucket: { ...fresh, used: 1 }, resetsat: fresh.resetsat, reason: `The bucket window of ${input.bucket.origin} reset and the command consumes the first slot of ${fresh.limit}.` };
6900
+ }
6901
+ if (input.bucket.used < input.bucket.limit) {
6902
+ return { allowed: true, deferred: false, bucket: { ...input.bucket, used: input.bucket.used + 1 }, resetsat: input.bucket.resetsat, reason: `The command consumes slot ${input.bucket.used + 1} of ${input.bucket.limit} in the bucket of ${input.bucket.origin}.` };
6903
+ }
6904
+ return { allowed: false, deferred: true, bucket: input.bucket, resetsat: input.bucket.resetsat, reason: `The bucket of ${input.bucket.origin} holds its ${input.bucket.limit} command bound; the command defers until the window resets at ${input.bucket.resetsat}.` };
6905
+ }
6906
+ function deferredeventof(input) {
6907
+ if (input.stepid.trim() === "" || input.kind.trim() === "") throw new Error("The deferred event needs its step and kind.");
6908
+ return { id: input.id ?? randomid(), stepid: input.stepid, kind: input.kind, origin: input.origin, reason: input.reason, resetsat: input.resetsat, at: input.now };
6909
+ }
6910
+
6911
+ // confirmgates.ts
6912
+ function stepoptions2(step) {
6913
+ if (!step.options) return {};
6914
+ try {
6915
+ const parsed = JSON.parse(step.options);
6916
+ return Boolean(parsed) && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
6917
+ } catch {
6918
+ return {};
6919
+ }
6920
+ }
6921
+ function gatekindfor(classes) {
6922
+ if (classes.includes("payment")) return "confirmpay";
6923
+ if (classes.includes("delete")) return "confirmdelete";
6924
+ if (classes.includes("credential")) return "confirmcreds";
6925
+ return void 0;
6926
+ }
6927
+ function paypayload(input) {
6928
+ const payload = { payeeorigin: input.payeeorigin };
6929
+ if (input.amount !== void 0 && input.amount.trim() !== "") payload.amount = input.amount.trim();
6930
+ if (input.target !== void 0 && input.target.trim() !== "") payload.target = input.target.trim();
6931
+ return payload;
6932
+ }
6933
+ function deletepayload(input) {
6934
+ const payload = { scope: input.scope, irreversibility: input.irreversibility };
6935
+ if (input.target !== void 0 && input.target.trim() !== "") payload.target = input.target.trim();
6936
+ return payload;
6937
+ }
6938
+ function credspayload(label) {
6939
+ if (label.trim() === "") throw new Error("The confirmcreds gate names its credential label; the value never appears.");
6940
+ return { label: label.trim() };
6941
+ }
6942
+ function opengate(input) {
6943
+ if (input.stepid.trim() === "" || input.runid.trim() === "" || input.origin.trim() === "") throw new Error("The confirm gate needs its step, run and origin.");
6944
+ if (Object.keys(input.payload).length === 0) throw new Error("The confirm gate carries the payload the human reviews.");
6945
+ return { gateid: input.gateid ?? randomid(), kind: input.kind, stepid: input.stepid, runid: input.runid, origin: input.origin, payload: { ...input.payload }, state: "open", openedat: input.now };
6946
+ }
6947
+ function gatestateof(gates, stepid) {
6948
+ const gate = [...gates].reverse().find((candidate) => candidate.stepid === stepid);
6949
+ if (gate === void 0) return { state: "none" };
6950
+ return { state: gate.state, gate };
6951
+ }
6952
+ function resolvegate(input) {
6953
+ if (input.actor.trim() === "") throw new Error("The gate resolution names its acting user; only a human resolves a gate.");
6954
+ const gate = input.gates.find((candidate) => candidate.gateid === input.gateid);
6955
+ if (gate === void 0) return { gates: input.gates };
6956
+ if (gate.state !== "open") return { gates: input.gates };
6957
+ const resolution = { gateid: gate.gateid, kind: gate.kind, stepid: gate.stepid, decision: input.decision, actor: input.actor, at: input.now };
6958
+ return { gates: input.gates.map((candidate) => candidate.gateid === input.gateid ? { ...candidate, state: input.decision, resolvedat: input.now, actor: input.actor } : candidate), resolution };
6959
+ }
6960
+ function gateprompttext(gate) {
6961
+ if (gate.kind === "confirmpay") {
6962
+ const amount = gate.payload.amount !== void 0 ? `the amount ${gate.payload.amount}` : "an amount the step options name";
6963
+ const target = gate.payload.target !== void 0 ? ` on ${gate.payload.target}` : "";
6964
+ return `Approve the payment of ${amount} to ${gate.payload.payeeorigin}${target}? The step dispatches only after this distinct human action.`;
6965
+ }
6966
+ if (gate.kind === "confirmdelete") {
6967
+ const target = gate.payload.target !== void 0 ? ` on ${gate.payload.target}` : "";
6968
+ return `Approve the destructive delete${target} scoped to ${gate.payload.scope}? ${gate.payload.irreversibility} The step dispatches only after this distinct human action.`;
6969
+ }
6970
+ return `Approve the use of the credential ${gate.payload.label} on ${gate.origin}? The value stays behind the vault; the label is everything this prompt shows.`;
6971
+ }
6972
+ function gateforstep(input) {
6973
+ const kind = gatekindfor(input.classes);
6974
+ if (kind === void 0) return void 0;
6975
+ const options = stepoptions2(input.step);
6976
+ if (kind === "confirmpay") {
6977
+ const amount = typeof options.amount === "string" ? options.amount : typeof options.value === "string" ? options.value : void 0;
6978
+ return opengate({ kind, stepid: input.step.id, runid: input.runid, origin: input.origin, payload: paypayload({ ...amount !== void 0 && amount !== "" ? { amount } : {}, payeeorigin: String(options.payeeorigin ?? input.origin), ...input.step.target !== void 0 && input.step.target !== "" ? { target: input.step.target } : {} }), now: input.now });
6979
+ }
6980
+ if (kind === "confirmdelete") {
6981
+ return opengate({ kind, stepid: input.step.id, runid: input.runid, origin: input.origin, payload: deletepayload({ ...input.step.target !== void 0 && input.step.target !== "" ? { target: input.step.target } : {}, scope: String(options.scope ?? input.origin), irreversibility: String(options.irreversibility ?? "A destructive delete destroys state the page cannot restore.") }), now: input.now });
6982
+ }
6983
+ if (input.credentiallabel === void 0 || input.credentiallabel.trim() === "") return void 0;
6984
+ return opengate({ kind, stepid: input.step.id, runid: input.runid, origin: input.origin, payload: credspayload(input.credentiallabel), now: input.now });
6985
+ }
6986
+
6987
+ // phishguard.ts
6988
+ function stepoptions3(step) {
6989
+ if (!step.options) return {};
6990
+ try {
6991
+ const parsed = JSON.parse(step.options);
6992
+ return Boolean(parsed) && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
6993
+ } catch {
6994
+ return {};
6995
+ }
6996
+ }
6997
+ function credentialstep(step) {
6998
+ const credentialkinds2 = /* @__PURE__ */ new Set(["consentpassword", "saveapikey", "handleauth", "authflow", "fillcard", "fillcode"]);
6999
+ if (credentialkinds2.has(step.kind)) return true;
7000
+ const options = stepoptions3(step);
7001
+ const fields = Array.isArray(options.fields) ? options.fields.filter((item) => Boolean(item) && typeof item === "object") : [];
7002
+ const names = [...fields.map((field) => typeof field.name === "string" ? field.name : ""), typeof options.field === "string" ? options.field : "", step.target ?? ""].map((name) => name.toLowerCase());
7003
+ return names.some((name) => name.includes("password") || name.includes("passwd") || name.includes("passphrase") || name.includes("token") || name.includes("secret") || name.includes("apikey"));
7004
+ }
7005
+ function originlabels(origin) {
7006
+ const host = origin.trim().replace(/^[a-z][a-z0-9+.-]*:\/\//i, "").split("/")[0] ?? "";
7007
+ return host.split(".").filter((label) => label !== "").reverse();
7008
+ }
7009
+ function labeldistance(one, two) {
7010
+ const rows = one.length + 1;
7011
+ const columns = two.length + 1;
7012
+ let previous = Array.from({ length: columns }, (_, index) => index);
7013
+ for (let row = 1; row < rows; row += 1) {
7014
+ const current = [row, ...Array.from({ length: columns - 1 }, () => 0)];
7015
+ for (let column = 1; column < columns; column += 1) {
7016
+ const substitution = (previous[column - 1] ?? 0) + (one[row - 1] === two[column - 1] ? 0 : 1);
7017
+ current[column] = Math.min((previous[column] ?? 0) + 1, (current[column - 1] ?? 0) + 1, substitution);
7018
+ }
7019
+ previous = current;
7020
+ }
7021
+ return previous[columns - 1] ?? Math.max(one.length, two.length);
7022
+ }
7023
+ function lookalikedistance(one, two) {
7024
+ if (one.trim() === "" || two.trim() === "") return 1;
7025
+ if (one === two) return 0;
7026
+ const first = originlabels(one);
7027
+ const second = originlabels(two);
7028
+ const edits = labeldistance(first, second);
7029
+ const longest = Math.max(first.length, second.length);
7030
+ if (longest === 0) return 1;
7031
+ const distance = edits / longest;
7032
+ return Math.min(1, Math.max(0, distance));
7033
+ }
7034
+ function phishthresholdvalid(threshold) {
7035
+ if (!Number.isFinite(threshold) || threshold <= 0 || threshold >= 1) return { valid: false, reason: "The phishguard threshold stays a user choice between zero and one; the lookalike line never defaults." };
7036
+ return { valid: true, reason: `The lookalike threshold ${threshold} stays the user configured line a login origin crosses at its own risk.` };
7037
+ }
7038
+ function phishverdictof(input) {
7039
+ const threshold = phishthresholdvalid(input.threshold);
7040
+ if (!threshold.valid) throw new Error(threshold.reason);
7041
+ if (input.granted.includes(input.origin)) {
7042
+ return { origin: input.origin, distance: 0, threshold: input.threshold, blocked: false, reason: `The login origin ${input.origin} sits among the granted origins; no lookalike watch applies.`, at: input.now };
7043
+ }
7044
+ let matchedorigin;
7045
+ let distance = 1;
7046
+ for (const granted of input.granted) {
7047
+ const candidate = lookalikedistance(input.origin, granted);
7048
+ if (candidate < distance) {
7049
+ distance = candidate;
7050
+ matchedorigin = granted;
7051
+ }
7052
+ }
7053
+ if (matchedorigin !== void 0 && distance <= input.threshold) {
7054
+ return { origin: input.origin, matchedorigin, distance, threshold: input.threshold, blocked: true, reason: `The login origin ${input.origin} sits ${distance} away from the granted origin ${matchedorigin} and crosses the user threshold ${input.threshold}; the credential step blocks and the deny event names ${matchedorigin}.`, at: input.now };
7055
+ }
7056
+ return { origin: input.origin, ...matchedorigin !== void 0 ? { matchedorigin } : {}, distance, threshold: input.threshold, blocked: false, reason: matchedorigin !== void 0 ? `The login origin ${input.origin} sits ${distance} away from its closest granted origin ${matchedorigin} and stays under the user threshold ${input.threshold}.` : `The login origin ${input.origin} carries no granted origin to resemble; the watch records the first visit.`, at: input.now };
7057
+ }
7058
+
6352
7059
  // policy.ts
6353
7060
  var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts", "recordscreen", "captureaudio", "downloadimages", "callrest", "callgraphql", "sendmessage", "blockrequest", "mockresponse", "rewriteheaders", "setcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles", "attachcdp", "detachcdp", "cdpcmd", "overridescript", "heapshot", "profilecpu", "capturesourcemaps", "emulatedevice", "emulatenetwork", "emulatelocate", "setuseragent", "overridepermission", "restoresession", "exportsessions", "importsessions", "runworkflow", "visitrule", "urlrule", "menurule", "keyrule", "buttonrule", "cronrule", "intervalrule", "urllistrule", "webhookrule", "eventrule"]);
6354
7061
  var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction", "capturebodies", "setbreakpoint", "stepcode", "watchexpr", "loop", "repeatuntil", "whileloop", "foreach", "parallel", "trycatch"]);
@@ -9563,6 +10270,103 @@ function sandboxorigingate(input) {
9563
10270
  function environmentrequirements() {
9564
10271
  return environmentrequirementsof([...allowedactions]);
9565
10272
  }
10273
+ function automationallowlistgate(input) {
10274
+ const verdict = allowlistcheck({ origin: input.origin, allowlist: input.allowlist, ...input.session !== void 0 ? { sessionorigin: input.session.origin } : {} });
10275
+ if (!verdict.allowed) return { allowed: false, reason: verdict.reason };
10276
+ return { allowed: true, reason: verdict.reason };
10277
+ }
10278
+ function originprofilegate(input) {
10279
+ const verdict = profilegrade(input);
10280
+ if (!verdict.allowed) return { allowed: false, reason: verdict.reason };
10281
+ return { allowed: true, reason: verdict.reason };
10282
+ }
10283
+ function consentwindowgate(input) {
10284
+ if (!input.sensitive) return { allowed: true, reason: "The non-sensitive step rides the session origin grants and needs no consent window of its own." };
10285
+ const verdict = windowgatesstep({ window: input.window, sessionid: input.sessionid, origin: input.origin, now: input.now });
10286
+ if (!verdict.allowed) return { allowed: false, reason: verdict.reason };
10287
+ return { allowed: true, reason: verdict.reason };
10288
+ }
10289
+ function revokerungate(input) {
10290
+ if (input.revocation === void 0) return { allowed: true, reason: "No revocation halted the run; the steps keep their reviewed order." };
10291
+ if (input.revocation.sessionid !== input.sessionid) return { allowed: true, reason: "The revocation belongs to another session and halts nothing here." };
10292
+ if (input.revocation.runid !== input.runid) return { allowed: true, reason: "The revocation belongs to another run and halts nothing here." };
10293
+ 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(", ")}.` };
10294
+ }
10295
+ function sensitiveclassgate(input) {
10296
+ if (!input.sensitive) return { allowed: true, reason: "The step carries no sensitive class and needs no fresh consent prompt." };
10297
+ const verdict = missingclassconsents({ origin: input.origin, classes: input.classes, bydefault: input.bydefault, consents: input.consents, now: input.now });
10298
+ if (verdict.needed) return { allowed: false, reason: verdict.reason };
10299
+ return { allowed: true, reason: verdict.reason };
10300
+ }
10301
+ function consentdurationvalid(duration) {
10302
+ 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." };
10303
+ return { allowed: true, reason: `The consent window duration ${duration} milliseconds stays the user configured boundary the prompt names.` };
10304
+ }
10305
+ function logreadgate(input) {
10306
+ 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." };
10307
+ return { allowed: true, reason: "The log chain verifies from the genesis hash to the last entry; the audit accessor serves the entries." };
10308
+ }
10309
+ function schemaguardgate(input) {
10310
+ if (input.errors.length === 0) return { allowed: true, reason: "The inbound command matches its declared schemastrict grammar field by field." };
10311
+ const first = input.errors[0];
10312
+ return { allowed: false, reason: `${input.errors.length} schema error${input.errors.length === 1 ? "" : "s"} refuse the command before dispatch: ${input.errors.map((error) => error.reason).join(" ")}${first !== void 0 ? ` The first error sits at ${first.path} expecting ${first.expected}.` : ""}` };
10313
+ }
10314
+ function origincheckgate(input) {
10315
+ if (!input.verdict.accepted) return { allowed: false, reason: input.verdict.reason };
10316
+ return { allowed: true, reason: input.verdict.reason };
10317
+ }
10318
+ function ratelimitboundsvalid(limit, window2) {
10319
+ const bounds = bucketboundsvalid(limit, window2);
10320
+ if (!bounds.valid) return { allowed: false, reason: bounds.reason };
10321
+ return { allowed: true, reason: bounds.reason };
10322
+ }
10323
+ function confirmpaygate(input) {
10324
+ const kind = gatekindfor(input.classes);
10325
+ if (kind !== "confirmpay") return { allowed: true, reason: "The step carries no payment class and needs no confirmpay gate." };
10326
+ if (input.state === "resolved") return { allowed: true, reason: "The human resolved the confirmpay gate of the payment step; the step dispatches with its reviewed amount, payee origin and target." };
10327
+ if (input.state === "refused") return { allowed: false, reason: "The human refused the confirmpay gate of the payment step; the payment never dispatches." };
10328
+ if (input.state === "open") return { allowed: false, reason: "The confirmpay gate of the payment step stays open with the amount, the payee origin and the target element; the executor pauses until the human resolves it and no timeout ever resolves a gate." };
10329
+ return { allowed: false, reason: "The payment step opens its confirmpay gate with the amount, the payee origin and the target element; the executor pauses until one distinct human action resolves it." };
10330
+ }
10331
+ function confirmdeletegate(input) {
10332
+ const kind = gatekindfor(input.classes);
10333
+ if (kind !== "confirmdelete") return { allowed: true, reason: "The step carries no delete class and needs no confirmdelete gate." };
10334
+ if (input.state === "resolved") return { allowed: true, reason: "The human resolved the confirmdelete gate of the destructive step; the step dispatches with its reviewed target, scope and irreversibility." };
10335
+ if (input.state === "refused") return { allowed: false, reason: "The human refused the confirmdelete gate of the destructive step; the deletion never dispatches." };
10336
+ if (input.state === "open") return { allowed: false, reason: "The confirmdelete gate of the destructive step stays open with the target, the scope and the irreversibility; the executor pauses until the human resolves it and no timeout ever resolves a gate." };
10337
+ return { allowed: false, reason: "The destructive step opens its confirmdelete gate with the target, the scope and the irreversibility; the executor pauses until one distinct human action resolves it." };
10338
+ }
10339
+ function confirmcredsgate(input) {
10340
+ const kind = gatekindfor(input.classes);
10341
+ if (kind !== "confirmcreds") return { allowed: true, reason: "The step carries no credential class and needs no confirmcreds gate." };
10342
+ if (input.state === "resolved") return { allowed: true, reason: "The human resolved the confirmcreds gate of the credential step; the step reads its value from the vault at the last possible moment and no log records it." };
10343
+ if (input.state === "refused") return { allowed: false, reason: "The human refused the confirmcreds gate of the credential step; the credential never dispatches." };
10344
+ if (input.state === "open") return { allowed: false, reason: "The confirmcreds gate of the credential step stays open with its credential label only; the executor pauses until the human resolves it and no timeout ever resolves a gate." };
10345
+ return { allowed: false, reason: "The credential step opens its confirmcreds gate with its credential label only; the executor pauses until one distinct human action resolves it." };
10346
+ }
10347
+ function phishthresholdgate(threshold) {
10348
+ const verdict = phishthresholdvalid(threshold);
10349
+ if (!verdict.valid) return { allowed: false, reason: verdict.reason };
10350
+ return { allowed: true, reason: verdict.reason };
10351
+ }
10352
+ function phishguardgate(input) {
10353
+ if (input.verdict.blocked) return { allowed: false, reason: input.verdict.reason };
10354
+ return { allowed: true, reason: input.verdict.reason };
10355
+ }
10356
+ function safedefaultsgate(input) {
10357
+ if (input.profile !== void 0) return { allowed: true, reason: `The origin profile of ${input.profile.origin} exists; the safedefaults posture stays out of the decision.` };
10358
+ if (!input.sensitive) return { allowed: true, reason: "The non-sensitive step rides the reads only baseline of the safedefaults posture; the first visit grants reads alone." };
10359
+ return { allowed: false, reason: `No origin profile exists and the safedefaults posture denies the sensitive ${input.classes.length > 0 ? input.classes.join(" and ") : "by default"} step; open the originprofile editor to widen the profile the user controls.` };
10360
+ }
10361
+ function vaultsecretgate(input) {
10362
+ if (input.leaks.length > 0) return { allowed: false, reason: `The plan carries ${input.leaks.length} plaintext secret value${input.leaks.length === 1 ? "" : "s"} that digest to vault records; secrets never ride step options, variables or plan texts, only the vault holds them.` };
10363
+ if (input.carries) return { allowed: false, reason: "The step types a raw value into a masked field shape; credential steps read their value from the vault at the last possible moment and never carry it in the options." };
10364
+ return { allowed: true, reason: "The step and the plan carry no secret outside the vault; the values stay behind the seam." };
10365
+ }
10366
+ function untrustedrendergate(input) {
10367
+ if (input.environment === "sandboxframe") return { allowed: true, reason: "The extracted markup renders inside the sandboxframe under its nonce with scripts and handlers stripped; the untrusted content never reenters the page context." };
10368
+ return { allowed: false, reason: `The extracted markup grades untrusted and refuses to render inside the ${input.environment}; every untrusted render routes through the sandboxframe.` };
10369
+ }
9566
10370
 
9567
10371
  // progress.ts
9568
10372
  function emptyprogress(planid, now) {
@@ -9585,6 +10389,10 @@ function recordturnaround2(progress, planid, stepid, milliseconds, now) {
9585
10389
  const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
9586
10390
  return { ...base, turnarounds: { ...base.turnarounds ?? {}, [stepid]: milliseconds }, updatedat: now };
9587
10391
  }
10392
+ function recordgatewait(progress, planid, stepid, entry, now) {
10393
+ const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
10394
+ return { ...base, gatewaits: { ...base.gatewaits ?? {}, [stepid]: entry }, updatedat: now };
10395
+ }
9588
10396
  function iscomplete(progress, plan) {
9589
10397
  if (!progress || progress.planid !== plan.id) return false;
9590
10398
  const required = plan.steps.map((step) => step.id);
@@ -9748,9 +10556,68 @@ function recordtoolcall(progress, planid, stepid, entry, now) {
9748
10556
  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
10557
  return recordoutcome(base, planid, outcome, now);
9750
10558
  }
10559
+ function recorddenied(progress, planid, stepid, entry, now) {
10560
+ const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
10561
+ const outcome = { stepid, ok: false, summary: `The ${entry.kind} step on ${entry.origin} was denied: ${entry.reason}`, details: { denied: entry }, at: now };
10562
+ return recordoutcome(base, planid, outcome, now);
10563
+ }
10564
+ function recordrevocation(progress, planid, stepid, entry, now) {
10565
+ const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
10566
+ 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 };
10567
+ return recordoutcome(base, planid, outcome, now);
10568
+ }
10569
+
10570
+ // maskinputs.ts
10571
+ var defaultmaskshapes = ["password", "token", "card", "secret"];
10572
+ var maskmarker = "[redacted]";
10573
+ function fieldshapekind(name) {
10574
+ const lowered = name.toLowerCase();
10575
+ if (lowered.includes("password") || lowered.includes("passwd") || lowered.includes("pwd") || lowered.includes("passphrase")) return "password";
10576
+ if (lowered.includes("token") || lowered.includes("apikey") || lowered.includes("api_key") || lowered.includes("auth") || lowered.includes("bearer")) return "token";
10577
+ if (lowered.includes("card") || lowered.includes("cvc") || lowered.includes("cvv") || lowered.includes("expiry") || lowered.includes("pan")) return "card";
10578
+ if (lowered.includes("secret")) return "secret";
10579
+ return void 0;
10580
+ }
10581
+ function shapesof(input) {
10582
+ const shapes = new Set(defaultmaskshapes);
10583
+ for (const shape of input.settings?.maskshapes ?? []) if (shape.trim() !== "") shapes.add(shape.trim().toLowerCase());
10584
+ for (const rule of input.rules) {
10585
+ const scoped = rule.origin === void 0 || rule.origin === "" || input.origin !== void 0 && rule.origin === input.origin;
10586
+ if (scoped) {
10587
+ for (const shape of rule.shapes) if (shape.trim() !== "") shapes.add(shape.trim().toLowerCase());
10588
+ }
10589
+ }
10590
+ return [...shapes];
10591
+ }
10592
+ function maskingfield(name, shapes) {
10593
+ if (fieldshapekind(name) !== void 0) return true;
10594
+ const lowered = name.toLowerCase();
10595
+ return shapes.some((shape) => shape !== "" && lowered.includes(shape));
10596
+ }
10597
+ function maskvalue(value) {
10598
+ return value === "" ? "" : maskmarker;
10599
+ }
10600
+ function maskfield(input) {
10601
+ return maskingfield(input.name, input.shapes) ? maskvalue(input.value) : input.value;
10602
+ }
10603
+ function maskrecord(record2, shapes) {
10604
+ const masked = {};
10605
+ for (const [key, value] of Object.entries(record2)) {
10606
+ if (typeof value === "string") {
10607
+ const sibling = record2.name;
10608
+ masked[key] = key === "value" && typeof sibling === "string" ? maskfield({ name: sibling, value, shapes }) : maskfield({ name: key, value, shapes });
10609
+ } else if (Array.isArray(value)) masked[key] = value.map((item) => Boolean(item) && typeof item === "object" && !Array.isArray(item) ? maskrecord(item, shapes) : item);
10610
+ else if (Boolean(value) && typeof value === "object") masked[key] = maskrecord(value, shapes);
10611
+ else masked[key] = value;
10612
+ }
10613
+ return masked;
10614
+ }
10615
+ function maskexport(record2, shapes) {
10616
+ return maskrecord(record2, shapes);
10617
+ }
9751
10618
 
9752
10619
  // version.ts
9753
- var packageversion = "1.1.60";
10620
+ var packageversion = "1.1.62";
9754
10621
 
9755
10622
  // types.ts
9756
10623
  var protocolversion = packageversion;
@@ -10724,6 +11591,9 @@ function environmentreport(input) {
10724
11591
  ...input.keepalive !== void 0 ? { keepalive: input.keepalive } : {}
10725
11592
  };
10726
11593
  }
11594
+ function transparencyreport(input) {
11595
+ return { version: protocolversion, posture: "denydefault", grants: input.grants, windows: input.windows, connectallow: input.connectallow, permdiffs: input.permdiffs, safedefaults: input.safedefaults, vault: input.vault };
11596
+ }
10727
11597
 
10728
11598
  // capture.ts
10729
11599
  var capturekinds = ["shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet"];
@@ -11227,7 +12097,7 @@ async function readcapabilities() {
11227
12097
  ]);
11228
12098
  return { tabs, downloads, clipboardread, clipboardwrite, reportedat: Date.now() };
11229
12099
  }
11230
- function stepoptions(step) {
12100
+ function stepoptions4(step) {
11231
12101
  if (!step.options) return {};
11232
12102
  try {
11233
12103
  const parsed = JSON.parse(step.options);
@@ -11240,7 +12110,7 @@ function tabid(step) {
11240
12110
  return Number.parseInt(step.value ?? "", 10);
11241
12111
  }
11242
12112
  async function runbrowseraction(step, sessiontabid, windowid) {
11243
- const options = stepoptions(step);
12113
+ const options = stepoptions4(step);
11244
12114
  switch (step.kind) {
11245
12115
  case "tablist": {
11246
12116
  const tabs = await chrome.tabs.query({});
@@ -12700,6 +13570,144 @@ function acceptrenderresult(input) {
12700
13570
  return { accepted: true, result, reason: `The render result of the step ${render.stepid} answers the nonce of its render; the text stays inside the frame.` };
12701
13571
  }
12702
13572
 
13573
+ // secretvault.ts
13574
+ var vaultdigestprefix = "sha256:";
13575
+ async function vaultdigestof(value) {
13576
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
13577
+ return vaultdigestprefix + [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
13578
+ }
13579
+ function vaultentryof(input) {
13580
+ if (input.label.trim() === "") throw new Error("The vault record needs its label; the surfaces show the label only.");
13581
+ if (input.scope.trim() === "") throw new Error("The vault record needs its exact origin scope; a secret never rides every origin.");
13582
+ if (!input.digest.startsWith(vaultdigestprefix)) throw new Error("The vault record carries its sha-256 digest, never its value.");
13583
+ return { vaultid: input.vaultid ?? randomid(), label: input.label.trim(), scope: input.scope.trim(), profileid: input.profileid, provenance: input.provenance, algorithm: "sha-256", digest: input.digest, createdat: input.now };
13584
+ }
13585
+ function inmemoryvault() {
13586
+ const values = /* @__PURE__ */ new Map();
13587
+ return {
13588
+ put: async (vaultid, value) => {
13589
+ values.set(vaultid, value);
13590
+ },
13591
+ fetch: async (vaultid) => values.get(vaultid),
13592
+ drop: async (vaultid) => {
13593
+ values.delete(vaultid);
13594
+ }
13595
+ };
13596
+ }
13597
+ async function vaultstore(input) {
13598
+ if (input.value === "") throw new Error("The vault stores a secret value the user supplied; an empty value stores nothing.");
13599
+ const entry = vaultentryof({ label: input.label, scope: input.scope, profileid: input.profileid, provenance: input.provenance, digest: await vaultdigestof(input.value), now: input.now });
13600
+ await input.seam.put(entry.vaultid, input.value);
13601
+ return entry;
13602
+ }
13603
+ async function vaultvaluefor(input) {
13604
+ const value = await input.seam.fetch(input.entry.vaultid);
13605
+ if (value === void 0) return { ok: false, reason: `The vault holds no value behind the label ${input.entry.label}; add the secret again.` };
13606
+ return { ok: true, value, reason: `The vault released the value behind the label ${input.entry.label} at the last possible moment; the value reaches the credential field only and no log records it.` };
13607
+ }
13608
+ async function vaultdelete(input) {
13609
+ await input.seam.drop(input.entry.vaultid);
13610
+ return { dropped: true, label: input.entry.label, reason: `The vault dropped the secret ${input.entry.label} of ${input.entry.scope}; no value and no copy remains behind the seam.` };
13611
+ }
13612
+ async function secretleakscan(input) {
13613
+ const leaks = [];
13614
+ for (const candidate of input.candidates) {
13615
+ if (candidate.trim() === "") continue;
13616
+ const digest = await vaultdigestof(candidate);
13617
+ if (input.entries.some((entry) => entry.digest === digest)) leaks.push(candidate);
13618
+ }
13619
+ if (leaks.length > 0) return { leaks, reason: `The plan carries ${leaks.length} plaintext value${leaks.length === 1 ? "" : "s"} that digest to vault records; secrets never ride step options, variables or plan texts, only the vault holds them.` };
13620
+ return { leaks: [], reason: "No candidate value digests to a vault record; the plan carries no leaked secret." };
13621
+ }
13622
+ function stepoptions5(step) {
13623
+ if (!step.options) return {};
13624
+ try {
13625
+ const parsed = JSON.parse(step.options);
13626
+ return Boolean(parsed) && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
13627
+ } catch {
13628
+ return {};
13629
+ }
13630
+ }
13631
+ function secretshapecarrying(step) {
13632
+ const options = stepoptions5(step);
13633
+ const fields = Array.isArray(options.fields) ? options.fields.filter((item) => Boolean(item) && typeof item === "object") : [];
13634
+ const rawfield = fields.find((field) => typeof field.name === "string" && typeof field.value === "string" && field.value !== "" && maskingfield(field.name, []));
13635
+ if (rawfield !== void 0) return { carries: true, reason: `The ${step.kind} step types a raw value into the ${String(rawfield.name)} field; credential steps read their value from the vault at the last possible moment and never carry it in the options.` };
13636
+ if (typeof options.field === "string" && maskingfield(options.field, []) && step.value !== void 0 && step.value !== "") return { carries: true, reason: `The ${step.kind} step types a raw value into the ${options.field} field; credential steps read their value from the vault at the last possible moment and never carry it in the options.` };
13637
+ return { carries: false, reason: "The step carries no raw value behind a masked field shape." };
13638
+ }
13639
+ function vaultview(entries) {
13640
+ return entries.map((entry) => ({ vaultid: entry.vaultid, label: entry.label, scope: entry.scope, provenance: entry.provenance, createdat: entry.createdat, ...entry.lastusedat !== void 0 ? { lastusedat: entry.lastusedat } : {} }));
13641
+ }
13642
+
13643
+ // redactshots.ts
13644
+ function regionof(input) {
13645
+ if (input.origin.trim() === "" || input.template.trim() === "") throw new Error("The redact region needs its origin and its page template.");
13646
+ for (const value of [input.x, input.y, input.width, input.height]) {
13647
+ if (!Number.isFinite(value) || value < 0) throw new Error("The redact region needs finite, non-negative geometry in css pixels.");
13648
+ }
13649
+ if (input.width <= 0 || input.height <= 0) throw new Error("The redact region needs a positive width and height so the mask covers a real area.");
13650
+ if (input.reason.trim() === "") throw new Error("The redact region names its reason in plain language.");
13651
+ return { id: input.id ?? randomid(), origin: input.origin.trim(), template: input.template.trim(), x: input.x, y: input.y, width: input.width, height: input.height, reason: input.reason.trim(), source: input.source, createdat: input.now };
13652
+ }
13653
+ function regionsfor(regions, origin, template) {
13654
+ return regions.filter((region) => region.origin === origin && region.template === template);
13655
+ }
13656
+ function templateof(step) {
13657
+ if (step.options) {
13658
+ try {
13659
+ const parsed = JSON.parse(step.options);
13660
+ if (Boolean(parsed) && typeof parsed === "object" && !Array.isArray(parsed)) {
13661
+ const template = parsed.template;
13662
+ if (typeof template === "string" && template.trim() !== "") return template.trim();
13663
+ }
13664
+ } catch {
13665
+ }
13666
+ }
13667
+ return step.kind;
13668
+ }
13669
+ function redactedshot(record2, regions) {
13670
+ if (regions.length === 0) return record2;
13671
+ return { ...record2, redacted: true, redactedregions: regions.length };
13672
+ }
13673
+ function redactionsummary(regions) {
13674
+ if (regions.length === 0) return "No redact region covered the capture; the stored bytes carry everything the surface saw.";
13675
+ const sources = { fieldshape: 0, userdrawn: 0 };
13676
+ for (const region of regions) sources[region.source] += 1;
13677
+ return `${regions.length} redact region${regions.length === 1 ? "" : "s"} covered the capture before storage: ${sources.fieldshape} derived from sensitive field shapes and ${sources.userdrawn} drawn by the user (${regions.map((region) => region.reason).join("; ")}).`;
13678
+ }
13679
+
13680
+ // transparency.ts
13681
+ function permissiondiff(input) {
13682
+ if (input.fromversion.trim() === "" || input.toversion.trim() === "") throw new Error("The permdiff names the two versions it compares.");
13683
+ const added = [...new Set(input.to.filter((permission) => !input.from.includes(permission)))];
13684
+ const removed = [...new Set(input.from.filter((permission) => !input.to.includes(permission)))];
13685
+ return { fromversion: input.fromversion, toversion: input.toversion, added, removed, computedat: input.now };
13686
+ }
13687
+ function permdiffchanged(diff) {
13688
+ return diff.added.length > 0 || diff.removed.length > 0;
13689
+ }
13690
+ function permdiffsummary(diff) {
13691
+ if (!permdiffchanged(diff)) return `The update from ${diff.fromversion} to ${diff.toversion} changed no permission.`;
13692
+ const parts = [];
13693
+ if (diff.added.length > 0) parts.push(`added ${diff.added.join(", ")}`);
13694
+ if (diff.removed.length > 0) parts.push(`removed ${diff.removed.join(", ")}`);
13695
+ return `The update from ${diff.fromversion} to ${diff.toversion} ${parts.join(" and ")}.`;
13696
+ }
13697
+ function transparencygrants(input) {
13698
+ const grants = input.allowlist.map((entry) => ({ origin: entry.origin, scope: `automation allowlist of the profile workspace ${entry.profileid}`, boundary: "the user revokes the entry or the profile workspace", grantedat: entry.grantedat }));
13699
+ for (const profile of input.profiles) {
13700
+ grants.push({ origin: profile.origin, scope: `origin profile with ${profile.grants.length} granted and ${profile.denials.length} denied kinds`, boundary: "the user edits or revokes the profile", grantedat: profile.createdat });
13701
+ }
13702
+ return grants;
13703
+ }
13704
+ function windowhistory(windows) {
13705
+ return windows.map((window2) => ({ id: window2.id, origin: window2.origin, state: window2.state, boundary: window2.boundary, startedat: window2.startedat, expiresat: window2.expiresat }));
13706
+ }
13707
+ function connectallowlist(entries) {
13708
+ return entries.map((entry) => ({ senderid: entry.senderid, displayname: entry.displayname, ...entry.origin !== void 0 ? { origin: entry.origin } : {}, addedat: entry.addedat }));
13709
+ }
13710
+
12703
13711
  // modelroute.ts
12704
13712
  function routevalid(route) {
12705
13713
  if (route.kind.trim() === "") return { allowed: false, reason: "The model route needs its task kind." };
@@ -13906,13 +14914,28 @@ var chromestorage = {
13906
14914
  }
13907
14915
  };
13908
14916
  var memory = new sessionmemory(chromestorage);
14917
+ var vaultseamstore = (() => {
14918
+ const session = chrome.storage?.session;
14919
+ if (session) {
14920
+ return {
14921
+ put: async (vaultid, value) => {
14922
+ await session.set({ [`vault:${vaultid}`]: value });
14923
+ },
14924
+ fetch: async (vaultid) => (await session.get(`vault:${vaultid}`))[`vault:${vaultid}`],
14925
+ drop: async (vaultid) => {
14926
+ await session.remove(`vault:${vaultid}`);
14927
+ }
14928
+ };
14929
+ }
14930
+ return inmemoryvault();
14931
+ })();
13909
14932
  function extensionpage(sender) {
13910
14933
  return sender.id === chrome.runtime.id && Boolean(sender.url?.startsWith(chrome.runtime.getURL("")));
13911
14934
  }
13912
14935
  async function audit(kind, summary, extra = {}) {
13913
14936
  await memory.addaudi({ id: randomid(), kind, at: Date.now(), summary, ...extra });
13914
14937
  }
13915
- function stepoptions2(step) {
14938
+ function stepoptions6(step) {
13916
14939
  try {
13917
14940
  return parseoptions(step);
13918
14941
  } catch {
@@ -14119,6 +15142,189 @@ async function closeplanrun(planid, sessionid) {
14119
15142
  await closeoffscreendocument(planid);
14120
15143
  await audit("environment", `The run state of the plan ${planid} closed at its terminal state and the keepalive port released.`, { ...sessionid !== "" ? { sessionid } : {}, planid });
14121
15144
  }
15145
+ async function appendrunevent(kind, summary, session, origin, stepid) {
15146
+ if (!session) return;
15147
+ const now = Date.now();
15148
+ let log = await memory.getimmutablelog(session.id);
15149
+ if (!log) {
15150
+ log = openrunlog({ runid: session.id, sessionid: session.id, now });
15151
+ await memory.trackimmutablelog(session.id);
15152
+ }
15153
+ log = await appendlogentry({ log, kind, summary, origin, ...stepid !== void 0 ? { stepid } : {}, at: now });
15154
+ await memory.setimmutablelog(log);
15155
+ }
15156
+ async function securitystepgate(step, session, origin, settings) {
15157
+ const classification = sensitiveclassesof(step);
15158
+ if (!session) return { allowed: true, suspended: false, reason: "The step runs behind the session review chain; a sessionless preview never dispatches.", classification };
15159
+ const now = Date.now();
15160
+ const allowverdict = automationallowlistgate({ origin, allowlist: await memory.getautomationallowlist(), session });
15161
+ if (!allowverdict.allowed) return { allowed: false, suspended: false, reason: allowverdict.reason ?? "", classification };
15162
+ const profile = (await memory.getoriginprofiles()).find((candidate) => candidate.origin === origin);
15163
+ if (profile === void 0) {
15164
+ await memory.addsafedefaultapplication({ origin, firstseenat: now }).catch(() => {
15165
+ });
15166
+ const safedefaultverdict = safedefaultsgate({ profile, classes: classification.classes, sensitive: classification.sensitive });
15167
+ if (!safedefaultverdict.allowed) return { allowed: false, suspended: false, reason: safedefaultverdict.reason ?? "", classification };
15168
+ }
15169
+ const profileverdict = originprofilegate({ profile, kind: step.kind, sensitive: classification.sensitive });
15170
+ if (!profileverdict.allowed) return { allowed: false, suspended: false, reason: profileverdict.reason ?? "", classification };
15171
+ const windows = await memory.expireconsentwindows(now);
15172
+ const window2 = windows.find((candidate) => candidate.state === "active" && candidate.sessionid === session.id && candidate.origin === origin);
15173
+ const windowverdict = consentwindowgate({ window: window2, sessionid: session.id, origin, sensitive: classification.sensitive, now });
15174
+ if (!windowverdict.allowed) return { allowed: false, suspended: windowverdict.reason?.includes("suspends") ?? false, reason: windowverdict.reason ?? "", classification };
15175
+ const consentverdict = sensitiveclassgate({ origin, classes: classification.classes, bydefault: classification.bydefault, sensitive: classification.sensitive, consents: await memory.getclassconsents(), now });
15176
+ if (!consentverdict.allowed) return { allowed: false, suspended: false, reason: `${classification.reason} ${consentverdict.reason ?? ""}`, classification };
15177
+ const plan = await memory.getplan();
15178
+ const revocation = plan === void 0 ? void 0 : (await memory.getrevocations()).find((candidate) => candidate.sessionid === session.id && candidate.runid === plan.id);
15179
+ const revokeverdict = revokerungate({ revocation, sessionid: session.id, runid: plan?.id ?? "" });
15180
+ if (!revokeverdict.allowed) return { allowed: false, suspended: false, reason: revokeverdict.reason ?? "", classification };
15181
+ const confirmverdict = await confirmgatechain(step, session, plan, origin, classification, now);
15182
+ if (confirmverdict !== void 0) return { allowed: confirmverdict.allowed, suspended: false, reason: confirmverdict.reason, classification };
15183
+ return { allowed: true, suspended: false, reason: `${classification.reason} ${allowverdict.reason ?? ""} ${windowverdict.reason ?? ""} ${consentverdict.reason ?? ""}`, classification };
15184
+ }
15185
+ async function confirmgatechain(step, session, plan, origin, classification, now) {
15186
+ if (!session || !plan) return void 0;
15187
+ const kind = gatekindfor(classification.classes);
15188
+ if (kind !== void 0) {
15189
+ const gates = await memory.getgates();
15190
+ const state = gatestateof(gates, step.id);
15191
+ if (state.state === "none") {
15192
+ const vaultentries2 = await memory.getsecretvault();
15193
+ const vaultlabel = vaultentries2.find((entry) => entry.scope === origin)?.label;
15194
+ const gate = gateforstep({ step, classes: classification.classes, runid: plan.id, origin, credentiallabel: vaultlabel ?? `the credential the ${step.kind} step reviews`, now });
15195
+ if (gate) {
15196
+ await memory.savegate(gate);
15197
+ await appendrunevent("gate", `The ${gate.kind} gate opened for the ${step.kind} step ${step.id} on ${origin}: ${gateprompttext(gate)}`, session, origin, step.id).catch(() => {
15198
+ });
15199
+ await audit("gate", `The ${gate.kind} gate opened for the ${step.kind} step ${step.id} on ${origin}; the executor pauses until one distinct human action resolves it and no timeout ever resolves a gate.`, { sessionid: session.id, planid: plan.id, stepid: step.id });
15200
+ return { allowed: false, reason: `The ${gate.kind} gate opened for the ${step.kind} step ${step.id} on ${origin}: ${gateprompttext(gate)} The executor pauses until the human resolves it.` };
15201
+ }
15202
+ }
15203
+ const gateverdict = kind === "confirmpay" ? confirmpaygate({ classes: classification.classes, state: state.state }) : kind === "confirmdelete" ? confirmdeletegate({ classes: classification.classes, state: state.state }) : confirmcredsgate({ classes: classification.classes, state: state.state });
15204
+ if (!gateverdict.allowed) return { allowed: false, reason: gateverdict.reason ?? "" };
15205
+ if (state.state === "resolved" && state.gate !== void 0 && state.gate.resolvedat !== void 0) {
15206
+ await memory.setprogress(recordgatewait(await memory.getprogress(), plan.id, step.id, { gateid: state.gate.gateid, kind: state.gate.kind, openedat: state.gate.openedat, resolvedat: state.gate.resolvedat, waitedms: Math.max(0, state.gate.resolvedat - state.gate.openedat) }, now)).catch(() => {
15207
+ });
15208
+ }
15209
+ }
15210
+ if (credentialstep(step)) {
15211
+ const settings = await memory.getsettings();
15212
+ const threshold = settings?.phishdistance;
15213
+ if (threshold !== void 0) {
15214
+ const thresholdgate = phishthresholdgate(threshold);
15215
+ if (!thresholdgate.allowed) return { allowed: false, reason: thresholdgate.reason ?? "" };
15216
+ const live = await memory.expirephishverdicts(settings?.phishfreshness, now);
15217
+ const stored = live.find((verdict2) => verdict2.origin === origin);
15218
+ const verdict = stored ?? phishverdictof({ origin, granted: [.../* @__PURE__ */ new Set([...(await memory.getautomationallowlist()).map((entry) => entry.origin), session.origin])], threshold, now });
15219
+ if (stored === void 0) await memory.addphishverdict(verdict);
15220
+ const phishgate = phishguardgate({ verdict });
15221
+ if (!phishgate.allowed) {
15222
+ await appendrunevent("phish", `The phishguard blocked the credential step ${step.id} on ${origin}: ${verdict.reason}`, session, origin, step.id).catch(() => {
15223
+ });
15224
+ await audit("phish", `The phishguard blocked the ${step.kind} step ${step.id} on ${origin}: ${verdict.reason}`, { sessionid: session.id, planid: plan.id, stepid: step.id });
15225
+ return { allowed: false, reason: phishgate.reason ?? "" };
15226
+ }
15227
+ }
15228
+ }
15229
+ const buckets = await memory.getratelimitbuckets();
15230
+ const bucket = buckets.find((candidate) => candidate.origin === origin && candidate.sessionid === session.id);
15231
+ if (bucket !== void 0) {
15232
+ const consumed = bucketconsume({ bucket, now });
15233
+ if (!consumed.allowed) {
15234
+ const deferred = deferredeventof({ stepid: step.id, kind: step.kind, origin, reason: consumed.reason, resetsat: consumed.resetsat, now });
15235
+ await memory.adddeferredevent(deferred);
15236
+ await appendrunevent("suspend", `The ratelimit bucket of ${origin} deferred the ${step.kind} step ${step.id} until the window resets at ${deferred.resetsat}: ${consumed.reason}`, session, origin, step.id).catch(() => {
15237
+ });
15238
+ await audit("defer", `The ratelimit bucket of ${origin} deferred the ${step.kind} step ${step.id} until the window resets at ${deferred.resetsat}; the bounds stay user configured choices with no hidden ceiling.`, { sessionid: session.id, planid: plan.id, stepid: step.id });
15239
+ return { allowed: false, reason: consumed.reason };
15240
+ }
15241
+ await memory.saveratelimitbucket(consumed.bucket);
15242
+ }
15243
+ const vaultentries = await memory.getsecretvault();
15244
+ const options = stepoptions6(step);
15245
+ const candidates = [step.value ?? "", ...Object.values(options).filter((value) => typeof value === "string")];
15246
+ const leakscan = await secretleakscan({ candidates, entries: vaultentries });
15247
+ const secretverdict = vaultsecretgate({ leaks: leakscan.leaks, carries: secretshapecarrying(step).carries });
15248
+ if (!secretverdict.allowed) {
15249
+ await audit("vault", `The vault secret scan refused the ${step.kind} step ${step.id} on ${origin}: ${secretverdict.reason}`, { sessionid: session.id, planid: plan.id, stepid: step.id });
15250
+ return { allowed: false, reason: secretverdict.reason ?? "" };
15251
+ }
15252
+ return void 0;
15253
+ }
15254
+ async function resolvevaultvalues(step, session) {
15255
+ void session;
15256
+ const marker = /^vault:[A-Za-z0-9-]+$/;
15257
+ if (step.options === void 0 || !step.options.includes("vault:")) return step;
15258
+ const entries = await memory.getsecretvault();
15259
+ let options = step.options;
15260
+ try {
15261
+ const parsed = JSON.parse(step.options);
15262
+ if (Boolean(parsed) && typeof parsed === "object" && !Array.isArray(parsed)) {
15263
+ const record2 = parsed;
15264
+ const resolvevalue = async (value) => {
15265
+ if (!marker.test(value)) return value;
15266
+ const entry = entries.find((candidate) => candidate.vaultid === value.slice("vault:".length));
15267
+ if (!entry) return value;
15268
+ const fetched = await vaultvaluefor({ seam: vaultseamstore, entry });
15269
+ if (fetched.ok && fetched.value !== void 0) {
15270
+ await memory.stampsecretuse(entry.vaultid, Date.now());
15271
+ return fetched.value;
15272
+ }
15273
+ return value;
15274
+ };
15275
+ for (const [key, value] of Object.entries(record2)) if (typeof value === "string") record2[key] = await resolvevalue(value);
15276
+ if (Array.isArray(record2.fields)) {
15277
+ for (const field of record2.fields) {
15278
+ if (!Boolean(field) || typeof field !== "object" || Array.isArray(field)) continue;
15279
+ const fieldrecord = field;
15280
+ if (typeof fieldrecord.value === "string") fieldrecord.value = await resolvevalue(fieldrecord.value);
15281
+ }
15282
+ }
15283
+ options = JSON.stringify(record2);
15284
+ }
15285
+ } catch {
15286
+ }
15287
+ if (options === step.options) return step;
15288
+ return { ...step, options };
15289
+ }
15290
+ async function sealsessionrunlog(sessionid) {
15291
+ const log = await memory.getimmutablelog(sessionid);
15292
+ if (!log || log.seal !== void 0 || log.entries.length === 0) return;
15293
+ const sealed = await sealrunlog(log, Date.now());
15294
+ await memory.setimmutablelog(sealed.log);
15295
+ 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 });
15296
+ }
15297
+ async function securityviewof() {
15298
+ const now = Date.now();
15299
+ const settings = await memory.getsettings();
15300
+ const session = await memory.getsession();
15301
+ const logs = await memory.listimmutablelogs();
15302
+ const chain = [];
15303
+ for (const log of logs) chain.push(await chainreportof(log));
15304
+ return {
15305
+ allowlist: await memory.getautomationallowlist(),
15306
+ profiles: await memory.getoriginprofiles(),
15307
+ windows: await memory.expireconsentwindows(now),
15308
+ consents: await memory.getclassconsents(),
15309
+ revocations: await memory.getrevocations(),
15310
+ maskrules: await memory.getmaskrules(),
15311
+ chain,
15312
+ posture: "denydefault",
15313
+ gates: await memory.getgates(),
15314
+ resolutions: await memory.getgateresolutions(),
15315
+ deferred: await memory.getdeferredevents(),
15316
+ phishverdicts: await memory.getphishverdicts(),
15317
+ vault: await memory.getsecretvault(),
15318
+ connectallow: await memory.getconnectallow(),
15319
+ safedefaults: await memory.getsafedefaultapplications(),
15320
+ redactregions: await memory.getredactregions(),
15321
+ ...session ? { sessionorigin: session.origin } : {},
15322
+ ...settings?.consentduration !== void 0 ? { promptduration: settings.consentduration } : {},
15323
+ ...settings?.logretention !== void 0 ? { logretention: settings.logretention } : {},
15324
+ ...settings?.maskshapes !== void 0 ? { maskshapes: settings.maskshapes } : {},
15325
+ ...settings?.phishdistance !== void 0 ? { phishdistance: settings.phishdistance } : {}
15326
+ };
15327
+ }
14122
15328
  async function executeisolatedevaluate(step, tabid2, origin) {
14123
15329
  const injection = isolatedinjection(step);
14124
15330
  const result = await chrome.scripting.executeScript({ target: { tabId: tabid2 }, world: "ISOLATED", func: (code, args, expectedorigin) => {
@@ -14138,9 +15344,11 @@ async function executeisolatedevaluate(step, tabid2, origin) {
14138
15344
  return result[0]?.result ?? { ok: false, summary: "The isolated world returned no result." };
14139
15345
  }
14140
15346
  async function executesandboxrender(step, session, plan, origin) {
14141
- const options = stepoptions2(step);
15347
+ const options = stepoptions6(step);
14142
15348
  const markup = typeof options.markup === "string" ? options.markup : "";
14143
15349
  const sourceorigin = typeof options.sourceorigin === "string" ? options.sourceorigin : origin;
15350
+ const rendergate = untrustedrendergate({ environment: "sandboxframe" });
15351
+ if (!rendergate.allowed) throw new Error(rendergate.reason);
14144
15352
  const settings = await memory.getsettings();
14145
15353
  const origingate = sandboxorigingate({ origin: sourceorigin, allowed: settings?.sandboxorigins ?? [] });
14146
15354
  if (!origingate.allowed) throw new Error(origingate.reason);
@@ -14172,7 +15380,7 @@ async function offloadparsetoworker(step, output, session, plan, origin) {
14172
15380
  const ready = await ensureoffscreendocument(runid);
14173
15381
  if (!ready) return { output, turnaround: void 0 };
14174
15382
  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: stepoptions2(step), sentat: Date.now() });
15383
+ const request = workerrequestof({ id: randomid(), runid, stepid: step.id, kind: step.kind, payload, options: stepoptions6(step), sentat: Date.now() });
14176
15384
  const started = Date.now();
14177
15385
  let answer;
14178
15386
  try {
@@ -14263,7 +15471,14 @@ async function startsession() {
14263
15471
  const { tab, origin } = await activecontext();
14264
15472
  const session = { id: randomid(), tabid: tab.id, origin, startedat: Date.now(), expiresat: Date.now() + sessionduration, grants: [origin] };
14265
15473
  await memory.setsession(session);
15474
+ await memory.addallowlistorigin({ origin, profileid: runstateprofile, grantedat: session.startedat });
15475
+ const scope = scopegrantof({ origin, kinds: ["observe"], boundary: `the session expiry at ${new Date(session.expiresat).toISOString()}`, now: session.startedat });
15476
+ let runlog = openrunlog({ runid: session.id, sessionid: session.id, now: session.startedat });
15477
+ 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 });
15478
+ await memory.trackimmutablelog(session.id);
15479
+ await memory.setimmutablelog(runlog);
14266
15480
  await audit("session", `Session started for ${origin}.`, { sessionid: session.id });
15481
+ await audit("grant", `The consentscope grant of ${origin} was written into the immutable log with the boundary ${scope.boundary}.`, { sessionid: session.id });
14267
15482
  const policy = await memory.getdialogpolicy();
14268
15483
  if (policy) {
14269
15484
  try {
@@ -14368,7 +15583,7 @@ function stepauditkind(step, ok) {
14368
15583
  return ok ? "action" : "error";
14369
15584
  }
14370
15585
  function resolvedinnerstep(step, plan) {
14371
- const options = stepoptions2(step);
15586
+ const options = stepoptions6(step);
14372
15587
  if (typeof options.stepid === "string" && options.stepid.trim()) {
14373
15588
  return plan.steps.find((candidate) => candidate.id === options.stepid) ?? null;
14374
15589
  }
@@ -14377,7 +15592,7 @@ function resolvedinnerstep(step, plan) {
14377
15592
  async function executekeyhold(step, session, plan, tabid2, origin) {
14378
15593
  const output = await dispatchpagestep(step, tabid2, origin, plan);
14379
15594
  if (!output?.ok) return output ?? { ok: false, summary: "The key hold was not delivered." };
14380
- const options = stepoptions2(step);
15595
+ const options = stepoptions6(step);
14381
15596
  const holdid = typeof options.holdid === "string" && options.holdid.trim() ? options.holdid : randomid();
14382
15597
  const modifiers = Array.isArray(options.modifiers) ? options.modifiers.filter((item) => typeof item === "string") : [];
14383
15598
  const hold = { holdid, key: step.value ?? "", ...modifiers.length > 0 ? { modifiers } : {}, tabid: tabid2, stepid: step.id, pressedat: Date.now() };
@@ -14410,7 +15625,7 @@ async function executedismissdialog(step, session, plan, tabid2, origin) {
14410
15625
  return { ok: true, summary: `Dialog handler armed${answer} for the next confirm, alert or prompt.` };
14411
15626
  }
14412
15627
  async function executeretryaction(step, session, plan, tabid2, origin) {
14413
- const rule = stepoptions2(step).retryrule;
15628
+ const rule = stepoptions6(step).retryrule;
14414
15629
  const inner = resolvedinnerstep(step, plan);
14415
15630
  if (!inner) throw new Error("The reviewed wrapper step could not be resolved.");
14416
15631
  const innergate = validatestep(inner, origin);
@@ -14446,8 +15661,8 @@ async function executeenterframe(step, plan, tabid2, origin) {
14446
15661
  if (!inner) throw new Error("The reviewed frame wrapper step could not be resolved.");
14447
15662
  const innergate = validatestep(inner, origin);
14448
15663
  if (!innergate.allowed) throw new Error(`The wrapped step is not allowed: ${innergate.reason}`);
14449
- const options = stepoptions2(step);
14450
- const inneroptions = inner.options ? stepoptions2(inner) : void 0;
15664
+ const options = stepoptions6(step);
15665
+ const inneroptions = inner.options ? stepoptions6(inner) : void 0;
14451
15666
  const derived = { ...step, options: JSON.stringify({ ...options, kind: inner.kind, ...inner.target ? { target: inner.target } : {}, ...inner.value ? { value: inner.value } : {}, ...inneroptions ? { options: inneroptions } : {} }) };
14452
15667
  return dispatchpagestep(derived, tabid2, origin, plan);
14453
15668
  }
@@ -14459,7 +15674,7 @@ function detailarray(details, key) {
14459
15674
  return Array.isArray(value) ? value : [];
14460
15675
  }
14461
15676
  async function executediffsnapshots(step, session, plan, tabid2, origin) {
14462
- const options = stepoptions2(step);
15677
+ const options = stepoptions6(step);
14463
15678
  const versions = Array.isArray(options.versions) ? options.versions.filter((item) => typeof item === "number") : [];
14464
15679
  const baseversion = versions[0];
14465
15680
  const targetversion = versions[1];
@@ -14482,7 +15697,7 @@ async function executediffsnapshots(step, session, plan, tabid2, origin) {
14482
15697
  return { ok: Boolean(output.ok), summary: output.summary, details: { ...output.details ?? {}, versions: [baseversion, targetversion] } };
14483
15698
  }
14484
15699
  async function executewatchstep(step, session, plan, tabid2, origin) {
14485
- const options = stepoptions2(step);
15700
+ const options = stepoptions6(step);
14486
15701
  const watchid = typeof options.watchid === "string" && options.watchid.trim() ? options.watchid : randomid();
14487
15702
  const scopes = Array.isArray(options.scopes) ? options.scopes.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
14488
15703
  const events = Array.isArray(options.events) ? options.events.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
@@ -15164,7 +16379,7 @@ function commandtabids(step, options) {
15164
16379
  return listed.length > 0 ? listed : single;
15165
16380
  }
15166
16381
  async function executetabscommand(step, session, plan, sessiontabid) {
15167
- const options = stepoptions2(step);
16382
+ const options = stepoptions6(step);
15168
16383
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
15169
16384
  const windowid = step.value && /^\d+$/.test(step.value) ? Number.parseInt(step.value, 10) : 0;
15170
16385
  const layoutgate = layoutmutationgranted(session, Date.now());
@@ -15440,7 +16655,7 @@ async function executetabscommand(step, session, plan, sessiontabid) {
15440
16655
  }
15441
16656
  }
15442
16657
  async function executesaveprofiles(step, session, origin) {
15443
- const options = stepoptions2(step);
16658
+ const options = stepoptions6(step);
15444
16659
  const record2 = parseformrecord(options.formrecord);
15445
16660
  const name = typeof options.name === "string" ? options.name : "";
15446
16661
  if (!name || !record2) throw new Error("A reviewed profile name and form record are required.");
@@ -15460,7 +16675,7 @@ async function executeasksubmit(step, session, plan, tabid2, origin) {
15460
16675
  return { ok: true, summary: `Asksubmit prompt opened for form ${ticket.form || "the reviewed form"}; the submission waits for your approval.`, details: { ticket, values } };
15461
16676
  }
15462
16677
  async function executesubmitform(step, session, plan, tabid2, origin) {
15463
- const consentref = typeof stepoptions2(step).consentref === "string" ? stepoptions2(step).consentref : "";
16678
+ const consentref = typeof stepoptions6(step).consentref === "string" ? stepoptions6(step).consentref : "";
15464
16679
  const ticket = (await memory.gettickets()).find((item) => item.approved === true && (item.consentref === consentref || item.id === consentref));
15465
16680
  if (!ticket) throw new Error("No approved asksubmit ticket matches the reviewed consent ref; approve the submission in the review panel first.");
15466
16681
  const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The form submission returned no result." };
@@ -15484,7 +16699,7 @@ async function executeretryform(step, session, plan, tabid2, origin) {
15484
16699
  return { ok: Boolean(output?.ok), summary: output?.summary ?? "The retried submission returned no result.", details: { attempts, windows, ok: Boolean(output?.ok) } };
15485
16700
  }
15486
16701
  async function executeconsentpassword(step, session, plan, tabid2, origin) {
15487
- const consentref = typeof stepoptions2(step).consentref === "string" ? stepoptions2(step).consentref : "";
16702
+ const consentref = typeof stepoptions6(step).consentref === "string" ? stepoptions6(step).consentref : "";
15488
16703
  const gate = passwordconsentgranted(step);
15489
16704
  if (!gate.allowed) throw new Error(gate.reason ?? "A password fill requires a reviewed consent ref.");
15490
16705
  const output = await dispatchpagestep(step, tabid2, origin, plan);
@@ -15496,7 +16711,7 @@ async function executeattachfile(step, session, plan, tabid2, origin) {
15496
16711
  const artifacts = await memory.getartifacts();
15497
16712
  const artifact = artifacts.find((item) => item.name === name || item.id === name);
15498
16713
  if (!artifact) throw new Error(`No generated artifact named ${name} exists in the run store yet.`);
15499
- const derived = { ...step, options: JSON.stringify({ ...stepoptions2(step), artifact: artifact.id, artifactname: artifact.name }) };
16714
+ const derived = { ...step, options: JSON.stringify({ ...stepoptions6(step), artifact: artifact.id, artifactname: artifact.name }) };
15500
16715
  const output = await dispatchpagestep(derived, tabid2, origin, plan);
15501
16716
  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
16717
  return { ...output ?? { ok: false, summary: "The artifact attachment returned no result." }, details: { ...output?.details ?? {}, artifact } };
@@ -15529,7 +16744,7 @@ async function executeformstep(step, session, plan, tabid2, origin) {
15529
16744
  return executecaptchahandoff(step, session, plan, tabid2, origin);
15530
16745
  case "fillcode": {
15531
16746
  const stored = await memory.getcodevalue();
15532
- const source = typeof stepoptions2(step).source === "string" ? stepoptions2(step).source : "";
16747
+ const source = typeof stepoptions6(step).source === "string" ? stepoptions6(step).source : "";
15533
16748
  const derived = stored !== void 0 && source === "reviewed" ? { ...step, value: stored } : step;
15534
16749
  const output = await dispatchpagestep(derived, tabid2, origin, plan);
15535
16750
  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 +16815,7 @@ async function storeexport(stepid, datasetvalue, format, delimiter, session, pla
15600
16815
  return artifact;
15601
16816
  }
15602
16817
  async function executedatastep(step, session, plan, tabid2, origin) {
15603
- const options = stepoptions2(step);
16818
+ const options = stepoptions6(step);
15604
16819
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
15605
16820
  switch (step.kind) {
15606
16821
  case "scrapetable": {
@@ -15830,7 +17045,7 @@ async function verifyonerecord(record2, expected, extra) {
15830
17045
  return { ok: verification.ok, summary: verification.summary, details: { verification: { ...verification.matches, state: record2.state, path: record2.path, checksum: record2.checksum, bytes: record2.bytes } } };
15831
17046
  }
15832
17047
  async function executefilesstep(step, session, plan, tabid2, origin) {
15833
- const options = stepoptions2(step);
17048
+ const options = stepoptions6(step);
15834
17049
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
15835
17050
  switch (step.kind) {
15836
17051
  case "batchdownload": {
@@ -16059,7 +17274,7 @@ reconcilmimefilter().catch(() => {
16059
17274
  });
16060
17275
  var stitchprogress = /* @__PURE__ */ new Map();
16061
17276
  function stepcaptureoptions(step) {
16062
- return captureoptionsof(stepoptions2(step).capture);
17277
+ return captureoptionsof(stepoptions6(step).capture);
16063
17278
  }
16064
17279
  async function blobtodataurl(blob) {
16065
17280
  const buffer = new Uint8Array(await blob.arrayBuffer());
@@ -16183,7 +17398,7 @@ async function encodecanvas(width, height, draw, options) {
16183
17398
  return canvasdataurl(canvas, options.format, options.quality);
16184
17399
  }
16185
17400
  async function capturenamefor(step, plan, kind, format) {
16186
- const naming = stepoptions2(step).naming;
17401
+ const naming = stepoptions6(step).naming;
16187
17402
  const rule = naming && typeof naming === "object" && !Array.isArray(naming) ? naming : { run: true, step: true, sequence: true, kind: true };
16188
17403
  const counters = (await memory.getcapturecounters()).find((item) => item.taskid === plan.id);
16189
17404
  const advanced = advancecounter(counters?.counters ?? {}, step.id);
@@ -16223,13 +17438,62 @@ async function grabstateshot(step, session, plan, tabid2, phase) {
16223
17438
  return record2;
16224
17439
  }
16225
17440
  async function storecapture(record2, session, plan, step, origin) {
17441
+ const template = templateof(step);
17442
+ const regions = regionsfor(await memory.getredactregions(), origin, template);
17443
+ record2 = redactedshot(record2, regions);
16226
17444
  await memory.addcapture(record2);
17445
+ if (regions.length > 0) await audit("capture", `The capture of ${origin} on the template ${template} stored with its sensitive regions masked: ${redactionsummary(regions)}`, { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id });
16227
17446
  const routed = await routecapture(record2, session, plan, step, origin);
16228
17447
  await memory.setprogress(recordcapture(await memory.getprogress(), plan.id, step.id, record2, Date.now()));
16229
17448
  await refreshbadge();
16230
17449
  return { record: record2, routed };
16231
17450
  }
17451
+ var activeredactregions = [];
17452
+ async function drawredactoverlays(tabid2) {
17453
+ if (activeredactregions.length === 0) return;
17454
+ const rects = activeredactregions.map((region) => ({ x: region.x, y: region.y, width: region.width, height: region.height }));
17455
+ await chrome.scripting.executeScript({ target: { tabId: tabid2 }, func: (overlays) => {
17456
+ let host = document.getElementById("devthinkredacthost");
17457
+ if (!host) {
17458
+ host = document.createElement("div");
17459
+ host.id = "devthinkredacthost";
17460
+ host.style.position = "fixed";
17461
+ host.style.inset = "0";
17462
+ host.style.zIndex = "2147483647";
17463
+ host.style.pointerEvents = "none";
17464
+ document.documentElement.appendChild(host);
17465
+ }
17466
+ for (const overlay of overlays) {
17467
+ const rect = document.createElement("div");
17468
+ rect.style.position = "fixed";
17469
+ rect.style.left = `${overlay.x}px`;
17470
+ rect.style.top = `${overlay.y}px`;
17471
+ rect.style.width = `${overlay.width}px`;
17472
+ rect.style.height = `${overlay.height}px`;
17473
+ rect.style.background = "#000";
17474
+ host.appendChild(rect);
17475
+ }
17476
+ }, args: [rects] }).catch(() => {
17477
+ });
17478
+ }
17479
+ async function clearredactoverlays(tabid2) {
17480
+ if (activeredactregions.length === 0) return;
17481
+ await chrome.scripting.executeScript({ target: { tabId: tabid2 }, func: () => {
17482
+ document.getElementById("devthinkredacthost")?.remove();
17483
+ } }).catch(() => {
17484
+ });
17485
+ }
16232
17486
  async function executecapturestep(step, session, plan, tabid2, origin) {
17487
+ activeredactregions = regionsfor(await memory.getredactregions(), origin, templateof(step));
17488
+ await drawredactoverlays(tabid2);
17489
+ try {
17490
+ return await executecapturestepinner(step, session, plan, tabid2, origin);
17491
+ } finally {
17492
+ await clearredactoverlays(tabid2);
17493
+ activeredactregions = [];
17494
+ }
17495
+ }
17496
+ async function executecapturestepinner(step, session, plan, tabid2, origin) {
16233
17497
  const options = stepcaptureoptions(step);
16234
17498
  const format = options.format ?? "png";
16235
17499
  const ratio = options.pixelratio ?? 1;
@@ -16252,7 +17516,7 @@ async function executecapturestep(step, session, plan, tabid2, origin) {
16252
17516
  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
17517
  }
16254
17518
  if (step.kind === "shotfullpage") {
16255
- const rawoptions = stepoptions2(step);
17519
+ const rawoptions = stepoptions6(step);
16256
17520
  const settle2 = typeof rawoptions.settle === "number" ? rawoptions.settle : 150;
16257
17521
  const overlap = typeof rawoptions.overlap === "number" ? rawoptions.overlap : 0;
16258
17522
  const wait = typeof rawoptions.wait === "number" ? rawoptions.wait : void 0;
@@ -16289,7 +17553,7 @@ async function executecapturestep(step, session, plan, tabid2, origin) {
16289
17553
  }
16290
17554
  if (step.kind === "shotelement") {
16291
17555
  const selector = step.target ?? "";
16292
- const settle2 = typeof stepoptions2(step).settle === "number" ? stepoptions2(step).settle : 150;
17556
+ const settle2 = typeof stepoptions6(step).settle === "number" ? stepoptions6(step).settle : 150;
16293
17557
  const measured = await bridgecall(tabid2, "measurepage");
16294
17558
  const targetinfo = await bridgecall(tabid2, "elementrect", selector);
16295
17559
  if (!targetinfo.ok || !targetinfo.rect) throw new Error(targetinfo.summary);
@@ -16330,7 +17594,7 @@ async function executecapturestep(step, session, plan, tabid2, origin) {
16330
17594
  }
16331
17595
  }
16332
17596
  if (step.kind === "shotregion") {
16333
- const rawoptions = stepoptions2(step);
17597
+ const rawoptions = stepoptions6(step);
16334
17598
  const rect = rawoptions.regionrect;
16335
17599
  if (!rect) throw new Error("A reviewed regionrect is required in options.");
16336
17600
  const container = typeof rawoptions.container === "string" ? rawoptions.container : void 0;
@@ -16371,7 +17635,7 @@ async function executecapturestep(step, session, plan, tabid2, origin) {
16371
17635
  }
16372
17636
  }
16373
17637
  if (step.kind === "contactsheet") {
16374
- const rawoptions = stepoptions2(step);
17638
+ const rawoptions = stepoptions6(step);
16375
17639
  const elements = (Array.isArray(rawoptions.elements) ? rawoptions.elements : []).filter((item) => typeof item === "string" && item.trim().length > 0);
16376
17640
  const layout = rawoptions.sheet && typeof rawoptions.sheet === "object" && !Array.isArray(rawoptions.sheet) ? rawoptions.sheet : { cellsize: 240, columns: 3, label: "both" };
16377
17641
  const measured = await bridgecall(tabid2, "measurepage");
@@ -16482,7 +17746,7 @@ async function thumbonecapture(source, directive, plan, step) {
16482
17746
  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
17747
  }
16484
17748
  async function executemediastep(step, session, plan, tabid2, origin) {
16485
- const options = stepoptions2(step);
17749
+ const options = stepoptions6(step);
16486
17750
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
16487
17751
  const gate = mediagate(session, tabid2, origin, Date.now());
16488
17752
  if (!gate.allowed) throw new Error(gate.reason ?? "The media capture needs the active session tab grant.");
@@ -16741,7 +18005,7 @@ async function attachapikeys(names, origin) {
16741
18005
  return { headers, keys: attached };
16742
18006
  }
16743
18007
  async function executehttpstep(step, session, plan, tabid2, origin) {
16744
- const options = stepoptions2(step);
18008
+ const options = stepoptions6(step);
16745
18009
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
16746
18010
  if (step.kind === "fetchurl") {
16747
18011
  const request = fetchrequestof(options.fetch);
@@ -17006,7 +18270,7 @@ async function closechannelsforrun(runid) {
17006
18270
  channelbuses.clear();
17007
18271
  }
17008
18272
  async function executesocketstep(step, session, plan, tabid2, origin) {
17009
- const options = stepoptions2(step);
18273
+ const options = stepoptions6(step);
17010
18274
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
17011
18275
  if (step.kind === "opensocket") {
17012
18276
  const channel = channeloptionsof(options.socket);
@@ -17140,7 +18404,7 @@ async function executesocketstep(step, session, plan, tabid2, origin) {
17140
18404
  throw new Error("Unsupported socket observation kind.");
17141
18405
  }
17142
18406
  async function executenetwatchstep(step, session, plan, tabid2, origin) {
17143
- const options = stepoptions2(step);
18407
+ const options = stepoptions6(step);
17144
18408
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
17145
18409
  if (step.kind === "watchrequests") {
17146
18410
  const watch = options.watch && typeof options.watch === "object" && !Array.isArray(options.watch) ? options.watch : {};
@@ -17292,7 +18556,7 @@ function timelinedetail(entry, runid) {
17292
18556
  return { id: randomid(), runid, stepid: record2.stepid, time: record2.time, level: record2.level, source: record2.source, message: record2.message };
17293
18557
  }
17294
18558
  async function executetimelinestep(step, session, plan, tabid2, origin) {
17295
- const options = stepoptions2(step);
18559
+ const options = stepoptions6(step);
17296
18560
  const watch = options.watch && typeof options.watch === "object" && !Array.isArray(options.watch) ? options.watch : {};
17297
18561
  const watchwindow = typeof watch.window === "number" && Number.isFinite(watch.window) && watch.window >= 0 ? watch.window : 0;
17298
18562
  const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
@@ -17447,7 +18711,7 @@ function pauseframes(entry) {
17447
18711
  });
17448
18712
  }
17449
18713
  async function executecdpstep(step, session, plan, tabid2, origin) {
17450
- const options = stepoptions2(step);
18714
+ const options = stepoptions6(step);
17451
18715
  const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
17452
18716
  const debuggatecheck = debuggate(session, tabid2, origin, Date.now());
17453
18717
  if (!debuggatecheck.allowed) throw new Error(debuggatecheck.reason ?? "The devtools protocol step stays outside the debug gate.");
@@ -17681,7 +18945,7 @@ async function sampleheapforstep(tracker, stepid, tabid2, origin, plan) {
17681
18945
  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
18946
  }
17683
18947
  async function executeprofilestep(step, session, plan, tabid2, origin) {
17684
- const options = stepoptions2(step);
18948
+ const options = stepoptions6(step);
17685
18949
  const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
17686
18950
  const targets = profiletargetsof(options);
17687
18951
  const grants = await memory.getdebuggergrants();
@@ -18010,7 +19274,7 @@ async function controlledfetch(runid, url, init, controller, window2, streamstat
18010
19274
  return response;
18011
19275
  }
18012
19276
  async function executenetcontrolstep(step, session, plan, tabid2, origin) {
18013
- const options = stepoptions2(step);
19277
+ const options = stepoptions6(step);
18014
19278
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
18015
19279
  const ruleset = rulesetof(plan.id);
18016
19280
  if (step.kind === "blockrequest") {
@@ -18257,7 +19521,7 @@ async function enforcewindowreview(step, session, plan) {
18257
19521
  const progress = plan ? await memory.getprogress() : void 0;
18258
19522
  const tasktabids = plan ? trackedtasktabs(progress, plan.id) : [];
18259
19523
  const count = tasktabsinwindow(await livetabs(), windowid, tasktabids);
18260
- const gate = windowclosegate(count, stepoptions2(step).reviewed === true);
19524
+ const gate = windowclosegate(count, stepoptions6(step).reviewed === true);
18261
19525
  if (!gate.allowed) throw new Error(gate.reason ?? "The window close needs explicit review.");
18262
19526
  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
19527
  }
@@ -18343,7 +19607,7 @@ async function revertemulationforrun(runid, reason, tabid2) {
18343
19607
  }
18344
19608
  }
18345
19609
  async function executeemulationstep(step, session, plan, tabid2, origin) {
18346
- const options = stepoptions2(step);
19610
+ const options = stepoptions6(step);
18347
19611
  const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
18348
19612
  const revertplan = revertplanof(options.revertplan) ?? [];
18349
19613
  const family = familyofkind(step.kind) ?? "device";
@@ -18475,7 +19739,7 @@ async function performrestore(record2, restore, session) {
18475
19739
  return { restored, skippedorigins: grantscheck.skippedorigins };
18476
19740
  }
18477
19741
  async function executesessionstep(step, session, plan, tabid2, origin) {
18478
- const options = stepoptions2(step);
19742
+ const options = stepoptions6(step);
18479
19743
  const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
18480
19744
  if (step.kind === "persiststate") {
18481
19745
  const progress = await memory.getprogress();
@@ -18620,7 +19884,7 @@ async function dispatchworkflowstep(step, context) {
18620
19884
  return { ok: Boolean(output.ok), summary: output.summary, ...output.details !== void 0 ? { details: output.details } : {} };
18621
19885
  }
18622
19886
  async function executedelaystep(step) {
18623
- const options = stepoptions2({ id: step.id, kind: step.kind, summary: step.label, risk: "read", ...step.options !== void 0 ? { options: step.options } : {} });
19887
+ const options = stepoptions6({ id: step.id, kind: step.kind, summary: step.label, risk: "read", ...step.options !== void 0 ? { options: step.options } : {} });
18624
19888
  const delay = delayof(options.delay);
18625
19889
  const sampled = delayjitter(delay, hashseed(`${step.id}:${Date.now()}`));
18626
19890
  const transport = await sleepreviewed(sampled, step.id);
@@ -18667,7 +19931,7 @@ async function sleepreviewed(sampled, stepid) {
18667
19931
  return "timer";
18668
19932
  }
18669
19933
  async function executewaitelement(step, tabid2) {
18670
- const options = stepoptions2({ 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 } : {} });
19934
+ const options = stepoptions6({ 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
19935
  const wait = waitof(options.wait, step.target);
18672
19936
  const startedat = Date.now();
18673
19937
  const starttab = await chrome.tabs.get(tabid2).catch(() => void 0);
@@ -18695,7 +19959,7 @@ function waitof(value, target) {
18695
19959
  return { selector, timeout, poll };
18696
19960
  }
18697
19961
  async function executecomputestep(step, session) {
18698
- const options = stepoptions2({ id: step.id, kind: step.kind, summary: step.label, risk: "read", ...step.options !== void 0 ? { options: step.options } : {} });
19962
+ const options = stepoptions6({ id: step.id, kind: step.kind, summary: step.label, risk: "read", ...step.options !== void 0 ? { options: step.options } : {} });
18699
19963
  const expression = options.expression;
18700
19964
  if (!expression || typeof expression !== "object") throw new Error("The compute step needs a reviewed expression.");
18701
19965
  const scopes = runscopes(options.variables);
@@ -18704,7 +19968,7 @@ async function executecomputestep(step, session) {
18704
19968
  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
19969
  }
18706
19970
  async function executeextractvarsstep(step, session) {
18707
- const options = stepoptions2({ 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 } : {} });
19971
+ const options = stepoptions6({ 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
19972
  const rule = options.rule;
18709
19973
  if (!rule || typeof rule !== "object" || typeof rule.pattern !== "string") throw new Error("The variable extraction needs a reviewed regex rule.");
18710
19974
  const text2 = typeof options.text === "string" ? options.text : step.value ?? "";
@@ -18717,7 +19981,7 @@ async function executeextractvarsstep(step, session) {
18717
19981
  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
19982
  }
18719
19983
  async function executeworkflowstep(step, session, plan, tabid2, origin) {
18720
- const options = stepoptions2(step);
19984
+ const options = stepoptions6(step);
18721
19985
  if (step.kind === "composeworkflow") {
18722
19986
  const payload = options.workflow;
18723
19987
  if (!payload || typeof payload !== "object" || Array.isArray(payload)) throw new Error("The workflow composition needs the reviewed workflow payload.");
@@ -18773,7 +20037,7 @@ function workflowstepofentry(value) {
18773
20037
  return blockinvocationof(value);
18774
20038
  }
18775
20039
  async function executeworkflowrun(step, session, plan, tabid2, origin, dry) {
18776
- const options = stepoptions2(step);
20040
+ const options = stepoptions6(step);
18777
20041
  const workflowid = typeof options.workflowid === "string" ? options.workflowid : "";
18778
20042
  const storedrecord = await memory.getworkflowrecord(workflowid);
18779
20043
  if (!storedrecord) throw new Error(`No composed workflow matches ${workflowid || "the reviewed id"}.`);
@@ -18936,7 +20200,7 @@ async function storetimeoutabort(run, step, message, budget) {
18936
20200
  return { run: aborted, log: [entry] };
18937
20201
  }
18938
20202
  async function executetriggerstep(step, session, plan, tabid2, origin) {
18939
- const options = stepoptions2(step);
20203
+ const options = stepoptions6(step);
18940
20204
  const family = triggerfamilyof(step.kind);
18941
20205
  if (!family) throw new Error(`The ${step.kind} step is not a reviewed trigger kind.`);
18942
20206
  const workflowid = typeof options.workflowid === "string" ? options.workflowid : "";
@@ -19146,6 +20410,23 @@ async function executeaction(step, session, plan, tabid2, origin, settings, verd
19146
20410
  const capabilityverdict = offscreencapabilitygate({ environment: routing.environment, granted: offgranted });
19147
20411
  if (!capabilityverdict.allowed) throw new Error(capabilityverdict.reason);
19148
20412
  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 });
20413
+ const securityverdict = await securitystepgate(step, session, origin, settings);
20414
+ if (!securityverdict.allowed) {
20415
+ if (session && plan) {
20416
+ 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(() => {
20417
+ });
20418
+ await appendrunevent("deny", `The ${step.kind} step ${step.id} on ${origin} was denied without navigation: ${securityverdict.reason}`, session, origin, step.id).catch(() => {
20419
+ });
20420
+ }
20421
+ 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 });
20422
+ if (securityverdict.suspended && session) {
20423
+ 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(() => {
20424
+ });
20425
+ 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 });
20426
+ }
20427
+ throw new Error(securityverdict.reason);
20428
+ }
20429
+ step = await resolvevaultvalues(step, session);
19149
20430
  if (session && plan && plan.state === "approved" && mode === "plan") await openplanrun(session, plan);
19150
20431
  if (plan && plan.state === "approved") await markpendingstep(plan, step.id);
19151
20432
  if (routing.environment === "sandboxframe") return executesandboxrender(step, session, plan, origin);
@@ -19273,10 +20554,13 @@ async function executeaction(step, session, plan, tabid2, origin, settings, verd
19273
20554
  if (resolved) {
19274
20555
  await memory.addresolution({ stepid: step.id, mode: resolved.mode, selector: resolved.selector, label: resolved.label, at: Date.now() });
19275
20556
  }
19276
- const outcome = { stepid: step.id, ok: Boolean(output?.ok), summary, environment: routing.environment, ...output?.details ? { details: output.details } : {}, at: Date.now() };
20557
+ const maskshapes = shapesof({ ...settings !== void 0 ? { settings } : {}, rules: await memory.getmaskrules(), origin });
20558
+ const outcome = { stepid: step.id, ok: Boolean(output?.ok), summary, environment: routing.environment, ...output?.details ? { details: maskexport(output.details, maskshapes) } : {}, at: Date.now() };
19277
20559
  const auditkind = stepauditkind(step, Boolean(output?.ok));
19278
20560
  await audit(auditkind, summary, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {}, stepid: step.id });
19279
20561
  await memory.addoutcome(outcome);
20562
+ 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(() => {
20563
+ });
19280
20564
  if (output?.ok && plan && mode === "plan") {
19281
20565
  const base = await memory.getprogress();
19282
20566
  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 +20603,7 @@ async function previewstep(stepid) {
19319
20603
  if (!step) throw new Error("Reviewed step was not found.");
19320
20604
  const gate = canpreview({ session, plan, step, tabid: tab.id, origin });
19321
20605
  if (!gate.allowed) throw new Error(gate.reason);
19322
- if (!step.target && !stepoptions2(step).targetref) throw new Error("Only a target-based step can be previewed.");
20606
+ if (!step.target && !stepoptions6(step).targetref) throw new Error("Only a target-based step can be previewed.");
19323
20607
  const result = await chrome.scripting.executeScript({ target: { tabId: tab.id }, func: (action, expectedorigin) => {
19324
20608
  const bridge = globalThis.devthinkbridge;
19325
20609
  if (!bridge) throw new Error("Devthink page bridge is unavailable.");
@@ -19370,8 +20654,37 @@ async function extractionreportValue() {
19370
20654
  async function provenancereportValue() {
19371
20655
  return provenancereport({ records: await memory.getprovenances() });
19372
20656
  }
20657
+ var commandschemas = {
20658
+ security: { allowlist: "object", profile: "object", consent: "object", revoke: "object", mask: "object", read: "object", export: "object", settings: "object", gate: "object", vault: "object", connectallow: "object", ratelimit: "object", redact: "object" },
20659
+ environments: { action: "string", grants: "array", requestcapability: "boolean", pool: "object", offscreenclose: "boolean", sandbox: "object", settings: "object", render: "object", sandboxhost: "object" },
20660
+ transparency: {},
20661
+ execute: { stepid: "string" },
20662
+ configure: { endpoint: "string" }
20663
+ };
20664
+ function schemavalidation(message) {
20665
+ if (!Boolean(message) || typeof message !== "object" || Array.isArray(message)) return [{ path: "message", expected: "object", found: Array.isArray(message) ? "array" : typeof message, reason: "Every inbound command travels as one plain object; schemastrict refuses the carrier before dispatch." }];
20666
+ const command = message;
20667
+ if (typeof command.kind !== "string" || command.kind.trim() === "") return [{ path: "kind", expected: "string", found: typeof command.kind, reason: "Every inbound command names its kind as a non-empty string; a kindless command never dispatches." }];
20668
+ const schema = commandschemas[command.kind];
20669
+ if (schema === void 0) return [];
20670
+ return schemacheck({ command, schema }).errors;
20671
+ }
19373
20672
  async function handlerequest(message, sender) {
19374
- if (!extensionpage(sender)) throw new Error("Requests are accepted only from Devthink extension pages.");
20673
+ const originverdict = origincheckof({ ...sender.id !== void 0 ? { senderid: sender.id } : {}, ...sender.origin !== void 0 ? { senderorigin: sender.origin } : {}, extensionid: chrome.runtime.id, connectallow: await memory.getconnectallow() });
20674
+ const inboundgate = origincheckgate({ verdict: originverdict });
20675
+ if (!inboundgate.allowed) {
20676
+ await audit("inbound", `The origincheck dropped an inbound message from ${originverdict.sender}${originverdict.origin !== "" ? ` of ${originverdict.origin}` : ""} without handler execution: ${inboundgate.reason}`, {}).catch(() => {
20677
+ });
20678
+ throw new Error(inboundgate.reason);
20679
+ }
20680
+ if (sender.id === chrome.runtime.id && !extensionpage(sender)) throw new Error("Requests are accepted only from Devthink extension pages.");
20681
+ const schemaerrors = schemavalidation(message);
20682
+ const schemagate = schemaguardgate({ errors: schemaerrors });
20683
+ if (!schemagate.allowed) {
20684
+ await audit("schema", `The schemastrict validation refused an inbound command with ${schemaerrors.length} schema error${schemaerrors.length === 1 ? "" : "s"} at ${schemaerrors.map((error) => error.path).join(", ")}; the refusal echoes no payload.`, {}).catch(() => {
20685
+ });
20686
+ throw new Error(schemagate.reason);
20687
+ }
19375
20688
  const input = message;
19376
20689
  switch (input.kind) {
19377
20690
  case "configure": {
@@ -19491,7 +20804,7 @@ async function handlerequest(message, sender) {
19491
20804
  const livetab = session ? await chrome.tabs.get(session.tabid).catch(() => void 0) : void 0;
19492
20805
  const waitprofile = session ? waitprofiles.find((record2) => record2.origin === session.origin) : void 0;
19493
20806
  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() };
20807
+ 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
20808
  }
19496
20809
  case "capabilities":
19497
20810
  return refreshcapabilities();
@@ -20546,7 +21859,11 @@ async function handlerequest(message, sender) {
20546
21859
  });
20547
21860
  activerecordings.delete(id);
20548
21861
  }
20549
- if (session) await memory.setsession({ ...session, stoppedat: Date.now() });
21862
+ if (session) {
21863
+ await memory.setsession({ ...session, stoppedat: Date.now() });
21864
+ await sealsessionrunlog(session.id).catch(() => {
21865
+ });
21866
+ }
20550
21867
  const plan = await memory.getplan();
20551
21868
  if (plan && ["pending", "approved"].includes(plan.state)) await memory.setplan({ ...plan, state: "cancelled" });
20552
21869
  await audit("stop", "The user stopped the browser session.", { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {} });
@@ -22538,6 +23855,259 @@ async function handlerequest(message, sender) {
22538
23855
  }
22539
23856
  throw new Error("The swarm merge request carries no merge, report, export, compare, lesson, costs, timeline, replay or snapshot action.");
22540
23857
  }
23858
+ case "transparency": {
23859
+ const view = await memory.gettransparencyview();
23860
+ const report = transparencyreport({ grants: transparencygrants({ allowlist: view.allowlist, profiles: view.profiles }), windows: windowhistory(view.windows), connectallow: connectallowlist(view.connectallow), permdiffs: view.permdiffs, safedefaults: view.safedefaults, vault: vaultview(view.vault) });
23861
+ await audit("transparency", `The transparencypage read its transparency report in one memory read: ${report.grants.length} grant row${report.grants.length === 1 ? "" : "s"} with revoke actions, ${report.windows.length} consent window${report.windows.length === 1 ? "" : "s"}, ${report.connectallow.length} connectallow entr${report.connectallow.length === 1 ? "y" : "ies"}, ${report.permdiffs.length} permdiff record${report.permdiffs.length === 1 ? "" : "s"} and ${report.vault.length} vault label${report.vault.length === 1 ? "" : "s"}.`, {});
23862
+ return report;
23863
+ }
23864
+ case "security": {
23865
+ const input2 = message;
23866
+ const now = Date.now();
23867
+ const session = await memory.getsession();
23868
+ const settings = await memory.getsettings();
23869
+ if (input2.allowlist !== void 0) {
23870
+ if (input2.allowlist.add !== void 0) {
23871
+ const origin = input2.allowlist.add.origin?.trim() ?? "";
23872
+ if (origin === "") throw new Error("The allowlist grant needs its exact origin.");
23873
+ if (wildcardentry(origin)) throw new Error("The allowlist binds every grant to one exact origin; a wildcard entry never passes.");
23874
+ await memory.addallowlistorigin({ origin, profileid: runstateprofile, grantedat: now });
23875
+ 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 } : {} });
23876
+ return { ...await securityviewof(), granted: origin };
23877
+ }
23878
+ if (input2.allowlist.remove !== void 0) {
23879
+ const origin = input2.allowlist.remove.origin?.trim() ?? "";
23880
+ await memory.removeallowlistorigin(origin, runstateprofile);
23881
+ await audit("revoke", `The user removed the origin ${origin} from the automation allowlist; the denydefault posture refuses the origin again.`, { ...session ? { sessionid: session.id } : {} });
23882
+ return { ...await securityviewof(), removed: origin };
23883
+ }
23884
+ }
23885
+ if (input2.profile !== void 0) {
23886
+ const origin = input2.profile.origin?.trim() ?? session?.origin ?? "";
23887
+ const kind = input2.profile.kind?.trim() ?? "";
23888
+ const decision = input2.profile.decision === "deny" ? "deny" : "grant";
23889
+ if (origin === "" || kind === "") throw new Error("The origin profile decision needs its exact origin and its action kind.");
23890
+ const profiles = await memory.getoriginprofiles();
23891
+ const existing = profiles.find((candidate) => candidate.origin === origin);
23892
+ const updated = profilekind({ profile: existing ?? originprofileof({ origin, now }), kind, decision, now });
23893
+ await memory.saveoriginprofile(updated);
23894
+ 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 } : {} });
23895
+ return { ...await securityviewof(), profile: updated };
23896
+ }
23897
+ if (input2.consent !== void 0) {
23898
+ if (input2.consent.open !== void 0) {
23899
+ if (!session) throw new Error("The consent window opens inside an active session.");
23900
+ const duration = input2.consent.open.duration ?? settings?.consentduration;
23901
+ if (duration === void 0) throw new Error("The consent prompt needs its duration in milliseconds; no grant ever defaults to unlimited.");
23902
+ const durationgate = consentdurationvalid(duration);
23903
+ if (!durationgate.allowed) throw new Error(durationgate.reason);
23904
+ const origin = input2.consent.open.origin?.trim() !== "" && input2.consent.open.origin !== void 0 ? input2.consent.open.origin.trim() : session.origin;
23905
+ const kinds = (input2.consent.open.kinds ?? []).map((kind) => kind.trim()).filter((kind) => kind !== "");
23906
+ const window2 = openconsentwindow({ sessionid: session.id, origin, duration, kinds: kinds.length > 0 ? kinds : ["observe"], now });
23907
+ 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)]);
23908
+ await appendrunevent("grant", `The consent prompt opened the window ${window2.id} for ${origin} with the boundary ${window2.boundary}.`, session, origin).catch(() => {
23909
+ });
23910
+ 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 });
23911
+ return { ...await securityviewof(), window: window2 };
23912
+ }
23913
+ if (input2.consent.renew !== void 0) {
23914
+ if (!session) throw new Error("The consent window renewal opens inside an active session.");
23915
+ const windowid = input2.consent.renew.windowid?.trim() ?? "";
23916
+ const current = (await memory.getconsentwindows()).find((candidate) => candidate.id === windowid && candidate.sessionid === session.id);
23917
+ if (!current) throw new Error(`No consent window ${windowid} exists for the session.`);
23918
+ const duration = input2.consent.renew.duration ?? settings?.consentduration;
23919
+ if (duration === void 0) throw new Error("The renewal prompt needs its duration in milliseconds; a renewal only runs through a new explicit prompt.");
23920
+ const durationgate = consentdurationvalid(duration);
23921
+ if (!durationgate.allowed) throw new Error(durationgate.reason);
23922
+ const { renewed, closed } = renewconsentwindow({ window: current, duration, kinds: current.kinds, now });
23923
+ await memory.setconsentwindows([renewed, closed, ...(await memory.getconsentwindows()).filter((candidate) => candidate.id !== windowid)]);
23924
+ 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(() => {
23925
+ });
23926
+ 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 });
23927
+ return { ...await securityviewof(), window: renewed };
23928
+ }
23929
+ if (input2.consent.classes !== void 0) {
23930
+ const origin = input2.consent.classes.origin?.trim() || session?.origin || "";
23931
+ const classes = (input2.consent.classes.classes ?? []).map((kind) => kind.trim()).filter((kind) => kind !== "");
23932
+ if (origin === "" || classes.length === 0) throw new Error("The fresh class consent needs its origin and its sensitive classes.");
23933
+ for (const classname of classes) {
23934
+ await memory.addclassconsent({ id: randomid(), origin, sensitiveclass: classname, grantedat: now });
23935
+ 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 } : {} });
23936
+ }
23937
+ return { ...await securityviewof(), classes };
23938
+ }
23939
+ }
23940
+ if (input2.revoke !== void 0) {
23941
+ if (!session) throw new Error("The revocation needs its active session.");
23942
+ const plan2 = await memory.getplan();
23943
+ const runid = input2.revoke.runid?.trim() || plan2?.id || "";
23944
+ if (runid === "") throw new Error("The revocation needs its run.");
23945
+ const state = await memory.getrunstate(runstateprofile);
23946
+ const pendingstepid = state?.pendingstepid;
23947
+ const progress = await memory.getprogress();
23948
+ const completed = new Set(progress?.planid === runid ? progress.completedsteps : []);
23949
+ const queued = (plan2?.steps ?? []).map((step) => step.id).filter((id) => !completed.has(id));
23950
+ 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 });
23951
+ await memory.addrevocation(revocation);
23952
+ if (plan2 && plan2.id === runid && ["pending", "approved"].includes(plan2.state)) await memory.setplan({ ...plan2, state: "cancelled" });
23953
+ if (plan2 && plan2.id === runid && pendingstepid !== void 0) await memory.setprogress(recordrevocation(progress, runid, pendingstepid, { haltedstepids: revocation.haltedstepids, revokedstepid: pendingstepid, reason: revocation.reason }, now));
23954
+ 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(() => {
23955
+ });
23956
+ 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 } : {} });
23957
+ return { ...await securityviewof(), revocation };
23958
+ }
23959
+ if (input2.mask !== void 0) {
23960
+ if (input2.mask.add !== void 0) {
23961
+ const shapes = (input2.mask.add.shapes ?? []).map((shape) => shape.trim().toLowerCase()).filter((shape) => shape !== "");
23962
+ if (shapes.length === 0) throw new Error("The mask rule needs its field shapes.");
23963
+ const rule = { id: randomid(), ...input2.mask.add.origin !== void 0 && input2.mask.add.origin.trim() !== "" ? { origin: input2.mask.add.origin.trim() } : {}, shapes, createdat: now };
23964
+ await memory.addmaskrule(rule);
23965
+ 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 } : {} });
23966
+ return { ...await securityviewof(), rule };
23967
+ }
23968
+ if (input2.mask.remove !== void 0) {
23969
+ const id = input2.mask.remove.id?.trim() ?? "";
23970
+ await memory.removemaskrule(id);
23971
+ await audit("mask", `The user removed the mask rule ${id}.`, { ...session ? { sessionid: session.id } : {} });
23972
+ return { ...await securityviewof(), removed: id };
23973
+ }
23974
+ }
23975
+ if (input2.read !== void 0) {
23976
+ const runid = input2.read.runid?.trim() || session?.id || "";
23977
+ const log = await memory.getimmutablelog(runid);
23978
+ if (!log) throw new Error(`No run log exists for the run ${runid}.`);
23979
+ const read = await readverifiedlog(log);
23980
+ const readgate = logreadgate({ valid: read.ok });
23981
+ if (!readgate.allowed) throw new Error(readgate.reason);
23982
+ 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 });
23983
+ return { ...await securityviewof(), log: read.entries, verification: read.reason };
23984
+ }
23985
+ if (input2.export !== void 0) {
23986
+ const runid = input2.export.runid?.trim() || session?.id || "";
23987
+ const exported = await memory.exportverifiedrunlog(runid);
23988
+ if (!exported.chainvalid) throw new Error(exported.reason);
23989
+ 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 });
23990
+ return { ...await securityviewof(), export: exported };
23991
+ }
23992
+ if (input2.settings !== void 0) {
23993
+ const patch = { ...settings };
23994
+ if (input2.settings.consentduration !== void 0) {
23995
+ const durationgate = consentdurationvalid(input2.settings.consentduration);
23996
+ if (!durationgate.allowed) throw new Error(durationgate.reason);
23997
+ patch.consentduration = input2.settings.consentduration;
23998
+ }
23999
+ if (input2.settings.logretention !== void 0) patch.logretention = input2.settings.logretention;
24000
+ if (input2.settings.maskshapes !== void 0) patch.maskshapes = input2.settings.maskshapes.map((shape) => shape.trim().toLowerCase()).filter((shape) => shape !== "");
24001
+ if (input2.settings.phishdistance !== void 0) {
24002
+ const thresholdgate = phishthresholdgate(input2.settings.phishdistance);
24003
+ if (!thresholdgate.allowed) throw new Error(thresholdgate.reason);
24004
+ patch.phishdistance = input2.settings.phishdistance;
24005
+ }
24006
+ if (input2.settings.phishfreshness !== void 0) patch.phishfreshness = input2.settings.phishfreshness;
24007
+ await memory.setsettings(patch);
24008
+ 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.`, {});
24009
+ return { ...await securityviewof(), configured: true };
24010
+ }
24011
+ if (input2.gate !== void 0 && input2.gate.resolve !== void 0) {
24012
+ const gateid = input2.gate.resolve.gateid?.trim() ?? "";
24013
+ const decision = input2.gate.resolve.decision === "refused" ? "refused" : "resolved";
24014
+ if (gateid === "") throw new Error("The gate resolution names its single gate.");
24015
+ const resolved = resolvegate({ gates: await memory.getgates(), gateid, decision, actor: "user", now });
24016
+ if (resolved.resolution === void 0 || resolved.gates === void 0) throw new Error(`No open gate ${gateid} exists to resolve; a gate resolution stays a distinct human action on one gate.`);
24017
+ await memory.setgates(resolved.gates);
24018
+ await memory.addgateresolution(resolved.resolution);
24019
+ const gateplan = await memory.getplan();
24020
+ const gate = resolved.gates.find((candidate) => candidate.gateid === gateid);
24021
+ if (gateplan && gate?.resolvedat !== void 0) await memory.setprogress(recordgatewait(await memory.getprogress(), gateplan.id, gate.stepid, { gateid: gate.gateid, kind: gate.kind, openedat: gate.openedat, resolvedat: gate.resolvedat, waitedms: Math.max(0, gate.resolvedat - gate.openedat) }, now)).catch(() => {
24022
+ });
24023
+ if (session) await appendrunevent("gate", `The user ${decision === "resolved" ? "resolved" : "refused"} the ${resolved.resolution.kind} gate ${gateid} of the step ${resolved.resolution.stepid} through one distinct human action; no timeout resolved it and no batch approved it.`, session, gate?.origin ?? session.origin, resolved.resolution.stepid).catch(() => {
24024
+ });
24025
+ await audit("gate", `The user ${decision === "resolved" ? "resolved" : "refused"} the ${resolved.resolution.kind} gate ${gateid} of the step ${resolved.resolution.stepid} through one distinct human action; no timeout resolved it and no batch approved it.`, { ...session ? { sessionid: session.id } : {}, ...gateplan ? { planid: gateplan.id } : {}, stepid: resolved.resolution.stepid });
24026
+ return { ...await securityviewof(), gate };
24027
+ }
24028
+ if (input2.vault !== void 0) {
24029
+ if (input2.vault.add !== void 0) {
24030
+ const label = input2.vault.add.label?.trim() ?? "";
24031
+ const scope = input2.vault.add.scope?.trim() !== "" && input2.vault.add.scope !== void 0 ? input2.vault.add.scope.trim() : session?.origin ?? "";
24032
+ const value = input2.vault.add.value ?? "";
24033
+ if (label === "" || scope === "" || value === "") throw new Error("The vault entry needs its label, its exact origin scope and its value; the value stays behind the vault seam.");
24034
+ const entry = await vaultstore({ seam: vaultseamstore, label, scope, profileid: runstateprofile, provenance: input2.vault.add.provenance === "session" ? "session" : "user", value, now });
24035
+ await memory.addsecret(entry);
24036
+ await audit("vault", `The user stored the secret ${entry.label} for ${entry.scope} behind the vault seam; the metadata keeps the label, the scope, the provenance and the digest while no plaintext value persists anywhere.`, { ...session ? { sessionid: session.id } : {} });
24037
+ return { ...await securityviewof(), secret: { vaultid: entry.vaultid, label: entry.label, scope: entry.scope } };
24038
+ }
24039
+ if (input2.vault.delete !== void 0) {
24040
+ const vaultid = input2.vault.delete.vaultid?.trim() ?? "";
24041
+ const entry = (await memory.getsecretvault()).find((candidate) => candidate.vaultid === vaultid);
24042
+ if (!entry) throw new Error(`No vault entry ${vaultid} exists.`);
24043
+ const dropped = await vaultdelete({ seam: vaultseamstore, entry });
24044
+ await memory.removesecret(vaultid);
24045
+ await audit("vault", `The user deleted the secret ${dropped.label} of ${entry.scope}: ${dropped.reason}`, { ...session ? { sessionid: session.id } : {} });
24046
+ return { ...await securityviewof(), removedsecret: vaultid };
24047
+ }
24048
+ }
24049
+ if (input2.connectallow !== void 0) {
24050
+ if (input2.connectallow.add !== void 0) {
24051
+ const entry = connectallowentryof({ senderid: input2.connectallow.add.senderid?.trim() ?? "", displayname: input2.connectallow.add.displayname?.trim() ?? "", ...input2.connectallow.add.origin !== void 0 && input2.connectallow.add.origin.trim() !== "" ? { origin: input2.connectallow.add.origin.trim() } : {}, now });
24052
+ await memory.addconnectallow(entry);
24053
+ await audit("inbound", `The user allowed the external sender ${entry.displayname} (${entry.senderid})${entry.origin !== void 0 ? ` of ${entry.origin}` : ""}; the connectallow list ships empty by default and holds user managed entries only.`, { ...session ? { sessionid: session.id } : {} });
24054
+ return { ...await securityviewof(), allowedsender: entry };
24055
+ }
24056
+ if (input2.connectallow.remove !== void 0) {
24057
+ const senderid = input2.connectallow.remove.senderid?.trim() ?? "";
24058
+ await memory.removeconnectallow(senderid);
24059
+ await audit("inbound", `The user removed the external sender ${senderid} from the connectallow list; the origincheck drops its messages and ports again.`, { ...session ? { sessionid: session.id } : {} });
24060
+ return { ...await securityviewof(), removedsender: senderid };
24061
+ }
24062
+ }
24063
+ if (input2.ratelimit !== void 0) {
24064
+ if (input2.ratelimit.set !== void 0) {
24065
+ const origin = input2.ratelimit.set.origin?.trim() ?? "";
24066
+ const limit = input2.ratelimit.set.limit ?? 0;
24067
+ const window2 = input2.ratelimit.set.window ?? 0;
24068
+ if (origin === "") throw new Error("The ratelimit bucket needs its exact origin.");
24069
+ const bounds = ratelimitboundsvalid(limit, window2);
24070
+ if (!bounds.allowed) throw new Error(bounds.reason);
24071
+ const bucket = bucketof({ origin, sessionid: session?.id ?? "global", limit, window: window2, now });
24072
+ await memory.saveratelimitbucket(bucket);
24073
+ await audit("rate", `The user configured the ratelimit bucket of ${origin} at ${limit} command${limit === 1 ? "" : "s"} per ${window2} milliseconds; the bounds stay user choices with no hidden ceiling.`, { ...session ? { sessionid: session.id } : {} });
24074
+ return { ...await securityviewof(), bucket };
24075
+ }
24076
+ if (input2.ratelimit.remove !== void 0) {
24077
+ const origin = input2.ratelimit.remove.origin?.trim() ?? "";
24078
+ const buckets = await memory.getratelimitbuckets();
24079
+ for (const bucket of buckets.filter((candidate) => candidate.origin === origin)) await memory.removeratelimitbucket(bucket.origin, bucket.sessionid);
24080
+ await audit("rate", `The user removed the ratelimit bucket of ${origin}; the origin runs without a bucket because the bounds stay user choices only.`, { ...session ? { sessionid: session.id } : {} });
24081
+ return { ...await securityviewof(), removedbucket: origin };
24082
+ }
24083
+ }
24084
+ if (input2.redact !== void 0) {
24085
+ if (input2.redact.add !== void 0) {
24086
+ const region = regionof({ origin: input2.redact.add.origin?.trim() !== "" && input2.redact.add.origin !== void 0 ? input2.redact.add.origin.trim() : session?.origin ?? "", template: input2.redact.add.template?.trim() !== "" && input2.redact.add.template !== void 0 ? input2.redact.add.template.trim() : "page", x: input2.redact.add.x ?? 0, y: input2.redact.add.y ?? 0, width: input2.redact.add.width ?? 0, height: input2.redact.add.height ?? 0, reason: input2.redact.add.reason?.trim() !== "" && input2.redact.add.reason !== void 0 ? input2.redact.add.reason.trim() : "The user drew the mask on the capture surface.", source: "userdrawn", now });
24087
+ await memory.addredactregion(region);
24088
+ await audit("capture", `The user drew the redact region ${region.id} at ${region.x},${region.y} of ${region.width}x${region.height} on ${region.origin}/${region.template}: ${region.reason}`, { ...session ? { sessionid: session.id } : {} });
24089
+ return { ...await securityviewof(), region };
24090
+ }
24091
+ if (input2.redact.remove !== void 0) {
24092
+ const id = input2.redact.remove.id?.trim() ?? "";
24093
+ await memory.removeredactregion(id);
24094
+ await audit("capture", `The user removed the redact region ${id}.`, { ...session ? { sessionid: session.id } : {} });
24095
+ return { ...await securityviewof(), removedregion: id };
24096
+ }
24097
+ }
24098
+ const plan = await memory.getplan();
24099
+ const pending = [];
24100
+ if (session && plan && ["pending", "approved"].includes(plan.state)) {
24101
+ for (const step of plan.steps) {
24102
+ const classification = sensitiveclassesof(step);
24103
+ if (!classification.sensitive) continue;
24104
+ const missing = classification.classes.length > 0 ? classification.classes : [];
24105
+ if (missing.length === 0 && !classification.bydefault) continue;
24106
+ 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 }) });
24107
+ }
24108
+ }
24109
+ return { ...await securityviewof(), prompts: pending };
24110
+ }
22541
24111
  case "environments": {
22542
24112
  const input2 = message;
22543
24113
  const now = Date.now();
@@ -22982,7 +24552,7 @@ async function raiseremoteapproval(clientid, toolname, params, step) {
22982
24552
  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
24553
  }
22984
24554
  async function executelistruns(step, session) {
22985
- const options = stepoptions2(step);
24555
+ const options = stepoptions6(step);
22986
24556
  const statefilter = typeof options.state === "string" && options.state.trim() !== "" ? options.state : void 0;
22987
24557
  const runs = await memory.listworkflowruns();
22988
24558
  const selected = statefilter !== void 0 ? runs.filter((run) => run.state === statefilter) : runs;
@@ -23357,7 +24927,7 @@ async function maybeautosnapshot() {
23357
24927
  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
24928
  return;
23359
24929
  }
23360
- const options = stepoptions2(step);
24930
+ const options = stepoptions6(step);
23361
24931
  const snapshot2 = snapshotplanof(options.snapshot);
23362
24932
  if (!snapshot2) return;
23363
24933
  const record2 = await capturesessionrecord({ ...snapshot2, ...snapshot2.auto !== void 0 ? { auto: snapshot2.auto } : {} }, session, plan.id).catch(() => void 0);
@@ -23402,10 +24972,22 @@ async function restoreemulationstate() {
23402
24972
  restoreemulationstate().catch(() => {
23403
24973
  });
23404
24974
  chrome.runtime.onConnect.addListener((port) => {
23405
- if (port.name !== "devthinksidepanel" || port.sender?.id !== chrome.runtime.id || !port.sender.url?.startsWith(chrome.runtime.getURL(""))) return port.disconnect();
23406
- port.onMessage.addListener((message) => {
23407
- handlerequest(message, port.sender ?? {}).then((value) => port.postMessage({ ok: true, value })).catch((error) => port.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) }));
23408
- });
24975
+ void (async () => {
24976
+ const handshake = portaccept({ portname: port.name, ...port.sender?.id !== void 0 ? { senderid: port.sender.id } : {}, ...port.sender?.origin !== void 0 ? { senderorigin: port.sender.origin } : {}, extensionid: chrome.runtime.id, connectallow: await memory.getconnectallow() });
24977
+ if (!handshake.accepted) {
24978
+ await audit("inbound", `The port ${port.name} closed at its handshake: ${handshake.reason}`, {}).catch(() => {
24979
+ });
24980
+ port.disconnect();
24981
+ return;
24982
+ }
24983
+ if (port.name !== "devthinksidepanel" || port.sender?.id !== chrome.runtime.id || !port.sender.url?.startsWith(chrome.runtime.getURL(""))) {
24984
+ port.disconnect();
24985
+ return;
24986
+ }
24987
+ port.onMessage.addListener((message) => {
24988
+ handlerequest(message, port.sender ?? {}).then((value) => port.postMessage({ ok: true, value })).catch((error) => port.postMessage({ ok: false, error: error instanceof Error ? error.message : String(error) }));
24989
+ });
24990
+ })();
23409
24991
  });
23410
24992
  {
23411
24993
  const webnavigation = chrome.webNavigation;
@@ -23491,4 +25073,16 @@ restoretriggers().catch(() => {
23491
25073
  });
23492
25074
  restorerunstates().catch(() => {
23493
25075
  });
25076
+ async function recordinstalledpermdiff() {
25077
+ const manifest = chrome.runtime.getManifest();
25078
+ const permissions = [...(manifest.permissions ?? []).map((permission) => `required:${permission}`), ...(manifest.optional_permissions ?? []).map((permission) => `optional:${permission}`), ...(manifest.optional_host_permissions ?? []).map((host) => `optionalhost:${host}`)];
25079
+ const last = await memory.getlastpermissions();
25080
+ if (last !== void 0 && last.version === manifest.version) return;
25081
+ const diff = permissiondiff({ from: last?.permissions ?? [], to: permissions, fromversion: last?.version ?? "none", toversion: manifest.version, now: Date.now() });
25082
+ await memory.addpermdiff(diff);
25083
+ await memory.setlastpermissions(permissions, manifest.version);
25084
+ await audit("transparency", `The installed update to ${manifest.version} recorded its permdiff: ${permdiffsummary(diff)}`, {});
25085
+ }
25086
+ recordinstalledpermdiff().catch(() => {
25087
+ });
23494
25088
  //# sourceMappingURL=background.js.map