@wenathlan/extension 1.1.59 → 1.1.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +5 -3
  2. package/dist/cli.js +19 -2
  3. package/dist/environments.d.ts +96 -0
  4. package/dist/environments.d.ts.map +1 -0
  5. package/dist/immutablelog.d.ts +73 -0
  6. package/dist/immutablelog.d.ts.map +1 -0
  7. package/dist/index.d.ts +7 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1092 -1
  10. package/dist/index.js.map +4 -4
  11. package/dist/maskinputs.d.ts +52 -0
  12. package/dist/maskinputs.d.ts.map +1 -0
  13. package/dist/memory.d.ts +119 -1
  14. package/dist/memory.d.ts.map +1 -1
  15. package/dist/originpolicy.d.ts +150 -0
  16. package/dist/originpolicy.d.ts.map +1 -0
  17. package/dist/policy.d.ts +87 -1
  18. package/dist/policy.d.ts.map +1 -1
  19. package/dist/protocol.d.ts +121 -2
  20. package/dist/protocol.d.ts.map +1 -1
  21. package/dist/runstate.d.ts +127 -0
  22. package/dist/runstate.d.ts.map +1 -0
  23. package/dist/sandboxframe.d.ts +45 -0
  24. package/dist/sandboxframe.d.ts.map +1 -0
  25. package/dist/types.d.ts +312 -2
  26. package/dist/types.d.ts.map +1 -1
  27. package/dist/version.d.ts +1 -1
  28. package/extension/dist/background.js +1629 -50
  29. package/extension/dist/background.js.map +4 -4
  30. package/extension/dist/manifest.json +16 -2
  31. package/extension/dist/offscreen.html +7 -0
  32. package/extension/dist/offscreen.js +87 -0
  33. package/extension/dist/offscreen.js.map +7 -0
  34. package/extension/dist/pagebridge.js +98 -28
  35. package/extension/dist/pagebridge.js.map +3 -3
  36. package/extension/dist/popup.html +1 -1
  37. package/extension/dist/popup.js +77 -0
  38. package/extension/dist/popup.js.map +2 -2
  39. package/extension/dist/sandbox.html +28 -0
  40. package/extension/dist/sidepanel.html +1 -1
  41. package/extension/dist/sidepanel.js +572 -0
  42. package/extension/dist/sidepanel.js.map +4 -4
  43. package/extension/manifest.json +16 -2
  44. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2014,6 +2014,237 @@ function swarmstateof(input) {
2014
2014
  return { agents: input.agents, queue: input.queue, mailboxes: input.mailboxes, killswitch: input.killswitch };
2015
2015
  }
2016
2016
 
2017
+ // runstate.ts
2018
+ function openrun(input) {
2019
+ if (input.runid.trim() === "" || input.sessionid.trim() === "") throw new Error("The run state needs its run and session ids.");
2020
+ if (!Number.isFinite(input.interval) || input.interval <= 0) throw new Error("The keepalive heartbeat interval stays a positive user value in milliseconds.");
2021
+ return {
2022
+ runid: input.runid,
2023
+ sessionid: input.sessionid,
2024
+ planid: input.planid,
2025
+ profileid: input.profileid,
2026
+ state: "active",
2027
+ urlhistory: [],
2028
+ environments: {},
2029
+ turnarounds: {},
2030
+ keepalive: { runid: input.runid, sessionid: input.sessionid, state: "active", startedat: input.now, interval: input.interval, beats: 0, lastbeatat: input.now, portopen: true, events: [{ kind: "start", at: input.now, detail: `The keepalive port opens for the run ${input.runid} and beats every ${input.interval} milliseconds.` }] },
2031
+ updatedat: input.now
2032
+ };
2033
+ }
2034
+ function beatrun(state, now) {
2035
+ if (state.keepalive.state !== "active") throw new Error(`The run ${state.runid} is ${state.keepalive.state}; a stopped run emits no heartbeat.`);
2036
+ return {
2037
+ ...state,
2038
+ keepalive: { ...state.keepalive, beats: state.keepalive.beats + 1, lastbeatat: now, events: [...state.keepalive.events, { kind: "heartbeat", at: now }].slice(-200) },
2039
+ updatedat: now
2040
+ };
2041
+ }
2042
+ function closerun(state, now) {
2043
+ if (state.keepalive.state === "stopped") throw new Error(`The run ${state.runid} already stopped its keepalive port.`);
2044
+ return {
2045
+ ...state,
2046
+ state: "completed",
2047
+ keepalive: { ...state.keepalive, state: "stopped", stoppedat: now, portopen: false, events: [...state.keepalive.events, { kind: "stop", at: now, detail: "The run reached a terminal state and the keepalive port closed." }].slice(-200) },
2048
+ updatedat: now
2049
+ };
2050
+ }
2051
+ function reattachrun(state, now) {
2052
+ if (state.keepalive.state !== "active") throw new Error(`The run ${state.runid} is ${state.keepalive.state}; a stopped run never reattaches.`);
2053
+ return {
2054
+ ...state,
2055
+ state: "recovered",
2056
+ keepalive: { ...state.keepalive, portopen: true, events: [...state.keepalive.events, { kind: "reattach", at: now, detail: "The service worker restarted and the keepalive port reattached from the persisted run state." }].slice(-200) },
2057
+ updatedat: now
2058
+ };
2059
+ }
2060
+ function recordurl(state, entry) {
2061
+ if (entry.url.trim() === "") throw new Error("The url history entry needs its url.");
2062
+ const record2 = { url: entry.url, stepid: entry.stepid, at: entry.now };
2063
+ return { ...state, urlhistory: [...state.urlhistory.filter((item) => !(item.url === record2.url && item.stepid === record2.stepid)), record2], updatedat: entry.now };
2064
+ }
2065
+ function recordenvironment(state, input) {
2066
+ if (input.stepid.trim() === "") throw new Error("The environment record needs its step id.");
2067
+ const provenance = { origin: input.origin, stepid: input.stepid, environment: input.environment };
2068
+ return { ...state, environments: { ...state.environments, [input.stepid]: input.environment }, lastprovenance: provenance, updatedat: input.now };
2069
+ }
2070
+ function recordturnaround(state, input) {
2071
+ if (input.stepid.trim() === "") throw new Error("The turnaround record needs its step id.");
2072
+ if (!Number.isFinite(input.milliseconds) || input.milliseconds < 0) throw new Error("The worker turnaround stays a non-negative duration in milliseconds.");
2073
+ return { ...state, turnarounds: { ...state.turnarounds, [input.stepid]: input.milliseconds }, updatedat: input.now };
2074
+ }
2075
+ function markpending(state, stepid, now) {
2076
+ return { ...state, ...stepid !== void 0 && stepid.trim() !== "" ? { pendingstepid: stepid } : {}, updatedat: now };
2077
+ }
2078
+ function recoveryplan(state) {
2079
+ if (state.state === "completed") return { runid: state.runid, recoverable: false, reason: `The run ${state.runid} completed before the restart; nothing resumes.` };
2080
+ if (state.state === "reaped") return { runid: state.runid, recoverable: false, reason: `The run ${state.runid} was reaped as a zombie; the user starts a fresh reviewed run.` };
2081
+ if (state.pendingstepid === void 0 || state.pendingstepid.trim() === "") return { runid: state.runid, recoverable: false, reason: `The run ${state.runid} carries no pending step; a fresh reviewed run starts over instead of guessing.` };
2082
+ return { runid: state.runid, pendingstepid: state.pendingstepid, recoverable: true, reason: `The executor resumes the pending step ${state.pendingstepid} of the run ${state.runid} from the persisted run state.` };
2083
+ }
2084
+ function zombiesweep(input) {
2085
+ if (!Number.isFinite(input.interval) || input.interval <= 0) throw new Error("The zombie sweep needs its heartbeat interval as a positive user value.");
2086
+ if (!Number.isInteger(input.missedlimit) || input.missedlimit < 1) throw new Error("The zombie tolerance stays a positive whole number of silent intervals.");
2087
+ const silentfor = input.interval * input.missedlimit;
2088
+ const zombies = input.states.filter((state) => state.keepalive.state === "active" && input.now - state.keepalive.lastbeatat > silentfor);
2089
+ if (zombies.length === 0) return { states: input.states, reaped: [] };
2090
+ const ids = new Set(zombies.map((state) => state.runid));
2091
+ return { states: input.states.map((state) => ids.has(state.runid) ? { ...state, state: "reaped", keepalive: { ...state.keepalive, state: "stopped", portopen: false, ...state.keepalive.stoppedat === void 0 ? { stoppedat: input.now } : {}, events: [...state.keepalive.events, { kind: "stop", at: input.now, detail: "The zombie reaper closed the silent run." }] } } : state), reaped: [...ids] };
2092
+ }
2093
+ function acquirerunlock(input) {
2094
+ if (input.sessionid.trim() === "" || input.runid.trim() === "") throw new Error("The run lock needs its session and run ids.");
2095
+ const live = input.locks.filter((lock2) => lock2.sessionid === input.sessionid && (lock2.expiresat === void 0 || lock2.expiresat > input.now));
2096
+ const held = live.find((lock2) => lock2.runid !== input.runid);
2097
+ if (held) return { locks: input.locks, acquired: false, reason: `The session ${input.sessionid} already holds the run ${held.runid}; a session never carries two concurrent runs.` };
2098
+ const own = input.locks.find((lock2) => lock2.sessionid === input.sessionid && lock2.runid === input.runid);
2099
+ if (own) return { locks: input.locks, acquired: true, reason: `The run ${input.runid} of the session ${input.sessionid} already holds its lock.` };
2100
+ const lock = { sessionid: input.sessionid, runid: input.runid, holder: input.holder, acquiredat: input.now, ...input.expiresat !== void 0 ? { expiresat: input.expiresat } : {} };
2101
+ return { locks: [...input.locks.filter((entry) => entry.sessionid !== input.sessionid), lock], acquired: true, reason: `The run ${input.runid} locked the session ${input.sessionid} against concurrent runs.` };
2102
+ }
2103
+ function releaserunlock(input) {
2104
+ const lock = input.locks.find((entry) => entry.sessionid === input.sessionid);
2105
+ if (!lock || lock.runid !== input.runid) return { locks: input.locks, released: false, reason: `The run ${input.runid} holds no lock of the session ${input.sessionid}.` };
2106
+ return { locks: input.locks.filter((entry) => entry.sessionid !== input.sessionid), released: true, reason: `The run ${input.runid} released the run lock of the session ${input.sessionid}.` };
2107
+ }
2108
+ function expirerunlocks(locks, now) {
2109
+ const expired = locks.filter((lock) => lock.expiresat !== void 0 && now > lock.expiresat);
2110
+ if (expired.length === 0) return { locks, expired: [] };
2111
+ const ids = new Set(expired.map((lock) => lock.sessionid));
2112
+ return { locks: locks.filter((lock) => !ids.has(lock.sessionid)), expired: [...ids] };
2113
+ }
2114
+ function serializesteps(input) {
2115
+ const shared = /* @__PURE__ */ new Set();
2116
+ const counts = /* @__PURE__ */ new Map();
2117
+ for (const branch of input.branches) for (const step of branch.steps) counts.set(step.tabid, (counts.get(step.tabid) ?? 0) + 1);
2118
+ for (const [tabid, count] of counts) if (count > 1) shared.add(tabid);
2119
+ const order = [];
2120
+ let cursor = 0;
2121
+ for (const branch of input.branches) {
2122
+ for (const step of branch.steps) {
2123
+ if (shared.has(step.tabid)) {
2124
+ order.push({ branchid: branch.branchid, stepid: step.stepid, tabid: step.tabid, order: cursor });
2125
+ cursor += 1;
2126
+ }
2127
+ }
2128
+ }
2129
+ for (const branch of input.branches) {
2130
+ for (const step of branch.steps) {
2131
+ if (!shared.has(step.tabid)) {
2132
+ order.push({ branchid: branch.branchid, stepid: step.stepid, tabid: step.tabid, order: cursor });
2133
+ cursor += 1;
2134
+ }
2135
+ }
2136
+ }
2137
+ return order;
2138
+ }
2139
+ async function sealrunstate(state) {
2140
+ const payload = JSON.stringify(state);
2141
+ const digest = await sha256(payload);
2142
+ return { payload, algorithm: "sha-256", digest, sealedat: state.updatedat };
2143
+ }
2144
+ async function openseal(sealed) {
2145
+ const digest = await sha256(sealed.payload);
2146
+ if (digest !== sealed.digest) throw new Error("The sealed run state fails its integrity digest; a tampered run state never reaches the recovery.");
2147
+ const parsed = JSON.parse(sealed.payload);
2148
+ if (typeof parsed.runid !== "string" || typeof parsed.sessionid !== "string") throw new Error("The sealed run state carries no run record.");
2149
+ return parsed;
2150
+ }
2151
+ async function sha256(value) {
2152
+ const bytes = new TextEncoder().encode(value);
2153
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
2154
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
2155
+ }
2156
+ function prunerunstates(input) {
2157
+ if (!Number.isFinite(input.ceiling) || input.ceiling <= 0) return { states: input.states, pruned: [], reason: "No storage ceiling is configured, so the run states stay under the user choice alone." };
2158
+ if (input.used <= input.ceiling) return { states: input.states, pruned: [], reason: `The run state storage sits at ${input.used} of the ${input.ceiling} bytes the user configured; no pressure exists.` };
2159
+ const removable = input.states.filter((state) => state.state === "completed" || state.state === "reaped").sort((one, two) => one.updatedat - two.updatedat);
2160
+ const states = [...input.states];
2161
+ const pruned = [];
2162
+ for (const candidate of removable) {
2163
+ pruned.push(candidate.runid);
2164
+ const index = states.findIndex((state) => state.runid === candidate.runid);
2165
+ if (index >= 0) states.splice(index, 1);
2166
+ if (pruned.length >= Math.max(1, Math.ceil(input.states.length / 2))) break;
2167
+ }
2168
+ return { states, pruned, reason: `The storage pressure at ${input.used} of ${input.ceiling} bytes pruned the ${pruned.length} oldest finished run state${pruned.length === 1 ? "" : "s"} while every active run keeps its state.` };
2169
+ }
2170
+ function exportrunstate(states, now) {
2171
+ return {
2172
+ runs: states.length,
2173
+ urls: states.reduce((total, state) => total + state.urlhistory.length, 0),
2174
+ environments: states.reduce((total, state) => total + Object.keys(state.environments).length, 0),
2175
+ offloaded: states.reduce((total, state) => total + Object.values(state.turnarounds).length, 0),
2176
+ beats: states.reduce((total, state) => total + state.keepalive.beats, 0),
2177
+ exportedat: now
2178
+ };
2179
+ }
2180
+
2181
+ // immutablelog.ts
2182
+ async function sha2562(payload) {
2183
+ const bytes = new TextEncoder().encode(payload);
2184
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
2185
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
2186
+ }
2187
+ function entrybody(entry) {
2188
+ return JSON.stringify({ id: entry.id, runid: entry.runid, kind: entry.kind, summary: entry.summary, origin: entry.origin, ...entry.stepid !== void 0 ? { stepid: entry.stepid } : {}, at: entry.at });
2189
+ }
2190
+ async function entryhashof(input) {
2191
+ return { previous: input.previous, current: await sha2562(`${input.previous}
2192
+ ${entrybody(input.entry)}`), algorithm: "sha-256" };
2193
+ }
2194
+ async function logentryof(input) {
2195
+ if (input.summary.trim() === "") throw new Error("The log entry needs its summary in plain language.");
2196
+ if (input.origin.trim() === "") throw new Error("The log entry needs its origin provenance.");
2197
+ const entry = { id: input.id ?? randomid(), runid: input.runid, kind: input.kind, summary: input.summary, origin: input.origin, ...input.stepid !== void 0 ? { stepid: input.stepid } : {}, at: input.at };
2198
+ return { ...entry, hash: await entryhashof({ previous: input.previous, entry }) };
2199
+ }
2200
+ function openrunlog(input) {
2201
+ if (input.runid.trim() === "" || input.sessionid.trim() === "") throw new Error("The run log needs its run and session ids.");
2202
+ return { runid: input.runid, sessionid: input.sessionid, entries: [], updatedat: input.now };
2203
+ }
2204
+ function lasthashof(log) {
2205
+ const entry = log.entries[log.entries.length - 1];
2206
+ return entry === void 0 ? "0".repeat(64) : entry.hash.current;
2207
+ }
2208
+ async function appendlogentry(input) {
2209
+ if (input.log.seal !== void 0) throw new Error(`The run log of ${input.log.runid} sealed at ${input.log.seal.sealedat} and accepts no append; the seal is terminal.`);
2210
+ const entry = await logentryof({ ...input.id !== void 0 ? { id: input.id } : {}, runid: input.log.runid, kind: input.kind, summary: input.summary, origin: input.origin, ...input.stepid !== void 0 ? { stepid: input.stepid } : {}, at: input.at, previous: lasthashof(input.log) });
2211
+ return { ...input.log, entries: [...input.log.entries, entry], updatedat: input.at };
2212
+ }
2213
+ async function sealrunlog(log, now) {
2214
+ if (log.seal !== void 0) throw new Error(`The run log of ${log.runid} already sealed at ${log.seal.sealedat}; the seal is terminal.`);
2215
+ if (log.entries.length === 0) throw new Error("The run log seals at completion with at least one entry.");
2216
+ const sealhash = await entryhashof({ previous: lasthashof(log), entry: { id: `seal:${log.runid}`, runid: log.runid, kind: "seal", summary: `The run ${log.runid} completed and the log sealed with ${log.entries.length} entries.`, origin: log.entries[log.entries.length - 1]?.origin ?? log.runid, at: now } });
2217
+ const seal = { runid: log.runid, entries: log.entries.length, sealhash, sealedat: now };
2218
+ return { log: { ...log, seal, updatedat: now }, seal };
2219
+ }
2220
+ async function verifylogchain(entries) {
2221
+ let previous = "0".repeat(64);
2222
+ for (let index = 0; index < entries.length; index += 1) {
2223
+ const entry = entries[index];
2224
+ if (entry === void 0) continue;
2225
+ if (entry.hash.previous !== previous) return { valid: false, brokenat: index, reason: `The chain link of entry ${index} carries the previous hash ${entry.hash.previous} while its predecessor hashes to ${previous}; the chain reports tamper evidence.` };
2226
+ const expected = await entryhashof({ previous, entry: { id: entry.id, runid: entry.runid, kind: entry.kind, summary: entry.summary, origin: entry.origin, ...entry.stepid !== void 0 ? { stepid: entry.stepid } : {}, at: entry.at } });
2227
+ if (entry.hash.current !== expected.current) return { valid: false, brokenat: index, reason: `The entry hash of entry ${index} matches neither its body nor its predecessor hash; the chain reports tamper evidence.` };
2228
+ previous = entry.hash.current;
2229
+ }
2230
+ return { valid: true, reason: `The hash chain of ${entries.length} entr${entries.length === 1 ? "y" : "ies"} verifies from the genesis hash to the last entry.` };
2231
+ }
2232
+ async function readverifiedlog(log) {
2233
+ const verification = await verifylogchain(log.entries);
2234
+ if (!verification.valid) return { ok: false, entries: [], reason: verification.reason };
2235
+ return { ok: true, entries: [...log.entries], reason: verification.reason };
2236
+ }
2237
+ async function chainreportof(log) {
2238
+ const verification = await verifylogchain(log.entries);
2239
+ if (!verification.valid) return { runid: log.runid, valid: false, entries: log.entries.length, ...verification.brokenat !== void 0 ? { brokenat: verification.brokenat } : {}, reason: verification.reason };
2240
+ return { runid: log.runid, valid: true, entries: log.entries.length, reason: verification.reason, ...log.seal !== void 0 ? { sealhash: log.seal.sealhash.current, sealedat: log.seal.sealedat } : {} };
2241
+ }
2242
+ async function exportlogchain(log) {
2243
+ const read = await readverifiedlog(log);
2244
+ if (!read.ok) return { runid: log.runid, entries: 0, chainvalid: false, reason: read.reason, log: [] };
2245
+ return { runid: log.runid, entries: read.entries.length, chainvalid: true, reason: read.reason, ...log.seal !== void 0 ? { sealhash: log.seal.sealhash.current, sealedat: log.seal.sealedat } : {}, log: read.entries };
2246
+ }
2247
+
2017
2248
  // memory.ts
