@wenathlan/extension 1.1.61 → 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 (42) hide show
  1. package/README.md +4 -3
  2. package/dist/confirmgates.d.ts +60 -0
  3. package/dist/confirmgates.d.ts.map +1 -0
  4. package/dist/inboundguard.d.ts +86 -0
  5. package/dist/inboundguard.d.ts.map +1 -0
  6. package/dist/index.d.ts +7 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +747 -3
  9. package/dist/index.js.map +4 -4
  10. package/dist/memory.d.ts +89 -1
  11. package/dist/memory.d.ts.map +1 -1
  12. package/dist/originpolicy.d.ts +10 -0
  13. package/dist/originpolicy.d.ts.map +1 -1
  14. package/dist/phishguard.d.ts +24 -0
  15. package/dist/phishguard.d.ts.map +1 -0
  16. package/dist/policy.d.ts +68 -1
  17. package/dist/policy.d.ts.map +1 -1
  18. package/dist/protocol.d.ts +88 -0
  19. package/dist/protocol.d.ts.map +1 -1
  20. package/dist/redactshots.d.ts +54 -0
  21. package/dist/redactshots.d.ts.map +1 -0
  22. package/dist/secretvault.d.ts +89 -0
  23. package/dist/secretvault.d.ts.map +1 -0
  24. package/dist/transparency.d.ts +46 -0
  25. package/dist/transparency.d.ts.map +1 -0
  26. package/dist/types.d.ts +173 -3
  27. package/dist/types.d.ts.map +1 -1
  28. package/dist/version.d.ts +1 -1
  29. package/extension/dist/background.js +984 -54
  30. package/extension/dist/background.js.map +4 -4
  31. package/extension/dist/manifest.json +5 -1
  32. package/extension/dist/pagebridge.js.map +2 -2
  33. package/extension/dist/popup.html +1 -1
  34. package/extension/dist/popup.js +15 -0
  35. package/extension/dist/popup.js.map +2 -2
  36. package/extension/dist/sidepanel.js +52 -0
  37. package/extension/dist/sidepanel.js.map +2 -2
  38. package/extension/dist/transparencypage.html +24 -0
  39. package/extension/dist/transparencypage.js +156 -0
  40. package/extension/dist/transparencypage.js.map +7 -0
  41. package/extension/manifest.json +5 -1
  42. package/package.json +1 -1
@@ -4737,6 +4737,169 @@ var sessionmemory = class {
4737
4737
  }
4738
4738
  return kept;
4739
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
+ }
4740
4903
  };
4741
4904
  function mediakindof(record2) {
4742
4905
  if ("pages" in record2) return "pdf";
@@ -6673,6 +6836,226 @@ function consolediff(input) {
6673
6836
  return { base: input.baseid, target: input.targetid, lines, added: added.length, removed: removed.length, repeated: repeated.length, at: input.now };
6674
6837
  }
6675
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
+
6676
7059
  // policy.ts
6677
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"]);
6678
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"]);
@@ -9923,6 +10306,67 @@ function logreadgate(input) {
9923
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." };
9924
10307
  return { allowed: true, reason: "The log chain verifies from the genesis hash to the last entry; the audit accessor serves the entries." };
9925
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
+ }
9926
10370
 
9927
10371
  // progress.ts
9928
10372
  function emptyprogress(planid, now) {
@@ -9945,6 +10389,10 @@ function recordturnaround2(progress, planid, stepid, milliseconds, now) {
9945
10389
  const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
9946
10390
  return { ...base, turnarounds: { ...base.turnarounds ?? {}, [stepid]: milliseconds }, updatedat: now };
9947
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
+ }
9948
10396
  function iscomplete(progress, plan) {
9949
10397
  if (!progress || progress.planid !== plan.id) return false;
9950
10398
  const required = plan.steps.map((step) => step.id);
@@ -10169,7 +10617,7 @@ function maskexport(record2, shapes) {
10169
10617
  }
10170
10618
 
10171
10619
  // version.ts
10172
- var packageversion = "1.1.61";
10620
+ var packageversion = "1.1.62";
10173
10621
 
10174
10622
  // types.ts
10175
10623
  var protocolversion = packageversion;
@@ -11143,6 +11591,9 @@ function environmentreport(input) {
11143
11591
  ...input.keepalive !== void 0 ? { keepalive: input.keepalive } : {}
11144
11592
  };
11145
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
+ }
11146
11597
 
11147
11598
  // capture.ts
11148
11599
  var capturekinds = ["shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet"];
@@ -11646,7 +12097,7 @@ async function readcapabilities() {
11646
12097
  ]);
11647
12098
  return { tabs, downloads, clipboardread, clipboardwrite, reportedat: Date.now() };
11648
12099
  }
11649
- function stepoptions2(step) {
12100
+ function stepoptions4(step) {
11650
12101
  if (!step.options) return {};
11651
12102
  try {
11652
12103
  const parsed = JSON.parse(step.options);
@@ -11659,7 +12110,7 @@ function tabid(step) {
11659
12110
  return Number.parseInt(step.value ?? "", 10);
11660
12111
  }
11661
12112
  async function runbrowseraction(step, sessiontabid, windowid) {
11662
- const options = stepoptions2(step);
12113
+ const options = stepoptions4(step);
11663
12114
  switch (step.kind) {
11664
12115
  case "tablist": {
11665
12116
  const tabs = await chrome.tabs.query({});
@@ -13119,6 +13570,144 @@ function acceptrenderresult(input) {
13119
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.` };
13120
13571
  }
13121
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
+
13122
13711
  // modelroute.ts
13123
13712
  function routevalid(route) {
13124
13713
  if (route.kind.trim() === "") return { allowed: false, reason: "The model route needs its task kind." };
@@ -14325,13 +14914,28 @@ var chromestorage = {
14325
14914
  }
14326
14915
  };
14327
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
+ })();
14328
14932
  function extensionpage(sender) {
14329
14933
  return sender.id === chrome.runtime.id && Boolean(sender.url?.startsWith(chrome.runtime.getURL("")));
14330
14934
  }
14331
14935
  async function audit(kind, summary, extra = {}) {
14332
14936
  await memory.addaudi({ id: randomid(), kind, at: Date.now(), summary, ...extra });
14333
14937
  }
14334
- function stepoptions3(step) {
14938
+ function stepoptions6(step) {
14335
14939
  try {
14336
14940
  return parseoptions(step);
14337
14941
  } catch {
@@ -14556,6 +15160,12 @@ async function securitystepgate(step, session, origin, settings) {
14556
15160
  const allowverdict = automationallowlistgate({ origin, allowlist: await memory.getautomationallowlist(), session });
14557
15161
  if (!allowverdict.allowed) return { allowed: false, suspended: false, reason: allowverdict.reason ?? "", classification };
14558
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
+ }
14559
15169
  const profileverdict = originprofilegate({ profile, kind: step.kind, sensitive: classification.sensitive });
14560
15170
  if (!profileverdict.allowed) return { allowed: false, suspended: false, reason: profileverdict.reason ?? "", classification };
14561
15171
  const windows = await memory.expireconsentwindows(now);
@@ -14568,8 +15178,115 @@ async function securitystepgate(step, session, origin, settings) {
14568
15178
  const revocation = plan === void 0 ? void 0 : (await memory.getrevocations()).find((candidate) => candidate.sessionid === session.id && candidate.runid === plan.id);
14569
15179
  const revokeverdict = revokerungate({ revocation, sessionid: session.id, runid: plan?.id ?? "" });
14570
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 };
14571
15183
  return { allowed: true, suspended: false, reason: `${classification.reason} ${allowverdict.reason ?? ""} ${windowverdict.reason ?? ""} ${consentverdict.reason ?? ""}`, classification };
14572
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
+ }
14573
15290
  async function sealsessionrunlog(sessionid) {
14574
15291
  const log = await memory.getimmutablelog(sessionid);
14575
15292
  if (!log || log.seal !== void 0 || log.entries.length === 0) return;
@@ -14593,10 +15310,19 @@ async function securityviewof() {
14593
15310
  maskrules: await memory.getmaskrules(),
14594
15311
  chain,
14595
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(),
14596
15321
  ...session ? { sessionorigin: session.origin } : {},
14597
15322
  ...settings?.consentduration !== void 0 ? { promptduration: settings.consentduration } : {},
14598
15323
  ...settings?.logretention !== void 0 ? { logretention: settings.logretention } : {},
14599
- ...settings?.maskshapes !== void 0 ? { maskshapes: settings.maskshapes } : {}
15324
+ ...settings?.maskshapes !== void 0 ? { maskshapes: settings.maskshapes } : {},
15325
+ ...settings?.phishdistance !== void 0 ? { phishdistance: settings.phishdistance } : {}
14600
15326
  };
14601
15327
  }
14602
15328
  async function executeisolatedevaluate(step, tabid2, origin) {
@@ -14618,9 +15344,11 @@ async function executeisolatedevaluate(step, tabid2, origin) {
14618
15344
  return result[0]?.result ?? { ok: false, summary: "The isolated world returned no result." };
14619
15345
  }
14620
15346
  async function executesandboxrender(step, session, plan, origin) {
14621
- const options = stepoptions3(step);
15347
+ const options = stepoptions6(step);
14622
15348
  const markup = typeof options.markup === "string" ? options.markup : "";
14623
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);
14624
15352
  const settings = await memory.getsettings();
14625
15353
  const origingate = sandboxorigingate({ origin: sourceorigin, allowed: settings?.sandboxorigins ?? [] });
14626
15354
  if (!origingate.allowed) throw new Error(origingate.reason);
@@ -14652,7 +15380,7 @@ async function offloadparsetoworker(step, output, session, plan, origin) {
14652
15380
  const ready = await ensureoffscreendocument(runid);
14653
15381
  if (!ready) return { output, turnaround: void 0 };
14654
15382
  const payload = JSON.stringify({ summary: output?.summary ?? "", details: output?.details ?? {} });
14655
- const request = workerrequestof({ id: randomid(), runid, stepid: step.id, kind: step.kind, payload, options: stepoptions3(step), sentat: Date.now() });
15383
+ const request = workerrequestof({ id: randomid(), runid, stepid: step.id, kind: step.kind, payload, options: stepoptions6(step), sentat: Date.now() });
14656
15384
  const started = Date.now();
14657
15385
  let answer;
14658
15386
  try {
@@ -14855,7 +15583,7 @@ function stepauditkind(step, ok) {
14855
15583
  return ok ? "action" : "error";
14856
15584
  }
14857
15585
  function resolvedinnerstep(step, plan) {
14858
- const options = stepoptions3(step);
15586
+ const options = stepoptions6(step);
14859
15587
  if (typeof options.stepid === "string" && options.stepid.trim()) {
14860
15588
  return plan.steps.find((candidate) => candidate.id === options.stepid) ?? null;
14861
15589
  }
@@ -14864,7 +15592,7 @@ function resolvedinnerstep(step, plan) {
14864
15592
  async function executekeyhold(step, session, plan, tabid2, origin) {
14865
15593
  const output = await dispatchpagestep(step, tabid2, origin, plan);
14866
15594
  if (!output?.ok) return output ?? { ok: false, summary: "The key hold was not delivered." };
14867
- const options = stepoptions3(step);
15595
+ const options = stepoptions6(step);
14868
15596
  const holdid = typeof options.holdid === "string" && options.holdid.trim() ? options.holdid : randomid();
14869
15597
  const modifiers = Array.isArray(options.modifiers) ? options.modifiers.filter((item) => typeof item === "string") : [];
14870
15598
  const hold = { holdid, key: step.value ?? "", ...modifiers.length > 0 ? { modifiers } : {}, tabid: tabid2, stepid: step.id, pressedat: Date.now() };
@@ -14897,7 +15625,7 @@ async function executedismissdialog(step, session, plan, tabid2, origin) {
14897
15625
  return { ok: true, summary: `Dialog handler armed${answer} for the next confirm, alert or prompt.` };
14898
15626
  }
14899
15627
  async function executeretryaction(step, session, plan, tabid2, origin) {
14900
- const rule = stepoptions3(step).retryrule;
15628
+ const rule = stepoptions6(step).retryrule;
14901
15629
  const inner = resolvedinnerstep(step, plan);
14902
15630
  if (!inner) throw new Error("The reviewed wrapper step could not be resolved.");
14903
15631
  const innergate = validatestep(inner, origin);
@@ -14933,8 +15661,8 @@ async function executeenterframe(step, plan, tabid2, origin) {
14933
15661
  if (!inner) throw new Error("The reviewed frame wrapper step could not be resolved.");
14934
15662
  const innergate = validatestep(inner, origin);
14935
15663
  if (!innergate.allowed) throw new Error(`The wrapped step is not allowed: ${innergate.reason}`);
14936
- const options = stepoptions3(step);
14937
- const inneroptions = inner.options ? stepoptions3(inner) : void 0;
15664
+ const options = stepoptions6(step);
15665
+ const inneroptions = inner.options ? stepoptions6(inner) : void 0;
14938
15666
  const derived = { ...step, options: JSON.stringify({ ...options, kind: inner.kind, ...inner.target ? { target: inner.target } : {}, ...inner.value ? { value: inner.value } : {}, ...inneroptions ? { options: inneroptions } : {} }) };
14939
15667
  return dispatchpagestep(derived, tabid2, origin, plan);
14940
15668
  }
@@ -14946,7 +15674,7 @@ function detailarray(details, key) {
14946
15674
  return Array.isArray(value) ? value : [];
14947
15675
  }
14948
15676
  async function executediffsnapshots(step, session, plan, tabid2, origin) {
14949
- const options = stepoptions3(step);
15677
+ const options = stepoptions6(step);
14950
15678
  const versions = Array.isArray(options.versions) ? options.versions.filter((item) => typeof item === "number") : [];
14951
15679
  const baseversion = versions[0];
14952
15680
  const targetversion = versions[1];
@@ -14969,7 +15697,7 @@ async function executediffsnapshots(step, session, plan, tabid2, origin) {
14969
15697
  return { ok: Boolean(output.ok), summary: output.summary, details: { ...output.details ?? {}, versions: [baseversion, targetversion] } };
14970
15698
  }
14971
15699
  async function executewatchstep(step, session, plan, tabid2, origin) {
14972
- const options = stepoptions3(step);
15700
+ const options = stepoptions6(step);
14973
15701
  const watchid = typeof options.watchid === "string" && options.watchid.trim() ? options.watchid : randomid();
14974
15702
  const scopes = Array.isArray(options.scopes) ? options.scopes.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
14975
15703
  const events = Array.isArray(options.events) ? options.events.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
@@ -15651,7 +16379,7 @@ function commandtabids(step, options) {
15651
16379
  return listed.length > 0 ? listed : single;
15652
16380
  }
15653
16381
  async function executetabscommand(step, session, plan, sessiontabid) {
15654
- const options = stepoptions3(step);
16382
+ const options = stepoptions6(step);
15655
16383
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
15656
16384
  const windowid = step.value && /^\d+$/.test(step.value) ? Number.parseInt(step.value, 10) : 0;
15657
16385
  const layoutgate = layoutmutationgranted(session, Date.now());
@@ -15927,7 +16655,7 @@ async function executetabscommand(step, session, plan, sessiontabid) {
15927
16655
  }
15928
16656
  }
15929
16657
  async function executesaveprofiles(step, session, origin) {
15930
- const options = stepoptions3(step);
16658
+ const options = stepoptions6(step);
15931
16659
  const record2 = parseformrecord(options.formrecord);
15932
16660
  const name = typeof options.name === "string" ? options.name : "";
15933
16661
  if (!name || !record2) throw new Error("A reviewed profile name and form record are required.");
@@ -15947,7 +16675,7 @@ async function executeasksubmit(step, session, plan, tabid2, origin) {
15947
16675
  return { ok: true, summary: `Asksubmit prompt opened for form ${ticket.form || "the reviewed form"}; the submission waits for your approval.`, details: { ticket, values } };
15948
16676
  }
15949
16677
  async function executesubmitform(step, session, plan, tabid2, origin) {
15950
- const consentref = typeof stepoptions3(step).consentref === "string" ? stepoptions3(step).consentref : "";
16678
+ const consentref = typeof stepoptions6(step).consentref === "string" ? stepoptions6(step).consentref : "";
15951
16679
  const ticket = (await memory.gettickets()).find((item) => item.approved === true && (item.consentref === consentref || item.id === consentref));
15952
16680
  if (!ticket) throw new Error("No approved asksubmit ticket matches the reviewed consent ref; approve the submission in the review panel first.");
15953
16681
  const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The form submission returned no result." };
@@ -15971,7 +16699,7 @@ async function executeretryform(step, session, plan, tabid2, origin) {
15971
16699
  return { ok: Boolean(output?.ok), summary: output?.summary ?? "The retried submission returned no result.", details: { attempts, windows, ok: Boolean(output?.ok) } };
15972
16700
  }
15973
16701
  async function executeconsentpassword(step, session, plan, tabid2, origin) {
15974
- const consentref = typeof stepoptions3(step).consentref === "string" ? stepoptions3(step).consentref : "";
16702
+ const consentref = typeof stepoptions6(step).consentref === "string" ? stepoptions6(step).consentref : "";
15975
16703
  const gate = passwordconsentgranted(step);
15976
16704
  if (!gate.allowed) throw new Error(gate.reason ?? "A password fill requires a reviewed consent ref.");
15977
16705
  const output = await dispatchpagestep(step, tabid2, origin, plan);
@@ -15983,7 +16711,7 @@ async function executeattachfile(step, session, plan, tabid2, origin) {
15983
16711
  const artifacts = await memory.getartifacts();
15984
16712
  const artifact = artifacts.find((item) => item.name === name || item.id === name);
15985
16713
  if (!artifact) throw new Error(`No generated artifact named ${name} exists in the run store yet.`);
15986
- const derived = { ...step, options: JSON.stringify({ ...stepoptions3(step), artifact: artifact.id, artifactname: artifact.name }) };
16714
+ const derived = { ...step, options: JSON.stringify({ ...stepoptions6(step), artifact: artifact.id, artifactname: artifact.name }) };
15987
16715
  const output = await dispatchpagestep(derived, tabid2, origin, plan);
15988
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 });
15989
16717
  return { ...output ?? { ok: false, summary: "The artifact attachment returned no result." }, details: { ...output?.details ?? {}, artifact } };
@@ -16016,7 +16744,7 @@ async function executeformstep(step, session, plan, tabid2, origin) {
16016
16744
  return executecaptchahandoff(step, session, plan, tabid2, origin);
16017
16745
  case "fillcode": {
16018
16746
  const stored = await memory.getcodevalue();
16019
- const source = typeof stepoptions3(step).source === "string" ? stepoptions3(step).source : "";
16747
+ const source = typeof stepoptions6(step).source === "string" ? stepoptions6(step).source : "";
16020
16748
  const derived = stored !== void 0 && source === "reviewed" ? { ...step, value: stored } : step;
16021
16749
  const output = await dispatchpagestep(derived, tabid2, origin, plan);
16022
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 });
@@ -16087,7 +16815,7 @@ async function storeexport(stepid, datasetvalue, format, delimiter, session, pla
16087
16815
  return artifact;
16088
16816
  }
16089
16817
  async function executedatastep(step, session, plan, tabid2, origin) {
16090
- const options = stepoptions3(step);
16818
+ const options = stepoptions6(step);
16091
16819
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
16092
16820
  switch (step.kind) {
16093
16821
  case "scrapetable": {
@@ -16317,7 +17045,7 @@ async function verifyonerecord(record2, expected, extra) {
16317
17045
  return { ok: verification.ok, summary: verification.summary, details: { verification: { ...verification.matches, state: record2.state, path: record2.path, checksum: record2.checksum, bytes: record2.bytes } } };
16318
17046
  }
16319
17047
  async function executefilesstep(step, session, plan, tabid2, origin) {
16320
- const options = stepoptions3(step);
17048
+ const options = stepoptions6(step);
16321
17049
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
16322
17050
  switch (step.kind) {
16323
17051
  case "batchdownload": {
@@ -16546,7 +17274,7 @@ reconcilmimefilter().catch(() => {
16546
17274
  });
16547
17275
  var stitchprogress = /* @__PURE__ */ new Map();
16548
17276
  function stepcaptureoptions(step) {
16549
- return captureoptionsof(stepoptions3(step).capture);
17277
+ return captureoptionsof(stepoptions6(step).capture);
16550
17278
  }
16551
17279
  async function blobtodataurl(blob) {
16552
17280
  const buffer = new Uint8Array(await blob.arrayBuffer());
@@ -16670,7 +17398,7 @@ async function encodecanvas(width, height, draw, options) {
16670
17398
  return canvasdataurl(canvas, options.format, options.quality);
16671
17399
  }
16672
17400
  async function capturenamefor(step, plan, kind, format) {
16673
- const naming = stepoptions3(step).naming;
17401
+ const naming = stepoptions6(step).naming;
16674
17402
  const rule = naming && typeof naming === "object" && !Array.isArray(naming) ? naming : { run: true, step: true, sequence: true, kind: true };
16675
17403
  const counters = (await memory.getcapturecounters()).find((item) => item.taskid === plan.id);
16676
17404
  const advanced = advancecounter(counters?.counters ?? {}, step.id);
@@ -16710,13 +17438,62 @@ async function grabstateshot(step, session, plan, tabid2, phase) {
16710
17438
  return record2;
16711
17439
  }
16712
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);
16713
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 });
16714
17446
  const routed = await routecapture(record2, session, plan, step, origin);
16715
17447
  await memory.setprogress(recordcapture(await memory.getprogress(), plan.id, step.id, record2, Date.now()));
16716
17448
  await refreshbadge();
16717
17449
  return { record: record2, routed };
16718
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
+ }
16719
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) {
16720
17497
  const options = stepcaptureoptions(step);
16721
17498
  const format = options.format ?? "png";
16722
17499
  const ratio = options.pixelratio ?? 1;
@@ -16739,7 +17516,7 @@ async function executecapturestep(step, session, plan, tabid2, origin) {
16739
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 } } };
16740
17517
  }
16741
17518
  if (step.kind === "shotfullpage") {
16742
- const rawoptions = stepoptions3(step);
17519
+ const rawoptions = stepoptions6(step);
16743
17520
  const settle2 = typeof rawoptions.settle === "number" ? rawoptions.settle : 150;
16744
17521
  const overlap = typeof rawoptions.overlap === "number" ? rawoptions.overlap : 0;
16745
17522
  const wait = typeof rawoptions.wait === "number" ? rawoptions.wait : void 0;
@@ -16776,7 +17553,7 @@ async function executecapturestep(step, session, plan, tabid2, origin) {
16776
17553
  }
16777
17554
  if (step.kind === "shotelement") {
16778
17555
  const selector = step.target ?? "";
16779
- const settle2 = typeof stepoptions3(step).settle === "number" ? stepoptions3(step).settle : 150;
17556
+ const settle2 = typeof stepoptions6(step).settle === "number" ? stepoptions6(step).settle : 150;
16780
17557
  const measured = await bridgecall(tabid2, "measurepage");
16781
17558
  const targetinfo = await bridgecall(tabid2, "elementrect", selector);
16782
17559
  if (!targetinfo.ok || !targetinfo.rect) throw new Error(targetinfo.summary);
@@ -16817,7 +17594,7 @@ async function executecapturestep(step, session, plan, tabid2, origin) {
16817
17594
  }
16818
17595
  }
16819
17596
  if (step.kind === "shotregion") {
16820
- const rawoptions = stepoptions3(step);
17597
+ const rawoptions = stepoptions6(step);
16821
17598
  const rect = rawoptions.regionrect;
16822
17599
  if (!rect) throw new Error("A reviewed regionrect is required in options.");
16823
17600
  const container = typeof rawoptions.container === "string" ? rawoptions.container : void 0;
@@ -16858,7 +17635,7 @@ async function executecapturestep(step, session, plan, tabid2, origin) {
16858
17635
  }
16859
17636
  }
16860
17637
  if (step.kind === "contactsheet") {
16861
- const rawoptions = stepoptions3(step);
17638
+ const rawoptions = stepoptions6(step);
16862
17639
  const elements = (Array.isArray(rawoptions.elements) ? rawoptions.elements : []).filter((item) => typeof item === "string" && item.trim().length > 0);
16863
17640
  const layout = rawoptions.sheet && typeof rawoptions.sheet === "object" && !Array.isArray(rawoptions.sheet) ? rawoptions.sheet : { cellsize: 240, columns: 3, label: "both" };
16864
17641
  const measured = await bridgecall(tabid2, "measurepage");
@@ -16969,7 +17746,7 @@ async function thumbonecapture(source, directive, plan, step) {
16969
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 };
16970
17747
  }
16971
17748
  async function executemediastep(step, session, plan, tabid2, origin) {
16972
- const options = stepoptions3(step);
17749
+ const options = stepoptions6(step);
16973
17750
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
16974
17751
  const gate = mediagate(session, tabid2, origin, Date.now());
16975
17752
  if (!gate.allowed) throw new Error(gate.reason ?? "The media capture needs the active session tab grant.");
@@ -17228,7 +18005,7 @@ async function attachapikeys(names, origin) {
17228
18005
  return { headers, keys: attached };
17229
18006
  }
17230
18007
  async function executehttpstep(step, session, plan, tabid2, origin) {
17231
- const options = stepoptions3(step);
18008
+ const options = stepoptions6(step);
17232
18009
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
17233
18010
  if (step.kind === "fetchurl") {
17234
18011
  const request = fetchrequestof(options.fetch);
@@ -17493,7 +18270,7 @@ async function closechannelsforrun(runid) {
17493
18270
  channelbuses.clear();
17494
18271
  }
17495
18272
  async function executesocketstep(step, session, plan, tabid2, origin) {
17496
- const options = stepoptions3(step);
18273
+ const options = stepoptions6(step);
17497
18274
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
17498
18275
  if (step.kind === "opensocket") {
17499
18276
  const channel = channeloptionsof(options.socket);
@@ -17627,7 +18404,7 @@ async function executesocketstep(step, session, plan, tabid2, origin) {
17627
18404
  throw new Error("Unsupported socket observation kind.");
17628
18405
  }
17629
18406
  async function executenetwatchstep(step, session, plan, tabid2, origin) {
17630
- const options = stepoptions3(step);
18407
+ const options = stepoptions6(step);
17631
18408
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
17632
18409
  if (step.kind === "watchrequests") {
17633
18410
  const watch = options.watch && typeof options.watch === "object" && !Array.isArray(options.watch) ? options.watch : {};
@@ -17779,7 +18556,7 @@ function timelinedetail(entry, runid) {
17779
18556
  return { id: randomid(), runid, stepid: record2.stepid, time: record2.time, level: record2.level, source: record2.source, message: record2.message };
17780
18557
  }
17781
18558
  async function executetimelinestep(step, session, plan, tabid2, origin) {
17782
- const options = stepoptions3(step);
18559
+ const options = stepoptions6(step);
17783
18560
  const watch = options.watch && typeof options.watch === "object" && !Array.isArray(options.watch) ? options.watch : {};
17784
18561
  const watchwindow = typeof watch.window === "number" && Number.isFinite(watch.window) && watch.window >= 0 ? watch.window : 0;
17785
18562
  const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
@@ -17934,7 +18711,7 @@ function pauseframes(entry) {
17934
18711
  });
17935
18712
  }
17936
18713
  async function executecdpstep(step, session, plan, tabid2, origin) {
17937
- const options = stepoptions3(step);
18714
+ const options = stepoptions6(step);
17938
18715
  const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
17939
18716
  const debuggatecheck = debuggate(session, tabid2, origin, Date.now());
17940
18717
  if (!debuggatecheck.allowed) throw new Error(debuggatecheck.reason ?? "The devtools protocol step stays outside the debug gate.");
@@ -18168,7 +18945,7 @@ async function sampleheapforstep(tracker, stepid, tabid2, origin, plan) {
18168
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.` });
18169
18946
  }
18170
18947
  async function executeprofilestep(step, session, plan, tabid2, origin) {
18171
- const options = stepoptions3(step);
18948
+ const options = stepoptions6(step);
18172
18949
  const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
18173
18950
  const targets = profiletargetsof(options);
18174
18951
  const grants = await memory.getdebuggergrants();
@@ -18497,7 +19274,7 @@ async function controlledfetch(runid, url, init, controller, window2, streamstat
18497
19274
  return response;
18498
19275
  }
18499
19276
  async function executenetcontrolstep(step, session, plan, tabid2, origin) {
18500
- const options = stepoptions3(step);
19277
+ const options = stepoptions6(step);
18501
19278
  const extra = { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id };
18502
19279
  const ruleset = rulesetof(plan.id);
18503
19280
  if (step.kind === "blockrequest") {
@@ -18744,7 +19521,7 @@ async function enforcewindowreview(step, session, plan) {
18744
19521
  const progress = plan ? await memory.getprogress() : void 0;
18745
19522
  const tasktabids = plan ? trackedtasktabs(progress, plan.id) : [];
18746
19523
  const count = tasktabsinwindow(await livetabs(), windowid, tasktabids);
18747
- const gate = windowclosegate(count, stepoptions3(step).reviewed === true);
19524
+ const gate = windowclosegate(count, stepoptions6(step).reviewed === true);
18748
19525
  if (!gate.allowed) throw new Error(gate.reason ?? "The window close needs explicit review.");
18749
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 });
18750
19527
  }
@@ -18830,7 +19607,7 @@ async function revertemulationforrun(runid, reason, tabid2) {
18830
19607
  }
18831
19608
  }
18832
19609
  async function executeemulationstep(step, session, plan, tabid2, origin) {
18833
- const options = stepoptions3(step);
19610
+ const options = stepoptions6(step);
18834
19611
  const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
18835
19612
  const revertplan = revertplanof(options.revertplan) ?? [];
18836
19613
  const family = familyofkind(step.kind) ?? "device";
@@ -18962,7 +19739,7 @@ async function performrestore(record2, restore, session) {
18962
19739
  return { restored, skippedorigins: grantscheck.skippedorigins };
18963
19740
  }
18964
19741
  async function executesessionstep(step, session, plan, tabid2, origin) {
18965
- const options = stepoptions3(step);
19742
+ const options = stepoptions6(step);
18966
19743
  const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
18967
19744
  if (step.kind === "persiststate") {
18968
19745
  const progress = await memory.getprogress();
@@ -19107,7 +19884,7 @@ async function dispatchworkflowstep(step, context) {
19107
19884
  return { ok: Boolean(output.ok), summary: output.summary, ...output.details !== void 0 ? { details: output.details } : {} };
19108
19885
  }
19109
19886
  async function executedelaystep(step) {
19110
- const options = stepoptions3({ 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 } : {} });
19111
19888
  const delay = delayof(options.delay);
19112
19889
  const sampled = delayjitter(delay, hashseed(`${step.id}:${Date.now()}`));
19113
19890
  const transport = await sleepreviewed(sampled, step.id);
@@ -19154,7 +19931,7 @@ async function sleepreviewed(sampled, stepid) {
19154
19931
  return "timer";
19155
19932
  }
19156
19933
  async function executewaitelement(step, tabid2) {
19157
- const options = stepoptions3({ id: step.id, kind: step.kind, summary: step.label, risk: "read", ...step.target !== void 0 ? { target: step.target } : {}, ...step.options !== void 0 ? { options: step.options } : {} });
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 } : {} });
19158
19935
  const wait = waitof(options.wait, step.target);
19159
19936
  const startedat = Date.now();
19160
19937
  const starttab = await chrome.tabs.get(tabid2).catch(() => void 0);
@@ -19182,7 +19959,7 @@ function waitof(value, target) {
19182
19959
  return { selector, timeout, poll };
19183
19960
  }
19184
19961
  async function executecomputestep(step, session) {
19185
- const options = stepoptions3({ 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 } : {} });
19186
19963
  const expression = options.expression;
19187
19964
  if (!expression || typeof expression !== "object") throw new Error("The compute step needs a reviewed expression.");
19188
19965
  const scopes = runscopes(options.variables);
@@ -19191,7 +19968,7 @@ async function executecomputestep(step, session) {
19191
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 } };
19192
19969
  }
19193
19970
  async function executeextractvarsstep(step, session) {
19194
- const options = stepoptions3({ id: step.id, kind: step.kind, summary: step.label, risk: "read", ...step.value !== void 0 ? { value: step.value } : {}, ...step.options !== void 0 ? { options: step.options } : {} });
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 } : {} });
19195
19972
  const rule = options.rule;
19196
19973
  if (!rule || typeof rule !== "object" || typeof rule.pattern !== "string") throw new Error("The variable extraction needs a reviewed regex rule.");
19197
19974
  const text2 = typeof options.text === "string" ? options.text : step.value ?? "";
@@ -19204,7 +19981,7 @@ async function executeextractvarsstep(step, session) {
19204
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 } };
19205
19982
  }