2018
2249
  var sessionmemory = class {
2019
2250
  constructor(adapter) {
@@ -4358,6 +4589,254 @@ var sessionmemory = class {
4358
4589
  async getswarmcosts() {
4359
4590
  return await this.adapter.get("swarmcosts") ?? [];
4360
4591
  }
4592
+ /**
4593
+ * Execution environment persistence of the 1.1.60 family.
4594
+ * The run state store seals every persisted run state with its sha-256 integrity digest through the storage api (the browser offers no at-rest encryption for its storage areas, so the honest derivation is the integrity seal that makes tampering detectable before any recovery uses the record), scopes every run state per profile so parallel profiles never share it, expires stale run state past the user configured window while the keepalive summaries survive, tracks the storage quota usage of the run state and prunes the oldest finished run states under pressure.
4595
+ * The adapter seam keeps every accessor a one line storage delegation so a future worker state backend replaces the adapter only.
4596
+ */
4597
+ /** Returns the environment grant list of the active session; an absent list keeps the documented default posture. */
4598
+ async getenvironmentgrants() {
4599
+ return (await this.getsession())?.environmentgrants;
4600
+ }
4601
+ /** Replaces the environment grant list of the active session so the environment grants join the origin grants in the session record. */
4602
+ async setenvironmentgrants(grants) {
4603
+ const session = await this.getsession();
4604
+ if (!session) throw new Error("The environment grants need an active session to join.");
4605
+ await this.setsession({ ...session, environmentgrants: grants });
4606
+ }
4607
+ /** Seals and stores the run state of one profile: the payload travels beside its sha-256 digest so a tampered record at rest stays detectable before any recovery uses it. */
4608
+ async setrunstate(profileid, state) {
4609
+ const sealed = await sealrunstate(state);
4610
+ const index = await this.adapter.get("runstateindex") ?? [];
4611
+ await this.adapter.set(`runstate:${profileid}`, sealed);
4612
+ if (!index.includes(profileid)) await this.adapter.set("runstateindex", [...index, profileid]);
4613
+ }
4614
+ /** Opens the sealed run state of one profile; a missing or tampered seal returns undefined so the recovery never trusts a broken record. */
4615
+ async getrunstate(profileid) {
4616
+ const sealed = await this.adapter.get(`runstate:${profileid}`);
4617
+ if (!sealed) return void 0;
4618
+ try {
4619
+ return await openseal(sealed);
4620
+ } catch {
4621
+ return void 0;
4622
+ }
4623
+ }
4624
+ /** Removes the run state of one profile from the store and the index: the per profile key takes an empty seal that never opens, so the quota pruning drops the pruned records whole. */
4625
+ async removerunstate(profileid) {
4626
+ const index = await this.adapter.get("runstateindex") ?? [];
4627
+ await this.adapter.set("runstateindex", index.filter((entry) => entry !== profileid));
4628
+ await this.adapter.set(`runstate:${profileid}`, { payload: "", algorithm: "sha-256", digest: "", sealedat: 0 });
4629
+ }
4630
+ /** Lists the stored run state records of every profile, oldest update first. */
4631
+ async listrunstates() {
4632
+ const index = await this.adapter.get("runstateindex") ?? [];
4633
+ const states = [];
4634
+ for (const profileid of index) {
4635
+ const state = await this.getrunstate(profileid);
4636
+ if (state) states.push(state);
4637
+ }
4638
+ return states.sort((one, two) => one.updatedat - two.updatedat);
4639
+ }
4640
+ /** Expires the stale run states past the user configured window: the expired records reduce to their keepalive summaries while an absent window keeps every run state whole. */
4641
+ async expirerunstates(window, now) {
4642
+ if (window === void 0) return await this.listrunstates();
4643
+ const index = await this.adapter.get("runstateindex") ?? [];
4644
+ const kept = [];
4645
+ for (const profileid of index) {
4646
+ const state = await this.getrunstate(profileid);
4647
+ if (!state) continue;
4648
+ if (now - state.updatedat > window && state.keepalive.state === "stopped") {
4649
+ const summary = { runid: state.runid, sessionid: state.sessionid, planid: state.planid, profileid: state.profileid, state: "expired", urlhistory: [], environments: {}, turnarounds: {}, keepalive: state.keepalive, updatedat: now };
4650
+ const sealed = await sealrunstate(summary);
4651
+ await this.adapter.set(`runstate:${profileid}`, sealed);
4652
+ } else {
4653
+ kept.push(state);
4654
+ }
4655
+ }
4656
+ return kept;
4657
+ }
4658
+ /** Records one worker spawn or teardown event with its provenance beside the step outcomes. */
4659
+ async addworkerevent(event) {
4660
+ await this.adapter.set("workerevents", [event, ...await this.adapter.get("workerevents") ?? []].slice(0, 500));
4661
+ }
4662
+ /** Returns the recorded worker spawn and teardown events, newest first. */
4663
+ async getworkerevents() {
4664
+ return await this.adapter.get("workerevents") ?? [];
4665
+ }
4666
+ /** Records one spawned offscreen document with its reasons and justification in the registry. */
4667
+ async addoffscreenentry(entry) {
4668
+ await this.adapter.set("offscreenregistry", [entry, ...await this.adapter.get("offscreenregistry") ?? []]);
4669
+ }
4670
+ /** Replaces one registry entry after its offscreen document closes. */
4671
+ async updateoffscreenentry(entry) {
4672
+ await this.adapter.set("offscreenregistry", (await this.adapter.get("offscreenregistry") ?? []).map((candidate) => candidate.runid === entry.runid ? entry : candidate));
4673
+ }
4674
+ /** Returns the offscreen document registry with the reasons and justification of every spawn. */
4675
+ async getoffscreenentries() {
4676
+ return await this.adapter.get("offscreenregistry") ?? [];
4677
+ }
4678
+ /** Records one sandbox render with its provenance, source origin and nonce. */
4679
+ async addsandboxrender(render) {
4680
+ await this.adapter.set("sandboxrenders", [render, ...await this.adapter.get("sandboxrenders") ?? []].slice(0, 500));
4681
+ }
4682
+ /** Returns the recorded sandbox renders with their provenance, newest first. */
4683
+ async getsandboxrenders() {
4684
+ return await this.adapter.get("sandboxrenders") ?? [];
4685
+ }
4686
+ /** Replaces the stored run locks after one acquisition, release or expiry sweep. */
4687
+ async setrunlocks(locks) {
4688
+ return this.adapter.set("runlocks", locks);
4689
+ }
4690
+ /** Returns the held run locks with their sessions, runs and expiries. */
4691
+ async getrunlocks() {
4692
+ return await this.adapter.get("runlocks") ?? [];
4693
+ }
4694
+ /** Tracks the storage quota usage of the run state: the last measured bytes stay beside the user configured ceiling so the pruning reads both. */
4695
+ async trackrunstatequota(used) {
4696
+ const settings = await this.getsettings();
4697
+ await this.adapter.set("runstatequota", { used, ...settings?.runstatebytes !== void 0 ? { ceiling: settings.runstatebytes } : {}, trackedat: Date.now() });
4698
+ }
4699
+ /** Returns the last tracked storage quota usage of the run state with its ceiling when the user configured one. */
4700
+ async getrunstatequota() {
4701
+ return this.adapter.get("runstatequota");
4702
+ }
4703
+ /** Exports every stored run state as one single audit record through the runstate export envelope. */
4704
+ async exportrunstates() {
4705
+ return exportrunstate(await this.listrunstates(), Date.now());
4706
+ }
4707
+ /**
4708
+ * Security part one persistence of the 1.1.61 family.
4709
+ * The trust boundary records live here: the per origin automation allowlist scoped per profile workspace with one exact origin per entry, the per site originprofiles with their kind grants and denials, the active consentwindows with their expiry timestamps that expire closed past their boundary, the mid run revokerun events with the halted step ids that stay visible for later consent prompts, the fresh class consents per origin, the mask rules for field shapes per origin, and the sealed immutable run logs with their final hash.
4710
+ * The run log store exposes no update or delete path: appends land whole, the seal closes a log with its final hash and the read path verifies the chain before returning a single entry so a broken link refuses the read.
4711
+ * The adapter seam keeps every accessor a one line storage delegation so a future append only backend replaces the adapter only; the current storage areas offer no append only hardware, so the honest derivation is the hash chain that makes any rewrite detectable at read time.
4712
+ */
4713
+ /** Replaces the per origin automation allowlist of the profile workspaces; every entry carries one exact origin with no wildcard expansion. */
4714
+ async setautomationallowlist(entries) {
4715
+ return this.adapter.set("automationallowlist", entries);
4716
+ }
4717
+ /** Returns the per origin automation allowlist entries, oldest grant first. */
4718
+ async getautomationallowlist() {
4719
+ return await this.adapter.get("automationallowlist") ?? [];
4720
+ }
4721
+ /** Adds one exact origin to the automation allowlist of a profile workspace; a duplicate origin keeps its first grant. */
4722
+ async addallowlistorigin(entry) {
4723
+ const entries = await this.getautomationallowlist();
4724
+ if (entries.some((candidate) => candidate.origin === entry.origin && candidate.profileid === entry.profileid)) return;
4725
+ await this.setautomationallowlist([...entries, entry]);
4726
+ }
4727
+ /** Removes one origin from the automation allowlist; the denydefault posture refuses the origin again after the removal. */
4728
+ async removeallowlistorigin(origin, profileid) {
4729
+ await this.setautomationallowlist((await this.getautomationallowlist()).filter((entry) => !(entry.origin === origin && entry.profileid === profileid)));
4730
+ }
4731
+ /** Replaces the per site origin profiles with their kind grants and denials; one profile per origin. */
4732
+ async setoriginprofiles(profiles) {
4733
+ return this.adapter.set("originprofiles", profiles);
4734
+ }
4735
+ /** Returns the stored per site origin profiles, oldest update first. */
4736
+ async getoriginprofiles() {
4737
+ return await this.adapter.get("originprofiles") ?? [];
4738
+ }
4739
+ /** Upserts one origin profile: a profile of the same origin replaces its grants and denials while a new origin joins the list. */
4740
+ async saveoriginprofile(profile) {
4741
+ const profiles = await this.getoriginprofiles();
4742
+ await this.setoriginprofiles(profiles.some((candidate) => candidate.origin === profile.origin) ? profiles.map((candidate) => candidate.origin === profile.origin ? profile : candidate) : [...profiles, profile]);
4743
+ }
4744
+ /** Replaces the consent windows; active windows keep their expiry timestamps and closed windows stay for the audit trail. */
4745
+ async setconsentwindows(windows) {
4746
+ return this.adapter.set("consentwindows", windows);
4747
+ }
4748
+ /** Returns the stored consent windows, newest start first. */
4749
+ async getconsentwindows() {
4750
+ return await this.adapter.get("consentwindows") ?? [];
4751
+ }
4752
+ /** Expires every consent window past its duration boundary: the closed windows keep their records while their grants bind no step anymore. */
4753
+ async expireconsentwindows(now) {
4754
+ const windows = await this.getconsentwindows();
4755
+ const expired = windows.map((window) => window.state === "active" && now >= window.expiresat ? { ...window, state: "closed", closedat: now } : window);
4756
+ await this.setconsentwindows(expired);
4757
+ return expired;
4758
+ }
4759
+ /** Records one mid run revocation with its halted step ids; the history stays visible for later consent prompts. */
4760
+ async addrevocation(event) {
4761
+ await this.adapter.set("revocations", [event, ...await this.adapter.get("revocations") ?? []].slice(0, 500));
4762
+ }
4763
+ /** Returns the recorded mid run revocations with their halted step ids, newest first. */
4764
+ async getrevocations() {
4765
+ return await this.adapter.get("revocations") ?? [];
4766
+ }
4767
+ /** Replaces the fresh class consents per origin. */
4768
+ async setclassconsents(consents) {
4769
+ return this.adapter.set("classconsents", consents);
4770
+ }
4771
+ /** Returns the fresh class consents per origin, newest grant first. */
4772
+ async getclassconsents() {
4773
+ return await this.adapter.get("classconsents") ?? [];
4774
+ }
4775
+ /** Records one fresh class consent per origin; the prompt of one class never widens another class. */
4776
+ async addclassconsent(consent) {
4777
+ const consents = (await this.getclassconsents()).filter((candidate) => !(candidate.origin === consent.origin && candidate.sensitiveclass === consent.sensitiveclass));
4778
+ await this.setclassconsents([consent, ...consents]);
4779
+ }
4780
+ /** Replaces the mask rules for sensitive field shapes per origin. */
4781
+ async setmaskrules(rules) {
4782
+ return this.adapter.set("maskrules", rules);
4783
+ }
4784
+ /** Returns the stored mask rules for sensitive field shapes per origin, oldest rule first. */
4785
+ async getmaskrules() {
4786
+ return await this.adapter.get("maskrules") ?? [];
4787
+ }
4788
+ /** Adds one mask rule for field shapes, optionally scoped to one origin. */
4789
+ async addmaskrule(rule) {
4790
+ await this.setmaskrules([...await this.getmaskrules(), rule]);
4791
+ }
4792
+ /** Removes one mask rule by its id. */
4793
+ async removemaskrule(id) {
4794
+ await this.setmaskrules((await this.getmaskrules()).filter((rule) => rule.id !== id));
4795
+ }
4796
+ /** Stores the whole run log of one run: the append lands in one storage transaction so the entries and their chain links persist together. */
4797
+ async setimmutablelog(log) {
4798
+ return this.adapter.set(`immutablelog:${log.runid}`, log);
4799
+ }
4800
+ /** Returns the stored run log of one run; an absent log returns undefined. */
4801
+ async getimmutablelog(runid) {
4802
+ return this.adapter.get(`immutablelog:${runid}`);
4803
+ }
4804
+ /** Lists the stored run logs, oldest update first, with the sealed logs carrying their final hash. */
4805
+ async listimmutablelogs() {
4806
+ const index = await this.adapter.get("immutablelogindex") ?? [];
4807
+ const logs = [];
4808
+ for (const runid of index) {
4809
+ const log = await this.getimmutablelog(runid);
4810
+ if (log) logs.push(log);
4811
+ }
4812
+ return logs.sort((one, two) => one.updatedat - two.updatedat);
4813
+ }
4814
+ /** Stores the run log index entry of one run so the log listing reads every stored log. */
4815
+ async trackimmutablelog(runid) {
4816
+ const index = await this.adapter.get("immutablelogindex") ?? [];
4817
+ if (!index.includes(runid)) await this.adapter.set("immutablelogindex", [...index, runid]);
4818
+ }
4819
+ /** Exports the verified log chain of one run for the audit file: the read path verifies the whole hash chain first and a broken link refuses the export with no entries served. */
4820
+ async exportverifiedrunlog(runid) {
4821
+ const log = await this.getimmutablelog(runid);
4822
+ if (!log) throw new Error(`No run log exists for the run ${runid}.`);
4823
+ return exportlogchain(log);
4824
+ }
4825
+ /** Expires the sealed run logs past the user configured retention: the entries reduce to their chain summaries while the seal hash always survives. */
4826
+ async expireimmutablelogs(retention, now) {
4827
+ const logs = await this.listimmutablelogs();
4828
+ if (retention === void 0) return logs;
4829
+ const kept = [];
4830
+ for (const log of logs) {
4831
+ if (log.seal !== void 0 && now - log.seal.sealedat > retention) {
4832
+ const summary = { runid: log.runid, sessionid: log.sessionid, entries: [], seal: { ...log.seal, entries: log.seal.entries }, updatedat: now };
4833
+ await this.setimmutablelog(summary);
4834
+ } else {
4835
+ kept.push(log);
4836
+ }
4837
+ }
4838
+ return kept;
4839
+ }
4361
4840
  };
4362
4841
  function mediakindof(record2) {
4363
4842
  if ("pages" in record2) return "pdf";
@@ -5147,6 +5626,133 @@ function replayagentrun(input) {
5147
5626
  return interleavetimeline(input.events.filter((event) => event.agentid === input.agentid).map((event) => ({ id: event.id, kind: event.kind, summary: event.summary, at: event.at, ...event.agentid !== void 0 ? { agentid: event.agentid } : {} })));
5148
5627
  }
5149
5628
 
5629
+ // environments.ts
5630
+ var offloadfamilies = [
5631
+ { task: "htmlsnapshot", kinds: ["readhtml", "parsehtml", "readertree", "readoutline", "classifypage"] },
5632
+ { task: "jsonpayload", kinds: ["readjson", "parsejson"] },
5633
+ { task: "tablerows", kinds: ["readtable", "scrapetable", "detecttables", "deduperows", "transformvalues"] },
5634
+ { task: "a11ytree", kinds: ["a11ytree"] },
5635
+ { task: "complexselector", kinds: ["resolvexpath", "deriveselector", "detectvirtual"] },
5636
+ { task: "stitchshots", kinds: ["contactsheet", "timelapse", "makethumbs"] }
5637
+ ];
5638
+ function offfamilyof(kind) {
5639
+ return offloadfamilies.find((family) => family.kinds.includes(kind))?.task;
5640
+ }
5641
+ function environmentsof(step) {
5642
+ if (markuprenderstep(step)) return ["sandboxframe"];
5643
+ if (step.kind === "evaluate") return ["isolatedworld"];
5644
+ if (offfamilyof(step.kind) !== void 0) return ["pagecontext", "offscreenworker"];
5645
+ return ["pagecontext"];
5646
+ }
5647
+ function defaultenvironment(step) {
5648
+ if (markuprenderstep(step)) return "sandboxframe";
5649
+ if (step.kind === "evaluate") return "isolatedworld";
5650
+ return "pagecontext";
5651
+ }
5652
+ function offamilyeligible(kind) {
5653
+ return offloadfamilies.some((family) => family.kinds.includes(kind));
5654
+ }
5655
+ function offloadkinds() {
5656
+ return offloadfamilies.map((family) => ({ task: family.task, kinds: [...family.kinds] }));
5657
+ }
5658
+ function markuprenderstep(step) {
5659
+ if (!step.options) return false;
5660
+ try {
5661
+ const parsed = JSON.parse(step.options);
5662
+ return Boolean(parsed && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.markup === "string" && parsed.markup.trim() !== "");
5663
+ } catch {
5664
+ return false;
5665
+ }
5666
+ }
5667
+ function environmentrequirementsof(kinds) {
5668
+ return kinds.map((kind) => {
5669
+ const bare = { kind };
5670
+ const environments = environmentsof(bare);
5671
+ return { kind, environments, defaultenvironment: defaultenvironment(bare) };
5672
+ });
5673
+ }
5674
+ function executorregistry() {
5675
+ return [
5676
+ { environment: "pagecontext", adapter: "pagebridge", description: "The page bridge executes dom actions inside the live page because page events only fire there." },
5677
+ { environment: "isolatedworld", adapter: "scriptingapi", description: "The scripting api injects step logic inside the isolated world where page globals stay unreachable from step code." },
5678
+ { environment: "offscreenworker", adapter: "offscreendocument", description: "The offscreen document hosts the worker pool that parses heavy payloads away from the page; the capability gate keeps it behind the optional offscreen grant with an inline fallback." },
5679
+ { environment: "sandboxframe", adapter: "sandboxpage", description: "The sandboxed page renders untrusted markup with scripts and handlers stripped before render and posts its result back through a per render nonce." }
5680
+ ];
5681
+ }
5682
+ function routeenvironment(step, input) {
5683
+ const allowed = environmentsof(step);
5684
+ const named = step.environment;
5685
+ if (named !== void 0) {
5686
+ if (!allowed.includes(named)) return { environment: defaultenvironment(step), fallback: false, reason: `The ${named} environment sits outside the ${allowed.join(", ")} the ${step.kind} kind permits, so the executor routes to the ${defaultenvironment(step)} default.` };
5687
+ return { environment: named, fallback: false, reason: `The reviewed step names its ${named} environment and the ${step.kind} kind permits it.` };
5688
+ }
5689
+ if (markuprenderstep(step)) return { environment: "sandboxframe", fallback: false, reason: `The ${step.kind} step carries untrusted markup, so it renders inside the sandboxframe only.` };
5690
+ if (step.kind === "evaluate") return { environment: "isolatedworld", fallback: false, reason: "The evaluate kind runs inside the isolated world where page globals stay unreachable from step code." };
5691
+ if (offamilyeligible(step.kind)) {
5692
+ if (!input.offload) return { environment: "pagecontext", fallback: false, reason: `The ${step.kind} step stays inside the page because the user keeps the parse offload off.` };
5693
+ if (!input.granted) return { environment: "pagecontext", fallback: true, reason: `The ${step.kind} step falls back to inline parsing inside the page because the offscreen capability grant stays absent.` };
5694
+ return { environment: "offscreenworker", fallback: false, reason: `The ${step.kind} step offloads into the offscreen worker pool under the granted capability.` };
5695
+ }
5696
+ return { environment: "pagecontext", fallback: false, reason: `The ${step.kind} step keeps the pagecontext because page events only fire inside the live page.` };
5697
+ }
5698
+ function workerrequestof(input) {
5699
+ const task = offfamilyof(input.kind);
5700
+ if (task === void 0) throw new Error(`The ${input.kind} kind stays outside the offscreen worker pool families.`);
5701
+ if (input.payload.trim() === "") throw new Error("The worker request needs its payload reference.");
5702
+ return { id: input.id, runid: input.runid, stepid: input.stepid, task, payload: input.payload, transferables: transferablekeys(input.options ?? {}), sentat: input.sentat };
5703
+ }
5704
+ function transferablekeys(options) {
5705
+ return Object.keys(options).filter((key) => options[key] instanceof ArrayBuffer);
5706
+ }
5707
+ function workerresponseof(input) {
5708
+ if (input.summary.trim() === "") throw new Error("The worker answer needs its summary in plain language.");
5709
+ return { id: input.id, requestid: input.requestid, ok: input.ok, ...input.result !== void 0 ? { result: input.result } : {}, ...input.partial !== void 0 ? { partial: input.partial } : {}, summary: input.summary, receivedat: input.receivedat };
5710
+ }
5711
+ function acceptworkerresponse(response) {
5712
+ if (!response.ok) return { done: true, partial: false, reason: `The worker refused the request ${response.requestid}: ${response.summary}` };
5713
+ if (response.partial !== void 0) return { done: false, partial: true, reason: `The partial ${response.partial} of the request ${response.requestid} streams back to the executor.` };
5714
+ return { done: true, partial: false, ...response.result !== void 0 ? { result: response.result } : {}, reason: `The request ${response.requestid} completed inside the offscreen worker pool.` };
5715
+ }
5716
+ function poolplan(input) {
5717
+ if (input.size !== void 0) {
5718
+ if (!Number.isFinite(input.size) || input.size < 1 || !Number.isInteger(input.size)) return { workers: input.current, added: 0, retired: 0, reason: "The configured pool size stays a positive whole number the user chose; the pool keeps its current workers." };
5719
+ const target = input.size;
5720
+ if (target > input.current) return { workers: target, added: target - input.current, retired: 0, reason: `The user configured pool size ${target} adds ${target - input.current} worker${target - input.current === 1 ? "" : "s"} to the pool.` };
5721
+ if (target < input.current) return { workers: target, added: 0, retired: input.current - target, reason: `The user configured pool size ${target} retires ${input.current - target} worker${input.current - target === 1 ? "" : "s"} from the pool.` };
5722
+ return { workers: target, added: 0, retired: 0, reason: `The pool holds the ${target} workers the user configured.` };
5723
+ }
5724
+ if (input.pending > input.current) return { workers: input.pending, added: input.pending - input.current, retired: 0, reason: `The ${input.pending} pending parses grow the pool by ${input.pending - input.current} worker${input.pending - input.current === 1 ? "" : "s"}; no engine cap exists.` };
5725
+ if (input.current > input.pending) return { workers: input.pending, added: 0, retired: input.current - input.pending, reason: `The ${input.current - input.pending} idle worker${input.current - input.pending === 1 ? "" : "s"} retire down to the ${input.pending} pending parse${input.pending === 1 ? "" : "s"}.` };
5726
+ return { workers: input.current, added: 0, retired: 0, reason: `The ${input.current} workers match the ${input.pending} pending parses; the pool stays unchanged.` };
5727
+ }
5728
+ function openoffscreen(input) {
5729
+ if (input.document.trim() === "") throw new Error("The offscreen document needs its user configured path.");
5730
+ if (input.reasons.length === 0) throw new Error("The offscreen document needs the reasons the user reviewed.");
5731
+ if (input.justification.trim() === "") throw new Error("The offscreen document needs its justification in plain language.");
5732
+ const open = input.registry.find((entry2) => entry2.runid === input.runid && entry2.closedat === void 0);
5733
+ if (open) return { registry: input.registry, entry: open, reused: true };
5734
+ const entry = { document: input.document, runid: input.runid, reasons: [...input.reasons], justification: input.justification, createdat: input.now };
5735
+ return { registry: [entry, ...input.registry], entry, reused: false };
5736
+ }
5737
+ function closeoffscreen(registry, runid, now) {
5738
+ const open = registry.find((entry) => entry.runid === runid && entry.closedat === void 0);
5739
+ if (!open) return { registry, closed: false };
5740
+ return { registry: registry.map((entry) => entry === open ? { ...entry, closedat: now } : entry), closed: true };
5741
+ }
5742
+ function isolatedinjection(step) {
5743
+ if (step.kind !== "evaluate") throw new Error("The isolated world injection serves the evaluate kind only.");
5744
+ if (!step.value || step.value.trim() === "") throw new Error("The evaluate step needs its reviewed expression.");
5745
+ let args = [];
5746
+ if (step.options) {
5747
+ try {
5748
+ const parsed = JSON.parse(step.options);
5749
+ if (Array.isArray(parsed)) args = parsed.filter((item) => typeof item === "string");
5750
+ } catch {
5751
+ }
5752
+ }
5753
+ return { world: "ISOLATED", code: step.value, args };
5754
+ }
5755
+
5150
5756
  // httpclient.ts
5151
5757
  var httpkinds = ["fetchurl", "parsejson", "parsehtml", "callrest", "callgraphql"];
5152
5758
  var redirectstatuses = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
@@ -5433,6 +6039,144 @@ async function callgraphql(input) {
5433
6039
  }
5434
6040
  }
5435
6041
 
6042
+ // originpolicy.ts
6043
+ var denydefaultposture = "denydefault";
6044
+ function exactorigin(origin, entry) {
6045
+ return origin.trim() !== "" && origin === entry;
6046
+ }
6047
+ function wildcardentry(entry) {
6048
+ return entry.includes("*") || entry.includes("://*.") || entry.trim() === "" || entry.trim() === "https://" || entry.trim() === "http://";
6049
+ }
6050
+ function allowlistcheck(input) {
6051
+ if (input.origin.trim() === "") return { allowed: false, reason: "The step needs the exact origin it targets." };
6052
+ for (const entry of input.allowlist) {
6053
+ if (wildcardentry(entry.origin)) return { allowed: false, reason: `The allowlist entry ${entry.origin} carries a wildcard; every grant binds to one exact origin with no wildcard expansion.` };
6054
+ }
6055
+ const scoped = input.profileid === void 0 ? input.allowlist : input.allowlist.filter((entry) => entry.profileid === input.profileid);
6056
+ const granted = scoped.some((entry) => exactorigin(input.origin, entry.origin));
6057
+ if (granted) return { allowed: true, reason: `The origin ${input.origin} sits inside the automation allowlist the user granted.` };
6058
+ if (input.sessionorigin !== void 0 && exactorigin(input.origin, input.sessionorigin)) return { allowed: true, reason: `The active tab grant covers ${input.origin} as exactly one explicit single origin grant.` };
6059
+ return { allowed: false, reason: `The denydefault posture refuses ${input.origin} because the origin sits absent from the automation allowlist; grant the origin first.` };
6060
+ }
6061
+ function originprofileof(input) {
6062
+ if (input.origin.trim() === "") throw new Error("The origin profile needs its exact origin.");
6063
+ return { profileid: input.profileid ?? randomid(), origin: input.origin, grants: [...input.grants ?? []], denials: [...input.denials ?? []], createdat: input.now, updatedat: input.now };
6064
+ }
6065
+ function profilekind(input) {
6066
+ if (input.profile.grants.includes(input.kind) && input.decision === "grant") return input.profile;
6067
+ if (input.profile.denials.includes(input.kind) && input.decision === "deny") return input.profile;
6068
+ const grants = input.decision === "grant" ? [.../* @__PURE__ */ new Set([...input.profile.grants, input.kind])] : input.profile.grants.filter((kind) => kind !== input.kind);
6069
+ const denials = input.decision === "deny" ? [.../* @__PURE__ */ new Set([...input.profile.denials, input.kind])] : input.profile.denials.filter((kind) => kind !== input.kind);
6070
+ return { ...input.profile, grants, denials, updatedat: input.now };
6071
+ }
6072
+ function profilegrade(input) {
6073
+ if (!input.sensitive) return { allowed: true, consult: false, reason: `The ${input.kind} kind grades non-sensitive and the origin profile needs no consult.` };
6074
+ if (input.profile === void 0) return { allowed: true, consult: true, reason: `No origin profile exists for the ${input.kind} kind, so the fresh class consent gate alone routes the sensitive step.` };
6075
+ if (input.profile.denials.includes(input.kind)) return { allowed: false, consult: true, reason: `The origin profile of ${input.profile.origin} denies the ${input.kind} kind; a denied kind never runs on that origin.` };
6076
+ if (input.profile.grants.includes(input.kind)) return { allowed: true, consult: true, reason: `The origin profile of ${input.profile.origin} grants the ${input.kind} kind the user reviewed.` };
6077
+ return { allowed: true, consult: true, reason: `The origin profile of ${input.profile.origin} carries no ${input.kind} decision, so the fresh class consent gate alone routes the sensitive step.` };
6078
+ }
6079
+ function stepoptions(step) {
6080
+ if (!step.options) return {};
6081
+ try {
6082
+ const parsed = JSON.parse(step.options);
6083
+ return Boolean(parsed) && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
6084
+ } catch {
6085
+ return {};
6086
+ }
6087
+ }
6088
+ var paymentkinds = /* @__PURE__ */ new Set(["fillcard", "fillcode"]);
6089
+ var credentialkinds = /* @__PURE__ */ new Set(["consentpassword", "saveapikey", "handleauth", "authflow"]);
6090
+ var deletekinds = /* @__PURE__ */ new Set(["discardtab", "closepattern", "clearcookies", "removeattribute", "cleanupartifacts"]);
6091
+ var publishkinds = /* @__PURE__ */ new Set(["postform", "postfiles", "sendmessage", "submitform", "submitsearch", "writeclipboard"]);
6092
+ var defaultsensitivekinds = /* @__PURE__ */ new Set(["attachfile", "uploadfile", "uploadfiles", "downloadfile", "downloadimages", "batchdownload", "pausedownload", "resumedownload", "quarantinedownload", "evaluate"]);
6093
+ function sensitiveclassesof(step) {
6094
+ const options = stepoptions(step);
6095
+ const fields = Array.isArray(options.fields) ? options.fields.filter((item) => Boolean(item) && typeof item === "object") : [];
6096
+ const names = [...fields.map((field) => typeof field.name === "string" ? field.name : ""), typeof options.field === "string" ? options.field : "", typeof options.target === "string" ? options.target : ""].map((name) => name.toLowerCase());
6097
+ const carries = (shape) => names.some((name) => name.includes(shape));
6098
+ const classes = /* @__PURE__ */ new Set();
6099
+ if (paymentkinds.has(step.kind) || carries("card") || carries("cvc") || carries("cvv")) classes.add("payment");
6100
+ const credentialshape = carries("password") || carries("token") || carries("secret") || carries("apikey") || carries("passphrase");
6101
+ const submits = step.kind === "submitform" || step.kind === "postform" || step.kind === "submitsearch" || step.kind === "fillform" || step.kind === "filllabel" || step.kind === "fillplaceholder";
6102
+ if (credentialkinds.has(step.kind) || submits && credentialshape) classes.add("credential");
6103
+ if (deletekinds.has(step.kind)) classes.add("delete");
6104
+ if (publishkinds.has(step.kind) || step.kind === "callrest" || step.kind === "callgraphql") {
6105
+ const verb = typeof options.method === "string" ? options.method.trim().toUpperCase() : typeof options.verb === "string" ? options.verb.trim().toUpperCase() : "";
6106
+ if (step.kind === "callrest" || step.kind === "callgraphql") {
6107
+ if (verb !== "" && !["GET", "HEAD", "OPTIONS"].includes(verb)) classes.add("publish");
6108
+ } else classes.add("publish");
6109
+ }
6110
+ const bydefault = defaultsensitivekinds.has(step.kind);
6111
+ const list = [...classes];
6112
+ if (list.length === 0 && !bydefault) return { classes: [], bydefault: false, sensitive: false, reason: `The ${step.kind} kind carries no sensitive class and no default sensitive grade.` };
6113
+ return { classes: list, bydefault, sensitive: true, reason: `The ${step.kind} kind grades sensitive${list.length > 0 ? ` through the ${list.join(", ")} class${list.length === 1 ? "" : "es"}` : ""}${bydefault ? " by default" : ""}.` };
6114
+ }
6115
+ function classconsentcovers(consents, origin, sensitiveclass, now) {
6116
+ return consents.some((consent) => consent.origin === origin && consent.sensitiveclass === sensitiveclass && consent.grantedat <= now && (consent.expiresat === void 0 || now < consent.expiresat));
6117
+ }
6118
+ function missingclassconsents(input) {
6119
+ const missing = input.classes.filter((kind) => !classconsentcovers(input.consents, input.origin, kind, input.now));
6120
+ if (missing.length > 0) return { needed: true, missing, reason: `The sensitive classes ${missing.join(", ")} need one fresh consent prompt each on ${input.origin}.` };
6121
+ if (input.bydefault && input.classes.length === 0) return { needed: true, missing: [], reason: `The ${input.origin} step grades sensitive by default and needs its fresh consent window prompt.` };
6122
+ return { needed: false, missing: [], reason: `The fresh class consents of ${input.origin} cover every class the step names.` };
6123
+ }
6124
+ function openconsentwindow(input) {
6125
+ if (input.sessionid.trim() === "" || input.origin.trim() === "") throw new Error("The consent window needs its session and its exact origin.");
6126
+ if (!Number.isFinite(input.duration) || input.duration <= 0) throw new Error("The consent window needs its duration as a positive user value; no window defaults to unlimited.");
6127
+ return { id: input.id ?? randomid(), sessionid: input.sessionid, origin: input.origin, startedat: input.now, duration: input.duration, expiresat: input.now + input.duration, boundary: input.boundary?.trim() !== "" && input.boundary !== void 0 ? input.boundary : `${input.duration} milliseconds the user chose`, kinds: [...new Set(input.kinds)], state: "active" };
6128
+ }
6129
+ function consentwindowstate(window, now) {
6130
+ if (window.state === "closed" || now >= window.expiresat) return { state: "expired", remaining: 0, reason: `The consent window of ${window.origin} closed at its ${window.boundary} boundary; the run suspends until a new explicit prompt renews it.` };
6131
+ return { state: "active", remaining: window.expiresat - now, reason: `The consent window of ${window.origin} stays active with ${window.expiresat - now} milliseconds left of its ${window.boundary} boundary.` };
6132
+ }
6133
+ function windowgatesstep(input) {
6134
+ if (input.window === void 0) return { allowed: false, suspended: false, reason: `No active consent window covers ${input.origin}; the consent prompt opens one before any step dispatches.` };
6135
+ if (input.window.sessionid !== input.sessionid) return { allowed: false, suspended: false, reason: `The consent window scopes to the session ${input.window.sessionid} only and never widens to another session.` };
6136
+ if (input.window.origin !== input.origin) return { allowed: false, suspended: false, reason: `The consent window scopes to the origin ${input.window.origin} only and never widens to another origin.` };
6137
+ const state = consentwindowstate(input.window, input.now);
6138
+ if (state.state === "expired") return { allowed: false, suspended: true, reason: state.reason };
6139
+ return { allowed: true, suspended: false, reason: state.reason };
6140
+ }
6141
+ function expireconsentwindows(windows, now) {
6142
+ return windows.map((window) => window.state === "active" && now >= window.expiresat ? { ...window, state: "closed", closedat: now } : window);
6143
+ }
6144
+ function renewconsentwindow(input) {
6145
+ const closed = input.window.state === "active" ? { ...input.window, state: "closed", closedat: input.now } : input.window;
6146
+ const renewed = openconsentwindow({ sessionid: input.window.sessionid, origin: input.window.origin, duration: input.duration, kinds: input.kinds.length > 0 ? input.kinds : input.window.kinds, now: input.now });
6147
+ return { renewed, closed };
6148
+ }
6149
+ function revokerun(input) {
6150
+ if (input.sessionid.trim() === "" || input.runid.trim() === "") throw new Error("The revocation needs its session and run ids.");
6151
+ if (input.actor.trim() === "") throw new Error("The revocation names the acting user.");
6152
+ const halted = [...input.pendingstepid !== void 0 ? [input.pendingstepid] : [], ...input.queuedstepids ?? []];
6153
+ if (halted.length === 0) throw new Error("The revocation halts at least the pending step of the run.");
6154
+ return { id: input.id ?? randomid(), sessionid: input.sessionid, runid: input.runid, haltedstepids: halted, actor: input.actor, reason: input.reason?.trim() !== "" && input.reason !== void 0 ? input.reason : "The user revoked the consent mid run.", at: input.now };
6155
+ }
6156
+ function haltedstepsof(revocation) {
6157
+ return { ...revocation.haltedstepids.length > 0 ? { pending: revocation.haltedstepids[0] } : {}, queued: revocation.haltedstepids.slice(1) };
6158
+ }
6159
+ function scopegrantof(input) {
6160
+ if (input.origin.trim() === "") throw new Error("The consent scope needs its exact origin.");
6161
+ if (input.kinds.length === 0) throw new Error("The consent scope names the kinds it covers.");
6162
+ if (input.boundary.trim() === "") throw new Error("The consent scope names its boundary; no grant defaults to unlimited.");
6163
+ return { origin: input.origin, kinds: [...new Set(input.kinds)], boundary: input.boundary, grantedat: input.now };
6164
+ }
6165
+ function deniedevidenceof(input) {
6166
+ return { origin: input.origin, kind: input.kind, reason: input.reason, at: input.now };
6167
+ }
6168
+ function consentprompttext(input) {
6169
+ const label = input.classes.length > 0 ? `the ${input.classes.join(" and ")} class${input.classes.length === 1 ? "" : "es"}` : "a sensitive by default grade";
6170
+ return `Allow the ${input.kind} step on ${input.origin} graded as ${label} for ${input.duration} milliseconds? The consent window closes at that boundary; no grant ever defaults to unlimited.`;
6171
+ }
6172
+ function denydefaultnotice(origin) {
6173
+ return `The denydefault posture refuses ${origin} until the user adds the origin to the automation allowlist; no step dispatches without the grant.`;
6174
+ }
6175
+ function profilesummary(profile) {
6176
+ if (profile === void 0) return "No origin profile exists for this origin yet; sensitive steps route through their fresh consent prompts.";
6177
+ return `The origin profile of ${profile.origin} grants ${profile.grants.length} kind${profile.grants.length === 1 ? "" : "s"} and denies ${profile.denials.length} kind${profile.denials.length === 1 ? "" : "s"} the user reviewed.`;
6178
+ }
6179
+
5436
6180
  // socketbus.ts
5437
6181
  var socketkinds = ["opensocket", "sendmessage", "waitmessage", "subscribesse", "longpoll"];
5438
6182
  function channelorigin(url) {
@@ -9769,6 +10513,92 @@ function blackboardconsentgrade(entry) {
9769
10513
  if (entry.consentclass === "sensitive") return { allowed: true, reason: `The blackboard entry ${entry.key} inherits the sensitive class of its source extraction; every agent reads the class beside the value.` };
9770
10514
  return { allowed: true, reason: `The blackboard entry ${entry.key} inherits the ${entry.consentclass} class of its source extraction; every agent reads the class beside the value.` };
9771
10515
  }
10516
+ function stepenvironmentvalid(step) {
10517
+ if (step.environment !== void 0 && step.environment !== "pagecontext" && step.environment !== "isolatedworld" && step.environment !== "offscreenworker" && step.environment !== "sandboxframe") return { allowed: false, reason: "The step environment stays one of pagecontext, isolatedworld, offscreenworker and sandboxframe." };
10518
+ const allowed = environmentsof(step);
10519
+ if (step.environment !== void 0 && !allowed.includes(step.environment)) return { allowed: false, reason: `The ${step.environment} environment sits outside the ${allowed.join(", ")} the ${step.kind} kind permits; the review sees the environment of every step.` };
10520
+ return { allowed: true, reason: step.environment === void 0 ? `The ${step.kind} step carries no environment field and routes to its ${defaultenvironment(step)} default.` : `The ${step.environment} environment of the ${step.kind} step sits inside the ${allowed.join(", ")} the kind permits.` };
10521
+ }
10522
+ function environmentgrantgate(step, grants) {
10523
+ if (grants === void 0 || grants.length === 0) return { allowed: true, reason: `The session carries no environment grant list, so the ${defaultenvironment(step)} default of the ${step.kind} step stays the documented posture behind the same review.` };
10524
+ const environment = step.environment ?? defaultenvironment(step);
10525
+ if (!grants.includes(environment)) return { allowed: false, reason: `The ${environment} environment sits outside the ${grants.join(", ")} the session granted; no step ever widens the environment grants.` };
10526
+ return { allowed: true, reason: `The ${environment} environment of the ${step.kind} step sits inside the ${grants.join(", ")} the session granted.` };
10527
+ }
10528
+ function offscreencapabilitygate(input) {
10529
+ if (input.environment !== "offscreenworker") return { allowed: true, reason: `The ${input.environment} environment needs no offscreen capability grant.` };
10530
+ if (!input.granted) return { allowed: false, reason: "The offscreen worker pool runs only under the user granted offscreen capability; the step falls back to inline parsing inside the page." };
10531
+ return { allowed: true, reason: "The offscreen worker pool runs under the user granted offscreen capability." };
10532
+ }
10533
+ function keepalivegate(input) {
10534
+ if (!input.session) return { allowed: false, reason: "The keepalive port opens only inside an active session." };
10535
+ if (input.session.stoppedat !== void 0) return { allowed: false, reason: "The keepalive port stays closed for a stopped session." };
10536
+ if (input.session.pausedat !== void 0) return { allowed: false, reason: "The keepalive port stays closed while the session pauses; a resumed run reattaches it." };
10537
+ if (input.now > input.session.expiresat) return { allowed: false, reason: "The keepalive port stays closed for an expired session." };
10538
+ if (!input.plan || input.plan.state !== "approved") return { allowed: false, reason: "The keepalive port opens only behind an active reviewed plan; unreviewed work never holds the service worker alive." };
10539
+ return { allowed: true, reason: `The approved plan ${input.plan.id} of the active session holds the keepalive port open for its whole run.` };
10540
+ }
10541
+ function keepaliveintervalvalid(interval) {
10542
+ if (!Number.isFinite(interval) || interval <= 0) return { allowed: false, reason: "The keepalive heartbeat interval stays a positive user value in milliseconds." };
10543
+ return { allowed: true, reason: `The keepalive heartbeat interval ${interval} milliseconds stays the user configured value; the roadmap documents thirty seconds while the choice stays the user's.` };
10544
+ }
10545
+ function workerpoolsizevalid(size) {
10546
+ if (size === void 0) return { allowed: true, reason: "No worker pool size is configured, so the pool follows the pending parse queue alone with no engine cap." };
10547
+ if (!Number.isInteger(size) || size < 1) return { allowed: false, reason: "The worker pool size stays a positive whole number the user configured; no engine cap exists." };
10548
+ return { allowed: true, reason: `The worker pool size ${size} stays the user configured value; no engine cap exists.` };
10549
+ }
10550
+ function sandboxorigingate(input) {
10551
+ if (input.origin.trim() === "") return { allowed: false, reason: "The sandbox render needs the source origin of its untrusted markup." };
10552
+ if (input.allowed.length > 0 && !input.allowed.includes(input.origin)) return { allowed: false, reason: `The origin ${input.origin} sits outside the origins the user allows to render untrusted markup: ${input.allowed.join(", ")}.` };
10553
+ return { allowed: true, reason: input.allowed.length === 0 ? `The origin ${input.origin} renders untrusted markup under the documented open origin list the user chose not to narrow.` : `The origin ${input.origin} sits inside the origins the user allows to render untrusted markup.` };
10554
+ }
10555
+ function environmentrequirements() {
10556
+ return environmentrequirementsof([...allowedactions]);
10557
+ }
10558
+ function automationallowlistgate(input) {
10559
+ const verdict = allowlistcheck({ origin: input.origin, allowlist: input.allowlist, ...input.session !== void 0 ? { sessionorigin: input.session.origin } : {} });
10560
+ if (!verdict.allowed) return { allowed: false, reason: verdict.reason };
10561
+ return { allowed: true, reason: verdict.reason };
10562
+ }
10563
+ function originprofilegate(input) {
10564
+ const verdict = profilegrade(input);
10565
+ if (!verdict.allowed) return { allowed: false, reason: verdict.reason };
10566
+ return { allowed: true, reason: verdict.reason };
10567
+ }
10568
+ function consentwindowgate(input) {
10569
+ if (!input.sensitive) return { allowed: true, reason: "The non-sensitive step rides the session origin grants and needs no consent window of its own." };
10570
+ const verdict = windowgatesstep({ window: input.window, sessionid: input.sessionid, origin: input.origin, now: input.now });
10571
+ if (!verdict.allowed) return { allowed: false, reason: verdict.reason };
10572
+ return { allowed: true, reason: verdict.reason };
10573
+ }
10574
+ function revokerungate(input) {
10575
+ if (input.revocation === void 0) return { allowed: true, reason: "No revocation halted the run; the steps keep their reviewed order." };
10576
+ if (input.revocation.sessionid !== input.sessionid) return { allowed: true, reason: "The revocation belongs to another session and halts nothing here." };
10577
+ if (input.revocation.runid !== input.runid) return { allowed: true, reason: "The revocation belongs to another run and halts nothing here." };
10578
+ return { allowed: false, reason: `The revocation of ${input.revocation.actor} halted the pending step and ${Math.max(0, input.revocation.haltedstepids.length - 1)} queued step${Math.max(0, input.revocation.haltedstepids.length - 1) === 1 ? "" : "s"} without executing them: ${input.revocation.haltedstepids.join(", ")}.` };
10579
+ }
10580
+ function sensitiveclassgate(input) {
10581
+ if (!input.sensitive) return { allowed: true, reason: "The step carries no sensitive class and needs no fresh consent prompt." };
10582
+ const verdict = missingclassconsents({ origin: input.origin, classes: input.classes, bydefault: input.bydefault, consents: input.consents, now: input.now });
10583
+ if (verdict.needed) return { allowed: false, reason: verdict.reason };
10584
+ return { allowed: true, reason: verdict.reason };
10585
+ }
10586
+ function consentdurationvalid(duration) {
10587
+ if (!Number.isFinite(duration) || duration <= 0) return { allowed: false, reason: "The consent window duration stays a positive user value in milliseconds; no grant ever defaults to unlimited." };
10588
+ return { allowed: true, reason: `The consent window duration ${duration} milliseconds stays the user configured boundary the prompt names.` };
10589
+ }
10590
+ function logreadgate(input) {
10591
+ 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." };
10592
+ return { allowed: true, reason: "The log chain verifies from the genesis hash to the last entry; the audit accessor serves the entries." };
10593
+ }
10594
+ function sensitivepipelingate(input) {
10595
+ const classification = sensitiveclassesof(input.step);
10596
+ const profileverdict = originprofilegate({ profile: input.profile, kind: input.step.kind, sensitive: classification.sensitive });
10597
+ if (!profileverdict.allowed) return { allowed: false, reason: profileverdict.reason ?? "" };
10598
+ const consentverdict = sensitiveclassgate({ origin: input.origin, classes: classification.classes, bydefault: classification.bydefault, sensitive: classification.sensitive, consents: input.consents, now: input.now });
10599
+ if (!consentverdict.allowed) return { allowed: false, reason: `${classification.reason} ${consentverdict.reason}` };
10600
+ return { allowed: true, reason: `${classification.reason} ${consentverdict.reason}` };
10601
+ }
9772
10602
 
9773
10603
  // llm.ts
9774
10604
  var defaultrefusalmarkers = ["i cannot", "i can't", "i'm unable", "refusal:", "cannot comply"];
@@ -10129,7 +10959,7 @@ function budgetcheck(input) {
10129
10959
  }
10130
10960
 
10131
10961
  // version.ts
10132
- var packageversion = "1.1.59";
10962
+ var packageversion = "1.1.61";
10133
10963
 
10134
10964
  // types.ts
10135
10965
  var protocolversion = packageversion;
@@ -10725,6 +11555,79 @@ function streamsummaries(raw) {
10725
11555
  });
10726
11556
  }
10727
11557
 
11558
+ // maskinputs.ts
11559
+ var defaultmaskshapes = ["password", "token", "card", "secret"];
11560
+ var maskmarker = "[redacted]";
11561
+ function fieldshapekind(name) {
11562
+ const lowered = name.toLowerCase();
11563
+ if (lowered.includes("password") || lowered.includes("passwd") || lowered.includes("pwd") || lowered.includes("passphrase")) return "password";
11564
+ if (lowered.includes("token") || lowered.includes("apikey") || lowered.includes("api_key") || lowered.includes("auth") || lowered.includes("bearer")) return "token";
11565
+ if (lowered.includes("card") || lowered.includes("cvc") || lowered.includes("cvv") || lowered.includes("expiry") || lowered.includes("pan")) return "card";
11566
+ if (lowered.includes("secret")) return "secret";
11567
+ return void 0;
11568
+ }
11569
+ function shapesof(input) {
11570
+ const shapes = new Set(defaultmaskshapes);
11571
+ for (const shape of input.settings?.maskshapes ?? []) if (shape.trim() !== "") shapes.add(shape.trim().toLowerCase());
11572
+ for (const rule of input.rules) {
11573
+ const scoped = rule.origin === void 0 || rule.origin === "" || input.origin !== void 0 && rule.origin === input.origin;
11574
+ if (scoped) {
11575
+ for (const shape of rule.shapes) if (shape.trim() !== "") shapes.add(shape.trim().toLowerCase());
11576
+ }
11577
+ }
11578
+ return [...shapes];
11579
+ }
11580
+ function maskingfield(name, shapes) {
11581
+ if (fieldshapekind(name) !== void 0) return true;
11582
+ const lowered = name.toLowerCase();
11583
+ return shapes.some((shape) => shape !== "" && lowered.includes(shape));
11584
+ }
11585
+ function maskvalue(value) {
11586
+ return value === "" ? "" : maskmarker;
11587
+ }
11588
+ function maskfield(input) {
11589
+ return maskingfield(input.name, input.shapes) ? maskvalue(input.value) : input.value;
11590
+ }
11591
+ function maskrecord(record2, shapes) {
11592
+ const masked = {};
11593
+ for (const [key, value] of Object.entries(record2)) {
11594
+ if (typeof value === "string") {
11595
+ const sibling = record2.name;
11596
+ masked[key] = key === "value" && typeof sibling === "string" ? maskfield({ name: sibling, value, shapes }) : maskfield({ name: key, value, shapes });
11597
+ } else if (Array.isArray(value)) masked[key] = value.map((item) => Boolean(item) && typeof item === "object" && !Array.isArray(item) ? maskrecord(item, shapes) : item);
11598
+ else if (Boolean(value) && typeof value === "object") masked[key] = maskrecord(value, shapes);
11599
+ else masked[key] = value;
11600
+ }
11601
+ return masked;
11602
+ }
11603
+ function masktypedvalues(input) {
11604
+ const sensitive = maskingfield(input.step.target ?? "", input.shapes) || maskingfield(input.step.kind, input.shapes);
11605
+ const maskedvalue = input.step.value !== void 0 && sensitive ? maskvalue(input.step.value) : input.step.value;
11606
+ let maskedoptions = input.step.options;
11607
+ if (input.step.options !== void 0) {
11608
+ try {
11609
+ const parsed = JSON.parse(input.step.options);
11610
+ if (Boolean(parsed) && typeof parsed === "object" && !Array.isArray(parsed)) maskedoptions = JSON.stringify(maskrecord(parsed, input.shapes));
11611
+ } catch {
11612
+ }
11613
+ }
11614
+ return { ...maskedvalue !== void 0 ? { value: maskedvalue } : {}, ...maskedoptions !== void 0 ? { options: maskedoptions } : {} };
11615
+ }
11616
+ function maskformstate(fields, shapes) {
11617
+ return fields.map((field) => ({ ...field, value: maskfield({ name: field.name, value: field.value, shapes }) }));
11618
+ }
11619
+ function maskobservation(shot, shapes) {
11620
+ return { ...shot, forms: shot.forms.map((form) => maskingfield(form.name, shapes) ? { ...form, options: [maskmarker] } : form) };
11621
+ }
11622
+ function maskstoredvalues(record2, shapes) {
11623
+ const masked = {};
11624
+ for (const [key, value] of Object.entries(record2)) masked[key] = maskfield({ name: key, value, shapes });
11625
+ return masked;
11626
+ }
11627
+ function maskexport(record2, shapes) {
11628
+ return maskrecord(record2, shapes);
11629
+ }
11630
+
10728
11631
  // modelroute.ts
10729
11632
  function routevalid(route) {
10730
11633
  if (route.kind.trim() === "") return { allowed: false, reason: "The model route needs its task kind." };
@@ -10980,6 +11883,44 @@ function removetemplate(templates, name) {
10980
11883
  return templates.filter((template) => template.name !== name);
10981
11884
  }
10982
11885
 
11886
+ // sandboxframe.ts
11887
+ function stripscripts(markup) {
11888
+ return markup.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<script\b[^>]*\/>/gi, "").replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, "").replace(/\son[a-z]+\s*=\s*'[^']*'/gi, "").replace(/\son[a-z]+\s*=\s*[^\s>]+/gi, "").replace(/javascript:/gi, "").trim();
11889
+ }
11890
+ function nonceof(seed) {
11891
+ const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
11892
+ let hash = 0;
11893
+ for (let index = 0; index < seed.length; index += 1) hash = hash * 31 + seed.charCodeAt(index) >>> 0;
11894
+ let nonce = "";
11895
+ let state = hash === 0 ? 2654435769 : hash;
11896
+ for (let index = 0; index < 16; index += 1) {
11897
+ state = state * 1664525 + 1013904223 >>> 0;
11898
+ nonce += alphabet[state % alphabet.length];
11899
+ }
11900
+ return nonce;
11901
+ }
11902
+ function sandboxrenderof(input) {
11903
+ if (input.markup.trim() === "") throw new Error("The sandbox render needs its untrusted markup.");
11904
+ if (input.sourceorigin.trim() === "") throw new Error("The sandbox render needs the source origin of its untrusted markup.");
11905
+ if (input.stepid.trim() === "") throw new Error("The sandbox render names the reviewed step it renders for.");
11906
+ return { id: input.id, nonce: nonceof(`${input.id}:${input.now}`), markup: stripscripts(input.markup), sourceorigin: input.sourceorigin, stepid: input.stepid, renderedat: input.now };
11907
+ }
11908
+ function rendermessage(render) {
11909
+ return { channel: "devthinksandbox", type: "render", nonce: render.nonce, markup: render.markup };
11910
+ }
11911
+ function acceptrenderresult(input) {
11912
+ if (input.message.channel !== "devthinksandbox") return { accepted: false, reason: "The sandbox message travels the devthinksandbox channel only." };
11913
+ if (input.message.type !== "renderresult") return { accepted: false, reason: "The sandbox message answers with the renderresult type only." };
11914
+ const render = input.renders.find((entry) => entry.nonce === input.message.nonce && entry.renderedat <= input.now);
11915
+ if (!render) return { accepted: false, reason: "The sandbox message carries no nonce of a known render; a stale or replayed message never passes." };
11916
+ const text2 = (input.message.text ?? "").replace(/<[^>]*>/g, "");
11917
+ const result = { nonce: render.nonce, ok: input.message.ok !== false, text: text2, summary: input.message.summary?.trim() || `The sandbox frame rendered the markup of the step ${render.stepid} and returned its inert text.`, at: input.now };
11918
+ 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.` };
11919
+ }
11920
+ function renderprovenance(render) {
11921
+ return { origin: render.sourceorigin, stepid: render.stepid, environment: "sandboxframe" };
11922
+ }
11923
+
10983
11924
  // taskqueue.ts
10984
11925
  function emptyqueue(input = {}) {
10985
11926
  return { lanes: input.lanes ?? [], priorities: input.priorities ?? [], completionpolicy: input.completionpolicy ?? "all", items: [], claims: [] };
@@ -11852,6 +12793,50 @@ function reviewframe(input) {
11852
12793
  }
11853
12794
  };
11854
12795
  }
12796
+ function environmentgrammar() {
12797
+ return {
12798
+ version: protocolversion,
12799
+ kinds: environmentrequirements(),
12800
+ notes: [
12801
+ "The evaluate kind runs inside the isolated world only where page globals stay unreachable from step code.",
12802
+ "A step whose reviewed options carry untrusted markup renders inside the sandboxframe only, with scripts and event handlers stripped before the render.",
12803
+ "The parse heavy read kinds offload into the offscreen worker pool only under the user granted offscreen capability and the parse offload toggle, with an inline fallback inside the page.",
12804
+ "Every environment executes only reviewed steps against granted origins; the session environment grant list narrows the steps of one session and no environment ever bypasses the human review."
12805
+ ]
12806
+ };
12807
+ }
12808
+ function environmentreport(input) {
12809
+ return {
12810
+ version: protocolversion,
12811
+ environments: Object.entries(input.environments).map(([stepid, environment]) => ({ stepid, environment })),
12812
+ turnarounds: Object.entries(input.turnarounds ?? {}).map(([stepid, milliseconds]) => ({ stepid, milliseconds })),
12813
+ offscreen: input.offscreen ?? [],
12814
+ workers: input.workers ?? 0,
12815
+ ...input.keepalive !== void 0 ? { keepalive: input.keepalive } : {}
12816
+ };
12817
+ }
12818
+ function consentmodel() {
12819
+ return {
12820
+ version: protocolversion,
12821
+ posture: "denydefault",
12822
+ sensitiveclasses: ["payment", "credential", "delete", "publish"],
12823
+ maskshapes: [...defaultmaskshapes],
12824
+ notes: [
12825
+ "The denydefault posture refuses every origin the user never granted; the per origin automation allowlist holds one exact origin per entry with no wildcard expansion and the active tab grant counts as exactly one explicit single origin grant.",
12826
+ "The per site originprofiles grant and deny single action kinds; a denied kind never runs on that origin and a granted kind still routes its sensitive classes through the fresh consent prompts.",
12827
+ "Every consent window scopes to one session and one origin, binds the duration the user chose and names its boundary; no grant ever defaults to unlimited, and a window past its boundary suspends the run mid step until a new explicit prompt renews it.",
12828
+ "The revokerun is a terminal session event: the pending step and every queued step halt without executing and the immutable log records the user action.",
12829
+ "The immutable run log appends only: every entry chains through its loghash to the hash of its predecessor, the completion seal writes the final hash, and the read path verifies the whole chain before serving a single entry.",
12830
+ "maskinputs keeps typed values, form values and stored values out of every record behind the documented password, token, card and secret shapes the user extends; the observation schema keeps its field shapes while the values carry the redaction marker."
12831
+ ]
12832
+ };
12833
+ }
12834
+ function securityreport(input) {
12835
+ return { version: protocolversion, posture: "denydefault", allowlist: input.allowlist, profiles: input.profiles, windows: input.windows, consents: input.consents, revocations: input.revocations, maskrules: input.maskrules, chain: input.chain };
12836
+ }
12837
+ function logchainreport(input) {
12838
+ return { version: protocolversion, runid: input.runid, valid: input.valid, entries: input.entries, ...input.brokenat !== void 0 ? { brokenat: input.brokenat } : {}, reason: input.reason, ...input.sealhash !== void 0 ? { sealhash: input.sealhash } : {}, ...input.sealedat !== void 0 ? { sealedat: input.sealedat } : {} };
12839
+ }
11855
12840
 
11856
12841
  // workfloweditor.ts
11857
12842
  var palettecategories = ["actions", "controlflow", "waits", "variables", "triggers"];
@@ -12557,8 +13542,11 @@ function yamlscalarvalue(text2) {
12557
13542
  return text2;
12558
13543
  }
12559
13544
  export {
13545
+ acceptrenderresult,
13546
+ acceptworkerresponse,
12560
13547
  ackreview,
12561
13548
  acquirelock,
13549
+ acquirerunlock,
12562
13550
  activelayers,
12563
13551
  addedge,
12564
13552
  addnode,
@@ -12572,6 +13560,7 @@ export {
12572
13560
  agentpresetof,
12573
13561
  agentruncontext,
12574
13562
  agentscopevalid,
13563
+ allowlistcheck,
12575
13564
  allowlistcovers,
12576
13565
  allowlistreport,
12577
13566
  alltools,
@@ -12581,6 +13570,7 @@ export {
12581
13570
  apientries,
12582
13571
  apikeyconsentgranted,
12583
13572
  apireplayspecof,
13573
+ appendlogentry,
12584
13574
  applycooldown,
12585
13575
  applyheaderules,
12586
13576
  applylayer,
@@ -12605,8 +13595,10 @@ export {
12605
13595
  authrefusedmessage,
12606
13596
  authreport,
12607
13597
  autointervalof,
13598
+ automationallowlistgate,
12608
13599
  backoffdelay,
12609
13600
  batchreport,
13601
+ beatrun,
12610
13602
  bindlocalhost,
12611
13603
  bindparam,
12612
13604
  bindvariables,
@@ -12672,6 +13664,7 @@ export {
12672
13664
  cdpeventruleof,
12673
13665
  cdpkinds,
12674
13666
  cdpreport,
13667
+ chainreportof,
12675
13668
  channellive,
12676
13669
  channeloptionsof,
12677
13670
  channelorigin,
@@ -12680,9 +13673,12 @@ export {
12680
13673
  choosebranch,
12681
13674
  claim,
12682
13675
  claimheartbeat,
13676
+ classconsentcovers,
12683
13677
  classifyintent,
12684
13678
  closechannel,
12685
13679
  closeidlechannels,
13680
+ closeoffscreen,
13681
+ closerun,
12686
13682
  collectmessages,
12687
13683
  collectresults,
12688
13684
  commandguard,
@@ -12693,6 +13689,11 @@ export {
12693
13689
  confirmmanualrun,
12694
13690
  connectclient,
12695
13691
  consensusstate,
13692
+ consentdurationvalid,
13693
+ consentmodel,
13694
+ consentprompttext,
13695
+ consentwindowgate,
13696
+ consentwindowstate,
12696
13697
  consolecapture,
12697
13698
  consoleconsentcovers,
12698
13699
  consolediff,
@@ -12724,10 +13725,12 @@ export {
12724
13725
  dedupeimages,
12725
13726
  defaultapprovalwindowms,
12726
13727
  defaultchallengelifetimems,
13728
+ defaultenvironment,
12727
13729
  defaultheartbeatms,
12728
13730
  defaulthttpstream,
12729
13731
  defaultidlewindowms,
12730
13732
  defaultloopbound,
13733
+ defaultmaskshapes,
12731
13734
  defaultmcpconfig,
12732
13735
  defaultmcpport,
12733
13736
  defaultpairinglifetimems,
@@ -12735,6 +13738,9 @@ export {
12735
13738
  defaulttokenlifetimems,
12736
13739
  defaulttriggercooldown,
12737
13740
  delayjitter,
13741
+ deniedevidenceof,
13742
+ denydefaultnotice,
13743
+ denydefaultposture,
12738
13744
  actionrisk as deriveactionrisk,
12739
13745
  detachcdpsession,
12740
13746
  devicepresetof,
@@ -12769,6 +13775,13 @@ export {
12769
13775
  enqueue,
12770
13776
  enqueuerequest,
12771
13777
  entryfresh,
13778
+ entryhashof,
13779
+ environmentgrammar,
13780
+ environmentgrantgate,
13781
+ environmentreport,
13782
+ environmentrequirements,
13783
+ environmentrequirementsof,
13784
+ environmentsof,
12772
13785
  errorcapture,
12773
13786
  errorreportresponse,
12774
13787
  escalate,
@@ -12777,17 +13790,23 @@ export {
12777
13790
  eventnotification,
12778
13791
  eventresponse,
12779
13792
  eventrulematches,
13793
+ exactorigin,
12780
13794
  exchangesreport,
13795
+ executorregistry,
12781
13796
  expandblocks,
12782
13797
  expandtemplate,
12783
13798
  expireapprovals,
13799
+ expireconsentwindows,
12784
13800
  expirelayers,
12785
13801
  expirelocks,
12786
13802
  expireprofilerecords,
13803
+ expirerunlocks,
12787
13804
  expiresessions,
12788
13805
  expiretokens,
12789
13806
  exportcontentreview,
13807
+ exportlogchain,
12790
13808
  exportpresetlibrary,
13809
+ exportrunstate,
12791
13810
  exportsessionfile,
12792
13811
  exportworkflow,
12793
13812
  expressioneval,
@@ -12800,6 +13819,7 @@ export {
12800
13819
  familyofkind,
12801
13820
  fetchoptionsof,
12802
13821
  fetchrequestof,
13822
+ fieldshapekind,
12803
13823
  filteredsessions,
12804
13824
  filterentries,
12805
13825
  filterexchanges,
@@ -12821,6 +13841,7 @@ export {
12821
13841
  growthtrend,
12822
13842
  guardoutput,
12823
13843
  guardverdictgate,
13844
+ haltedstepsof,
12824
13845
  handleframe,
12825
13846
  handoffframe,
12826
13847
  headerfilterof,
@@ -12856,6 +13877,7 @@ export {
12856
13877
  isformkind,
12857
13878
  islocalorigin,
12858
13879
  isnetwatchkind,
13880
+ isolatedinjection,
12859
13881
  isprofilekind,
12860
13882
  issessionkind,
12861
13883
  issocketkind,
@@ -12868,11 +13890,14 @@ export {
12868
13890
  isworkflowkind,
12869
13891
  joinbranches,
12870
13892
  jsonpathrulesof,
13893
+ keepalivegate,
13894
+ keepaliveintervalvalid,
12871
13895
  killall,
12872
13896
  killswitchgate,
12873
13897
  lanereport,
12874
13898
  lapseframes,
12875
13899
  lapseplanof,
13900
+ lasthashof,
12876
13901
  latesttemplate,
12877
13902
  launchbridge,
12878
13903
  layernames,
@@ -12890,7 +13915,10 @@ export {
12890
13915
  locationpresetof,
12891
13916
  locationrangevalid,
12892
13917
  lockkey,
13918
+ logchainreport,
13919
+ logentryof,
12893
13920
  loglevels,
13921
+ logreadgate,
12894
13922
  longtaskcapture,
12895
13923
  loopof,
12896
13924
  mailboxof,
@@ -12899,7 +13927,19 @@ export {
12899
13927
  mapresponse,
12900
13928
  mapurlof,
12901
13929
  markbreakpoint,
13930
+ markpending,
12902
13931
  markprovider,
13932
+ markuprenderstep,
13933
+ maskexport,
13934
+ maskfield,
13935
+ maskformstate,
13936
+ maskingfield,
13937
+ maskmarker,
13938
+ maskobservation,
13939
+ maskrecord,
13940
+ maskstoredvalues,
13941
+ masktypedvalues,
13942
+ maskvalue,
12903
13943
  matchmessage,
12904
13944
  matchurl,
12905
13945
  matchurlpattern,
@@ -12912,6 +13952,7 @@ export {
12912
13952
  messagefilterof,
12913
13953
  methoddomain,
12914
13954
  minimapfocus,
13955
+ missingclassconsents,
12915
13956
  mockfor,
12916
13957
  mockreport,
12917
13958
  mockspecof,
@@ -12938,15 +13979,26 @@ export {
12938
13979
  newsessionrecord,
12939
13980
  newworkflowrun,
12940
13981
  nextrequest,
13982
+ nonceof,
12941
13983
  normalizeendpoint,
12942
13984
  oauthflowof,
12943
13985
  observationmodeof,
12944
13986
  observationresponse,
12945
13987
  observeevents,
13988
+ offfamilyof,
13989
+ offloadkinds,
13990
+ offscreencapabilitygate,
12946
13991
  openchannel,
12947
13992
  openconsensus,
13993
+ openconsentwindow,
13994
+ openoffscreen,
13995
+ openrun,
13996
+ openrunlog,
13997
+ openseal,
12948
13998
  openstreamchannel,
12949
13999
  opentabagent,
14000
+ originprofilegate,
14001
+ originprofileof,
12950
14002
  outcomeresponse,
12951
14003
  overrideinputof,
12952
14004
  overridematches,
@@ -12994,14 +14046,18 @@ export {
12994
14046
  pollcursorof,
12995
14047
  polldecision,
12996
14048
  pollurl,
14049
+ poolplan,
12997
14050
  popscope,
12998
14051
  postentry,
12999
14052
  preparehandoff,
13000
14053
  privatemime,
14054
+ profilegrade,
13001
14055
  profilegrantgranted,
14056
+ profilekind,
13002
14057
  profilereport,
13003
14058
  profileretentionwindow,
13004
14059
  profilerkinds,
14060
+ profilesummary,
13005
14061
  progressnoticeframe,
13006
14062
  promptcallframe,
13007
14063
  promptreport,
@@ -13011,6 +14067,7 @@ export {
13011
14067
  providervalid,
13012
14068
  proxygate,
13013
14069
  proxyrouteof,
14070
+ prunerunstates,
13014
14071
  publishmessage,
13015
14072
  pushscope,
13016
14073
  quarantinereport,
@@ -13026,12 +14083,18 @@ export {
13026
14083
  readentries,
13027
14084
  readpath,
13028
14085
  readstream,
14086
+ readverifiedlog,
14087
+ reattachrun,
13029
14088
  receivemessage,
13030
14089
  receivemessages,
13031
14090
  reconnectwaits,
13032
14091
  recordagentusage,
14092
+ recordenvironment,
13033
14093
  recordingoptionsof,
14094
+ recordturnaround,
14095
+ recordurl,
13034
14096
  recordwatchvalue,
14097
+ recoveryplan,
13035
14098
  redactconsoletext,
13036
14099
  redactedcookies,
13037
14100
  redactparams,
@@ -13046,12 +14109,16 @@ export {
13046
14109
  rejectioncapture,
13047
14110
  relayframe,
13048
14111
  releaselock,
14112
+ releaserunlock,
13049
14113
  removeedge,
13050
14114
  removenode,
13051
14115
  removetemplate,
14116
+ rendermessage,
13052
14117
  renderminimap,
14118
+ renderprovenance,
13053
14119
  rendertemplate,
13054
14120
  rendertoolbriefs,
14121
+ renewconsentwindow,
13055
14122
  reordersteps,
13056
14123
  repeatuntilof,
13057
14124
  replannonfail,
@@ -13093,11 +14160,14 @@ export {
13093
14160
  reviewframe,
13094
14161
  revocationruleof,
13095
14162
  revokeclient,
14163
+ revokerun,
14164
+ revokerungate,
13096
14165
  rewritesourcelocation,
13097
14166
  roleaddress,
13098
14167
  roledefaults,
13099
14168
  rotatelogs,
13100
14169
  rotationruleof,
14170
+ routeenvironment,
13101
14171
  routesfor,
13102
14172
  routevalid,
13103
14173
  rpcerrorcodeof,
@@ -13122,6 +14192,8 @@ export {
13122
14192
  runworkflow,
13123
14193
  safetyresponse,
13124
14194
  samplingframes,
14195
+ sandboxorigingate,
14196
+ sandboxrenderof,
13125
14197
  savetemplate,
13126
14198
  saveworkflow,
13127
14199
  scaledrect,
@@ -13131,21 +14203,29 @@ export {
13131
14203
  scheduleinterval,
13132
14204
  scopecheck,
13133
14205
  scopegate,
14206
+ scopegrantof,
14207
+ sealrunlog,
14208
+ sealrunstate,
13134
14209
  seamweights,
13135
14210
  searchfields,
13136
14211
  searchqueryof,
13137
14212
  searchsessionrecords,
13138
14213
  searchsteps,
13139
14214
  searchtemplates,
14215
+ securityreport,
13140
14216
  seededrandom,
13141
14217
  selectorresponse,
13142
14218
  sendcdpcommand,
13143
14219
  sendfetch,
13144
14220
  sendmessage,
14221
+ sensitiveclassesof,
14222
+ sensitiveclassgate,
14223
+ sensitivepipelingate,
13145
14224
  sequenceintegrity,
13146
14225
  serializearg,
13147
14226
  serializecdpcommand,
13148
14227
  serializeframe,
14228
+ serializesteps,
13149
14229
  serverbindgate,
13150
14230
  servercapabilities,
13151
14231
  serverenablementgate,
@@ -13160,6 +14240,7 @@ export {
13160
14240
  sessionrestoregate,
13161
14241
  sessiontabof,
13162
14242
  setvariable,
14243
+ shapesof,
13163
14244
  sharelesson,
13164
14245
  shareworkflow,
13165
14246
  shiftentryof,
@@ -13182,6 +14263,7 @@ export {
13182
14263
  starttls,
13183
14264
  statusclassof,
13184
14265
  steal,
14266
+ stepenvironmentvalid,
13185
14267
  stepmodeof,
13186
14268
  steptemplateof,
13187
14269
  stepwindows,
@@ -13192,6 +14274,7 @@ export {
13192
14274
  streamsummaries,
13193
14275
  streamwindowof,
13194
14276
  stripguardrails,
14277
+ stripscripts,
13195
14278
  structurederrorreport,
13196
14279
  submitreviewgranted,
13197
14280
  subscriptionframes,
@@ -13246,6 +14329,7 @@ export {
13246
14329
  tracestart,
13247
14330
  tracetofile,
13248
14331
  trailreport,
14332
+ transferablekeys,
13249
14333
  transferhandoff,
13250
14334
  transformgrammar,
13251
14335
  triggereventcatalog,
@@ -13278,6 +14362,7 @@ export {
13278
14362
  validatevaluegen,
13279
14363
  validateworkflow,
13280
14364
  verifyauth,
14365
+ verifylogchain,
13281
14366
  verifytoken,
13282
14367
  verifywebhook,
13283
14368
  visitmatch,
@@ -13290,8 +14375,13 @@ export {
13290
14375
  watchgate,
13291
14376
  webhooksecretok,
13292
14377
  whileof,
14378
+ wildcardentry,
14379
+ windowgatesstep,
13293
14380
  wireformat,
13294
14381
  wizardreport,
14382
+ workerpoolsizevalid,
14383
+ workerrequestof,
14384
+ workerresponseof,
13295
14385
  workflowblockof,
13296
14386
  workflowfileversion,
13297
14387
  workflowgate,
@@ -13300,6 +14390,7 @@ export {
13300
14390
  workflowreport,
13301
14391
  workflowstepof,
13302
14392
  workstealgrade,
14393
+ zombiesweep,
13303
14394
  zoomcanvas
13304
14395
  };
13305
14396
  //# sourceMappingURL=index.js.map