19206
19983
  async function executeworkflowstep(step, session, plan, tabid2, origin) {
19207
- const options = stepoptions3(step);
19984
+ const options = stepoptions6(step);
19208
19985
  if (step.kind === "composeworkflow") {
19209
19986
  const payload = options.workflow;
19210
19987
  if (!payload || typeof payload !== "object" || Array.isArray(payload)) throw new Error("The workflow composition needs the reviewed workflow payload.");
@@ -19260,7 +20037,7 @@ function workflowstepofentry(value) {
19260
20037
  return blockinvocationof(value);
19261
20038
  }
19262
20039
  async function executeworkflowrun(step, session, plan, tabid2, origin, dry) {
19263
- const options = stepoptions3(step);
20040
+ const options = stepoptions6(step);
19264
20041
  const workflowid = typeof options.workflowid === "string" ? options.workflowid : "";
19265
20042
  const storedrecord = await memory.getworkflowrecord(workflowid);
19266
20043
  if (!storedrecord) throw new Error(`No composed workflow matches ${workflowid || "the reviewed id"}.`);
@@ -19423,7 +20200,7 @@ async function storetimeoutabort(run, step, message, budget) {
19423
20200
  return { run: aborted, log: [entry] };
19424
20201
  }
19425
20202
  async function executetriggerstep(step, session, plan, tabid2, origin) {
19426
- const options = stepoptions3(step);
20203
+ const options = stepoptions6(step);
19427
20204
  const family = triggerfamilyof(step.kind);
19428
20205
  if (!family) throw new Error(`The ${step.kind} step is not a reviewed trigger kind.`);
19429
20206
  const workflowid = typeof options.workflowid === "string" ? options.workflowid : "";
@@ -19649,6 +20426,7 @@ async function executeaction(step, session, plan, tabid2, origin, settings, verd
19649
20426
  }
19650
20427
  throw new Error(securityverdict.reason);
19651
20428
  }
20429
+ step = await resolvevaultvalues(step, session);
19652
20430
  if (session && plan && plan.state === "approved" && mode === "plan") await openplanrun(session, plan);
19653
20431
  if (plan && plan.state === "approved") await markpendingstep(plan, step.id);
19654
20432
  if (routing.environment === "sandboxframe") return executesandboxrender(step, session, plan, origin);
@@ -19825,7 +20603,7 @@ async function previewstep(stepid) {
19825
20603
  if (!step) throw new Error("Reviewed step was not found.");
19826
20604
  const gate = canpreview({ session, plan, step, tabid: tab.id, origin });
19827
20605
  if (!gate.allowed) throw new Error(gate.reason);
19828
- if (!step.target && !stepoptions3(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.");
19829
20607
  const result = await chrome.scripting.executeScript({ target: { tabId: tab.id }, func: (action, expectedorigin) => {
19830
20608
  const bridge = globalThis.devthinkbridge;
19831
20609
  if (!bridge) throw new Error("Devthink page bridge is unavailable.");
@@ -19876,8 +20654,37 @@ async function extractionreportValue() {
19876
20654
  async function provenancereportValue() {
19877
20655
  return provenancereport({ records: await memory.getprovenances() });
19878
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
+ }
19879
20672
  async function handlerequest(message, sender) {
19880
- 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
+ }
19881
20688
  const input = message;
19882
20689
  switch (input.kind) {
19883
20690
  case "configure": {
@@ -23048,6 +23855,12 @@ async function handlerequest(message, sender) {
23048
23855
  }
23049
23856
  throw new Error("The swarm merge request carries no merge, report, export, compare, lesson, costs, timeline, replay or snapshot action.");
23050
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
+ }
23051
23864
  case "security": {
23052
23865
  const input2 = message;
23053
23866
  const now = Date.now();
@@ -23185,10 +23998,103 @@ async function handlerequest(message, sender) {
23185
23998
  }
23186
23999
  if (input2.settings.logretention !== void 0) patch.logretention = input2.settings.logretention;
23187
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;
23188
24007
  await memory.setsettings(patch);
23189
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.`, {});
23190
24009
  return { ...await securityviewof(), configured: true };
23191
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
+ }
23192
24098
  const plan = await memory.getplan();
23193
24099
  const pending = [];
23194
24100
  if (session && plan && ["pending", "approved"].includes(plan.state)) {
@@ -23646,7 +24552,7 @@ async function raiseremoteapproval(clientid, toolname, params, step) {
23646
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 };
23647
24553
  }
23648
24554
  async function executelistruns(step, session) {
23649
- const options = stepoptions3(step);
24555
+ const options = stepoptions6(step);
23650
24556
  const statefilter = typeof options.state === "string" && options.state.trim() !== "" ? options.state : void 0;
23651
24557
  const runs = await memory.listworkflowruns();
23652
24558
  const selected = statefilter !== void 0 ? runs.filter((run) => run.state === statefilter) : runs;
@@ -24021,7 +24927,7 @@ async function maybeautosnapshot() {
24021
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 });
24022
24928
  return;
24023
24929
  }
24024
- const options = stepoptions3(step);
24930
+ const options = stepoptions6(step);
24025
24931
  const snapshot2 = snapshotplanof(options.snapshot);
24026
24932
  if (!snapshot2) return;
24027
24933
  const record2 = await capturesessionrecord({ ...snapshot2, ...snapshot2.auto !== void 0 ? { auto: snapshot2.auto } : {} }, session, plan.id).catch(() => void 0);
@@ -24066,10 +24972,22 @@ async function restoreemulationstate() {
24066
24972
  restoreemulationstate().catch(() => {
24067
24973
  });
24068
24974
  chrome.runtime.onConnect.addListener((port) => {
24069
- if (port.name !== "devthinksidepanel" || port.sender?.id !== chrome.runtime.id || !port.sender.url?.startsWith(chrome.runtime.getURL(""))) return port.disconnect();
24070
- port.onMessage.addListener((message) => {
24071
- 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) }));
24072
- });
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
+ })();
24073
24991
  });
24074
24992
  {
24075
24993
  const webnavigation = chrome.webNavigation;
@@ -24155,4 +25073,16 @@ restoretriggers().catch(() => {
24155
25073
  });
24156
25074
  restorerunstates().catch(() => {
24157
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
+ });
24158
25088
  //# sourceMappingURL=background.js.map