@wenathlan/extension 1.1.58 → 1.1.60

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 +6 -4
  2. package/dist/cli.js +19 -2
  3. package/dist/coordination.d.ts +139 -0
  4. package/dist/coordination.d.ts.map +1 -0
  5. package/dist/environments.d.ts +96 -0
  6. package/dist/environments.d.ts.map +1 -0
  7. package/dist/index.d.ts +6 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1080 -1
  10. package/dist/index.js.map +4 -4
  11. package/dist/memory.d.ts +119 -1
  12. package/dist/memory.d.ts.map +1 -1
  13. package/dist/multiagent.d.ts +1 -1
  14. package/dist/multiagent.d.ts.map +1 -1
  15. package/dist/orchestration.d.ts +174 -0
  16. package/dist/orchestration.d.ts.map +1 -0
  17. package/dist/policy.d.ts +76 -1
  18. package/dist/policy.d.ts.map +1 -1
  19. package/dist/protocol.d.ts +82 -3
  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 +444 -4
  26. package/dist/types.d.ts.map +1 -1
  27. package/dist/version.d.ts +1 -1
  28. package/extension/dist/background.js +1837 -58
  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 +7 -0
  35. package/extension/dist/pagebridge.js.map +2 -2
  36. package/extension/dist/popup.html +1 -1
  37. package/extension/dist/popup.js +39 -1
  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 +848 -3
  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
@@ -1908,6 +1908,8 @@ function watchdogpass(input) {
1908
1908
  function roledefaults(role) {
1909
1909
  if (role === "planner") return { toolnamespaces: ["workflow", "memory", "system"], description: "Planners compose reviewed plans and read memory; they never act on the page themselves." };
1910
1910
  if (role === "observer") return { toolnamespaces: ["memory", "system"], description: "Observers read the shared memory and the system reports only." };
1911
+ if (role === "critic") return { toolnamespaces: ["workflow", "memory", "system"], description: "Critics review the outputs of the other agents read only; they never act on the page themselves." };
1912
+ if (role === "verifier") return { toolnamespaces: ["browser", "memory", "system"], description: "Verifiers re-read the page to check the claims of the other agents; their checks stay read side." };
1911
1913
  return { toolnamespaces: ["browser", "workflow", "memory", "system"], description: role === "worker" ? "Workers execute the reviewed steps of approved plans." : `The custom role ${role} carries the worker defaults until the user narrows its scope.` };
1912
1914
  }
1913
1915
  function registeragent(input) {
@@ -2012,6 +2014,170 @@ function swarmstateof(input) {
2012
2014
  return { agents: input.agents, queue: input.queue, mailboxes: input.mailboxes, killswitch: input.killswitch };
2013
2015
  }
2014
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
+
2015
2181
  // memory.ts
2016
2182
  var sessionmemory = class {
2017
2183
  constructor(adapter) {
@@ -4233,6 +4399,244 @@ var sessionmemory = class {
4233
4399
  const mailboxes = await this.getmailboxes();
4234
4400
  return swarmoverview({ agents, queue: queue ?? { lanes: [], priorities: [], completionpolicy: "all", items: [], claims: [] }, mailboxes });
4235
4401
  }
4402
+ /** Returns the leader worker topology of the 1.1.59 swarm with its leader, worker, critic and verifier lanes and its worker assignments. */
4403
+ async gettopology() {
4404
+ return this.adapter.get("swarmtopology");
4405
+ }
4406
+ /** Replaces the stored leader worker topology after one election, assignment, collection or scaling change. */
4407
+ async settopology(topology) {
4408
+ return this.adapter.set("swarmtopology", topology);
4409
+ }
4410
+ /** Returns the stored planner executor splits of the 1.1.59 swarm with their step reports. */
4411
+ async getplannersplits() {
4412
+ return await this.adapter.get("swarmsplits") ?? [];
4413
+ }
4414
+ /** Replaces the stored planner executor splits after one split or one executor step report. */
4415
+ async setplannersplits(splits) {
4416
+ return this.adapter.set("swarmsplits", splits);
4417
+ }
4418
+ /** Records one critic review of an agent output, newest first. */
4419
+ async addcriticreview(review) {
4420
+ await this.adapter.set("swarmreviews", [review, ...await this.adapter.get("swarmreviews") ?? []]);
4421
+ }
4422
+ /** Returns the recorded critic reviews, newest first. */
4423
+ async getcriticreviews() {
4424
+ return await this.adapter.get("swarmreviews") ?? [];
4425
+ }
4426
+ /** Records one verifier check of a result claim, newest first. */
4427
+ async addverifiercheck(check) {
4428
+ await this.adapter.set("swarmverifierchecks", [check, ...await this.adapter.get("swarmverifierchecks") ?? []]);
4429
+ }
4430
+ /** Returns the recorded verifier checks with their pass and fail outcomes, newest first. */
4431
+ async getverifierchecks() {
4432
+ return await this.adapter.get("swarmverifierchecks") ?? [];
4433
+ }
4434
+ /** Replaces the stored review requests routed between agents after one request, ack, answer or timeout. */
4435
+ async setreviewrequests(requests) {
4436
+ return this.adapter.set("swarmreviewrequests", requests);
4437
+ }
4438
+ /** Returns the stored review requests routed between agents. */
4439
+ async getreviewrequests() {
4440
+ return await this.adapter.get("swarmreviewrequests") ?? [];
4441
+ }
4442
+ /** Records one tab handoff with its packaged task state and its resumed state. */
4443
+ async addhandoff(record2) {
4444
+ await this.adapter.set("swarmhandoffs", [record2, ...await this.adapter.get("swarmhandoffs") ?? []].filter((entry, index, all) => all.findIndex((candidate) => candidate.id === entry.id) === index));
4445
+ }
4446
+ /** Replaces one stored handoff record after its transfer or resume. */
4447
+ async updatehandoff(record2) {
4448
+ await this.adapter.set("swarmhandoffs", (await this.adapter.get("swarmhandoffs") ?? []).map((entry) => entry.id === record2.id ? record2 : entry));
4449
+ }
4450
+ /** Returns the handoff log of tab transfers between agents, newest first. */
4451
+ async gethandoffs() {
4452
+ return await this.adapter.get("swarmhandoffs") ?? [];
4453
+ }
4454
+ /** Replaces the stored resource locks after one acquire, release or expiry sweep. */
4455
+ async setlocks(locks) {
4456
+ return this.adapter.set("swarmlocks", locks);
4457
+ }
4458
+ /** Returns the held resource locks with their holders and expiries. */
4459
+ async getlocks() {
4460
+ return await this.adapter.get("swarmlocks") ?? [];
4461
+ }
4462
+ /** Records one conflict scan report of overlapping writes, newest first. */
4463
+ async addconflictscan(scan) {
4464
+ await this.adapter.set("swarmconflicts", [scan, ...await this.adapter.get("swarmconflicts") ?? []]);
4465
+ }
4466
+ /** Returns the recorded conflict scan reports, newest first. */
4467
+ async getconflictscans() {
4468
+ return await this.adapter.get("swarmconflicts") ?? [];
4469
+ }
4470
+ /** Stores the merged result report with its mergeentry provenance. */
4471
+ async setreport(report) {
4472
+ return this.adapter.set("swarmreport", report);
4473
+ }
4474
+ /** Returns the stored merged result report across agents. */
4475
+ async getreport() {
4476
+ return this.adapter.get("swarmreport");
4477
+ }
4478
+ /** Records one progressboard snapshot under the user configured retention window; an absent window keeps every snapshot. */
4479
+ async addboardsnapshot(board) {
4480
+ const retention = (await this.getsettings())?.boardretention;
4481
+ await this.adapter.set("swarmboards", [board, ...await this.adapter.get("swarmboards") ?? []].slice(0, retention ?? 100));
4482
+ }
4483
+ /** Returns the stored progressboard snapshots, newest first. */
4484
+ async getboardsnapshots() {
4485
+ return await this.adapter.get("swarmboards") ?? [];
4486
+ }
4487
+ /** Records one escalation lifted to the user, newest first. */
4488
+ async addescalation(escalation) {
4489
+ await this.adapter.set("swarmescalations", [escalation, ...await this.adapter.get("swarmescalations") ?? []]);
4490
+ }
4491
+ /** Replaces one stored escalation after its user decision. */
4492
+ async updateescalation(escalation) {
4493
+ await this.adapter.set("swarmescalations", (await this.adapter.get("swarmescalations") ?? []).map((entry) => entry.id === escalation.id ? escalation : entry));
4494
+ }
4495
+ /** Returns the escalations awaiting the user and the decided ones, newest first. */
4496
+ async getescalations() {
4497
+ return await this.adapter.get("swarmescalations") ?? [];
4498
+ }
4499
+ /** Records one consensus round or replaces the stored one after a vote. */
4500
+ async setconsensusround(round) {
4501
+ const rounds = await this.adapter.get("swarmconsensus") ?? [];
4502
+ await this.adapter.set("swarmconsensus", rounds.some((entry) => entry.id === round.id) ? rounds.map((entry) => entry.id === round.id ? round : entry) : [round, ...rounds]);
4503
+ }
4504
+ /** Returns the consensus rounds with their votes and quorum states, newest first. */
4505
+ async getconsensusrounds() {
4506
+ return await this.adapter.get("swarmconsensus") ?? [];
4507
+ }
4508
+ /** Appends one action to the interleaved timeline of swarm actions, oldest first under a window of 500. */
4509
+ async addswarmaction(action) {
4510
+ await this.adapter.set("swarmtimeline", [...await this.adapter.get("swarmtimeline") ?? [], action].slice(-500));
4511
+ }
4512
+ /** Returns the interleaved timeline of swarm actions with the optional agent and kind filters, oldest first. */
4513
+ async getswarmtimeline(filters) {
4514
+ const actions = await this.adapter.get("swarmtimeline") ?? [];
4515
+ return actions.filter((action) => filters?.agentid === void 0 || action.agentid === filters.agentid).filter((action) => filters?.kind === void 0 || action.kind === filters.kind).filter((action) => filters?.since === void 0 || action.at >= filters.since);
4516
+ }
4517
+ /** Stores one shared cost accounting snapshot of the swarm, newest first. */
4518
+ async addswarmcost(cost) {
4519
+ await this.adapter.set("swarmcosts", [cost, ...await this.adapter.get("swarmcosts") ?? []].slice(0, 100));
4520
+ }
4521
+ /** Returns the stored shared cost accounting snapshots of the swarm, newest first. */
4522
+ async getswarmcosts() {
4523
+ return await this.adapter.get("swarmcosts") ?? [];
4524
+ }
4525
+ /**
4526
+ * Execution environment persistence of the 1.1.60 family.
4527
+ * 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.
4528
+ * The adapter seam keeps every accessor a one line storage delegation so a future worker state backend replaces the adapter only.
4529
+ */
4530
+ /** Returns the environment grant list of the active session; an absent list keeps the documented default posture. */
4531
+ async getenvironmentgrants() {
4532
+ return (await this.getsession())?.environmentgrants;
4533
+ }
4534
+ /** Replaces the environment grant list of the active session so the environment grants join the origin grants in the session record. */
4535
+ async setenvironmentgrants(grants) {
4536
+ const session = await this.getsession();
4537
+ if (!session) throw new Error("The environment grants need an active session to join.");
4538
+ await this.setsession({ ...session, environmentgrants: grants });
4539
+ }
4540
+ /** 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. */
4541
+ async setrunstate(profileid, state) {
4542
+ const sealed = await sealrunstate(state);
4543
+ const index = await this.adapter.get("runstateindex") ?? [];
4544
+ await this.adapter.set(`runstate:${profileid}`, sealed);
4545
+ if (!index.includes(profileid)) await this.adapter.set("runstateindex", [...index, profileid]);
4546
+ }
4547
+ /** Opens the sealed run state of one profile; a missing or tampered seal returns undefined so the recovery never trusts a broken record. */
4548
+ async getrunstate(profileid) {
4549
+ const sealed = await this.adapter.get(`runstate:${profileid}`);
4550
+ if (!sealed) return void 0;
4551
+ try {
4552
+ return await openseal(sealed);
4553
+ } catch {
4554
+ return void 0;
4555
+ }
4556
+ }
4557
+ /** 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. */
4558
+ async removerunstate(profileid) {
4559
+ const index = await this.adapter.get("runstateindex") ?? [];
4560
+ await this.adapter.set("runstateindex", index.filter((entry) => entry !== profileid));
4561
+ await this.adapter.set(`runstate:${profileid}`, { payload: "", algorithm: "sha-256", digest: "", sealedat: 0 });
4562
+ }
4563
+ /** Lists the stored run state records of every profile, oldest update first. */
4564
+ async listrunstates() {
4565
+ const index = await this.adapter.get("runstateindex") ?? [];
4566
+ const states = [];
4567
+ for (const profileid of index) {
4568
+ const state = await this.getrunstate(profileid);
4569
+ if (state) states.push(state);
4570
+ }
4571
+ return states.sort((one, two) => one.updatedat - two.updatedat);
4572
+ }
4573
+ /** 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. */
4574
+ async expirerunstates(window, now) {
4575
+ if (window === void 0) return await this.listrunstates();
4576
+ const index = await this.adapter.get("runstateindex") ?? [];
4577
+ const kept = [];
4578
+ for (const profileid of index) {
4579
+ const state = await this.getrunstate(profileid);
4580
+ if (!state) continue;
4581
+ if (now - state.updatedat > window && state.keepalive.state === "stopped") {
4582
+ const summary = { runid: state.runid, sessionid: state.sessionid, planid: state.planid, profileid: state.profileid, state: "expired", urlhistory: [], environments: {}, turnarounds: {}, keepalive: state.keepalive, updatedat: now };
4583
+ const sealed = await sealrunstate(summary);
4584
+ await this.adapter.set(`runstate:${profileid}`, sealed);
4585
+ } else {
4586
+ kept.push(state);
4587
+ }
4588
+ }
4589
+ return kept;
4590
+ }
4591
+ /** Records one worker spawn or teardown event with its provenance beside the step outcomes. */
4592
+ async addworkerevent(event) {
4593
+ await this.adapter.set("workerevents", [event, ...await this.adapter.get("workerevents") ?? []].slice(0, 500));
4594
+ }
4595
+ /** Returns the recorded worker spawn and teardown events, newest first. */
4596
+ async getworkerevents() {
4597
+ return await this.adapter.get("workerevents") ?? [];
4598
+ }
4599
+ /** Records one spawned offscreen document with its reasons and justification in the registry. */
4600
+ async addoffscreenentry(entry) {
4601
+ await this.adapter.set("offscreenregistry", [entry, ...await this.adapter.get("offscreenregistry") ?? []]);
4602
+ }
4603
+ /** Replaces one registry entry after its offscreen document closes. */
4604
+ async updateoffscreenentry(entry) {
4605
+ await this.adapter.set("offscreenregistry", (await this.adapter.get("offscreenregistry") ?? []).map((candidate) => candidate.runid === entry.runid ? entry : candidate));
4606
+ }
4607
+ /** Returns the offscreen document registry with the reasons and justification of every spawn. */
4608
+ async getoffscreenentries() {
4609
+ return await this.adapter.get("offscreenregistry") ?? [];
4610
+ }
4611
+ /** Records one sandbox render with its provenance, source origin and nonce. */
4612
+ async addsandboxrender(render) {
4613
+ await this.adapter.set("sandboxrenders", [render, ...await this.adapter.get("sandboxrenders") ?? []].slice(0, 500));
4614
+ }
4615
+ /** Returns the recorded sandbox renders with their provenance, newest first. */
4616
+ async getsandboxrenders() {
4617
+ return await this.adapter.get("sandboxrenders") ?? [];
4618
+ }
4619
+ /** Replaces the stored run locks after one acquisition, release or expiry sweep. */
4620
+ async setrunlocks(locks) {
4621
+ return this.adapter.set("runlocks", locks);
4622
+ }
4623
+ /** Returns the held run locks with their sessions, runs and expiries. */
4624
+ async getrunlocks() {
4625
+ return await this.adapter.get("runlocks") ?? [];
4626
+ }
4627
+ /** Tracks the storage quota usage of the run state: the last measured bytes stay beside the user configured ceiling so the pruning reads both. */
4628
+ async trackrunstatequota(used) {
4629
+ const settings = await this.getsettings();
4630
+ await this.adapter.set("runstatequota", { used, ...settings?.runstatebytes !== void 0 ? { ceiling: settings.runstatebytes } : {}, trackedat: Date.now() });
4631
+ }
4632
+ /** Returns the last tracked storage quota usage of the run state with its ceiling when the user configured one. */
4633
+ async getrunstatequota() {
4634
+ return this.adapter.get("runstatequota");
4635
+ }
4636
+ /** Exports every stored run state as one single audit record through the runstate export envelope. */
4637
+ async exportrunstates() {
4638
+ return exportrunstate(await this.listrunstates(), Date.now());
4639
+ }
4236
4640
  };
4237
4641
  function mediakindof(record2) {
4238
4642
  if ("pages" in record2) return "pdf";
@@ -4873,6 +5277,282 @@ function tlsstateof(tls) {
4873
5277
  return { mode: tls.mode, certificaterequired: tls.mode === "required" || tls.certificatefingerprint !== void 0, verified: tls.verifiedat !== void 0 };
4874
5278
  }
4875
5279
 
5280
+ // coordination.ts
5281
+ function lockkey(origin, selector) {
5282
+ return `${origin}|${selector}`;
5283
+ }
5284
+ function preparehandoff(input) {
5285
+ if (!input.agents.some((agent) => agent.id === input.fromagentid)) throw new Error(`The handoff names the transferring agent ${input.fromagentid} which is not registered.`);
5286
+ if (!input.agents.some((agent) => agent.id === input.toagentid)) throw new Error(`The handoff names the receiving agent ${input.toagentid} which is not registered.`);
5287
+ if (input.fromagentid === input.toagentid) throw new Error("A handoff moves a task between two different agents; an agent never hands off to itself.");
5288
+ if (input.taskstate.trim() === "") throw new Error("The handoff needs its packaged task state in plain language; the resume continues exactly from it.");
5289
+ const from = input.agents.find((agent) => agent.id === input.fromagentid);
5290
+ const tabid = input.tabid ?? from.tabid;
5291
+ if (tabid === void 0) throw new Error("The handoff needs its tab id; the transferring agent holds no tab to hand off.");
5292
+ return { id: input.id, fromagentid: input.fromagentid, toagentid: input.toagentid, tabid, taskstate: input.taskstate, state: "prepared", ...input.reason !== void 0 && input.reason.trim() !== "" ? { reason: input.reason } : {}, createdat: input.now };
5293
+ }
5294
+ function transferhandoff(input) {
5295
+ const record2 = input.handoffs.find((entry) => entry.id === input.id);
5296
+ if (!record2) throw new Error(`The handoff ${input.id} does not exist.`);
5297
+ if (record2.state !== "prepared") throw new Error(`The handoff ${record2.id} is ${record2.state}; only a prepared handoff transfers.`);
5298
+ const receiver = input.agents.find((agent) => agent.id === record2.toagentid);
5299
+ if (!receiver) throw new Error(`The receiving agent ${record2.toagentid} is not registered.`);
5300
+ if (receiver.state === "stopped") throw new Error(`The receiving agent ${receiver.name} is stopped; the handoff waits for its resume or another receiver.`);
5301
+ const holder = input.agents.find((agent) => agent.tabid === record2.tabid && agent.id !== record2.fromagentid && agent.state !== "stopped");
5302
+ if (holder) throw new Error(`Tab ${record2.tabid} already holds the agent ${holder.name}; one tab binds one agent.`);
5303
+ const agents = input.agents.map((agent) => {
5304
+ if (agent.id === record2.fromagentid) {
5305
+ const { tabid, ...rest } = agent;
5306
+ void tabid;
5307
+ return rest;
5308
+ }
5309
+ if (agent.id === record2.toagentid && record2.tabid !== void 0) return { ...agent, tabid: record2.tabid };
5310
+ return agent;
5311
+ });
5312
+ return { agents, handoffs: input.handoffs.map((entry) => entry.id === input.id ? { ...entry, state: "transferred", transferredat: input.now } : entry) };
5313
+ }
5314
+ function resumehandoff(input) {
5315
+ const record2 = input.handoffs.find((entry) => entry.id === input.id);
5316
+ if (!record2) throw new Error(`The handoff ${input.id} does not exist.`);
5317
+ if (record2.state !== "transferred") throw new Error(`The handoff ${record2.id} is ${record2.state}; only a transferred handoff resumes.`);
5318
+ return { ...record2, state: "resumed", resumedat: input.now };
5319
+ }
5320
+ function acquirelock(input) {
5321
+ if (input.holder.trim() === "") throw new Error("The lock needs its holder agent id.");
5322
+ if (input.origin.trim() === "") throw new Error("The lock needs its origin; a lock never spans unrelated origins.");
5323
+ if (input.selector.trim() === "") throw new Error("The lock needs its selector of the origin.");
5324
+ const kind = input.kind ?? "exclusive";
5325
+ const key = lockkey(input.origin.trim(), input.selector.trim());
5326
+ const held = input.locks.filter((lock2) => lock2.key === key);
5327
+ if (held.some((lock2) => lock2.holder === input.holder)) return { locks: input.locks, acquired: false, reason: `The agent ${input.holder} already holds the lock ${key}.` };
5328
+ if (held.length > 0) {
5329
+ if (held.some((lock2) => lock2.kind === "exclusive") || kind === "exclusive") return { locks: input.locks, acquired: false, reason: `The lock ${key} is held ${held.some((lock2) => lock2.kind === "exclusive") ? "exclusively" : "shared"}; the ${kind} request of ${input.holder} refuses.` };
5330
+ }
5331
+ const lock = { key, holder: input.holder, kind, origin: input.origin.trim(), selector: input.selector.trim(), acquiredat: input.now, ...input.expiresat !== void 0 ? { expiresat: input.expiresat } : {} };
5332
+ return { locks: [...input.locks, lock], acquired: true, reason: `The ${kind} lock ${key} went to the agent ${input.holder}.` };
5333
+ }
5334
+ function releaselock(input) {
5335
+ const lock = input.locks.find((entry) => entry.key === input.key && entry.holder === input.holder);
5336
+ if (!lock) return { locks: input.locks, released: false };
5337
+ return { locks: input.locks.filter((entry) => entry.key !== input.key || entry.holder !== input.holder), released: true };
5338
+ }
5339
+ function expirelocks(input) {
5340
+ const stale = input.locks.filter((lock) => lock.expiresat !== void 0 && input.now > lock.expiresat);
5341
+ if (stale.length === 0) return { locks: input.locks, expired: [] };
5342
+ const keys = new Set(stale.map((lock) => `${lock.key}:${lock.holder}`));
5343
+ return { locks: input.locks.filter((lock) => !keys.has(`${lock.key}:${lock.holder}`)), expired: [...keys] };
5344
+ }
5345
+ function scanconflicts(input) {
5346
+ const targets = /* @__PURE__ */ new Map();
5347
+ for (const writer of input.writers) {
5348
+ const key = lockkey(writer.origin, writer.selector);
5349
+ targets.set(key, [...targets.get(key) ?? [], writer]);
5350
+ }
5351
+ const overlaps = [...targets.entries()].filter(([, writers]) => writers.length > 1).map(([key, writers]) => ({ origin: writers[0].origin, selector: writers[0].selector, writers: writers.map((writer) => writer.agentid) }));
5352
+ const overlappingagents = new Set(overlaps.flatMap((entry) => entry.writers));
5353
+ return {
5354
+ id: input.id,
5355
+ writers: input.writers,
5356
+ overlaps,
5357
+ suggestedorder: input.writers.filter((writer) => overlappingagents.has(writer.agentid)).map((writer) => writer.agentid).filter((agentid, index, all) => all.indexOf(agentid) === index).sort((one, two) => one < two ? -1 : 1),
5358
+ clean: overlaps.length === 0,
5359
+ scannedat: input.now
5360
+ };
5361
+ }
5362
+ function mergeresults(input) {
5363
+ const keys = /* @__PURE__ */ new Map();
5364
+ for (const entry of input.entries) {
5365
+ keys.set(entry.key, [...keys.get(entry.key) ?? [], entry]);
5366
+ }
5367
+ const conflicts = [];
5368
+ const merged = [];
5369
+ for (const [key, entries] of keys) {
5370
+ const ordered = [...entries].sort((one, two) => one.mergedat - two.mergedat);
5371
+ if (ordered.length === 1) {
5372
+ merged.push(ordered[0]);
5373
+ continue;
5374
+ }
5375
+ if (input.rule === "fail") {
5376
+ conflicts.push(`The key ${key} carries ${ordered.length} parallel values from ${ordered.map((entry) => entry.agentid).join(", ")}; the fail rule refuses the fold.`);
5377
+ continue;
5378
+ }
5379
+ const winner = input.rule === "first" ? ordered[0] : input.rule === "last" ? ordered[ordered.length - 1] : ordered.find((entry) => entry.agentid === input.preferagent) ?? ordered[ordered.length - 1];
5380
+ const note = input.rule === "preferagent" && input.preferagent !== void 0 && !ordered.some((entry) => entry.agentid === input.preferagent) ? `The preferagent rule names the agent ${input.preferagent} which wrote no value; the latest value of ${winner.agentid} stayed.` : `The ${input.rule} rule kept the value of ${winner.agentid} from ${ordered.map((entry) => entry.agentid).join(", ")}.`;
5381
+ conflicts.push(`The key ${key}: ${note}`);
5382
+ merged.push({ ...winner, id: `${winner.id}:merged`, conflict: note });
5383
+ }
5384
+ return { entries: merged, conflicts, refused: input.rule === "fail" && conflicts.length > 0 };
5385
+ }
5386
+ function swarmreport(input) {
5387
+ if (input.title.trim() === "") throw new Error("The report needs its title.");
5388
+ const fold = mergeresults({ entries: input.outputs, rule: input.rule, ...input.preferagent !== void 0 ? { preferagent: input.preferagent } : {}, now: input.now });
5389
+ const groups = /* @__PURE__ */ new Map();
5390
+ for (const entry of fold.entries) {
5391
+ const group = entry.taskid ?? "general";
5392
+ groups.set(group, [...groups.get(group) ?? [], entry]);
5393
+ }
5394
+ const sections = [...groups.entries()].map(([taskid, entries]) => ({ title: `Task ${taskid}`, entries, sources: [...new Set(input.outputs.filter((output) => (output.taskid ?? "general") === taskid).map((output) => output.agentid))] }));
5395
+ return {
5396
+ report: { id: input.id, title: input.title.trim(), sections, sources: [...new Set(input.outputs.map((output) => output.agentid))], ...input.confidence !== void 0 && input.confidence.trim() !== "" ? { confidence: input.confidence } : {}, createdat: input.now },
5397
+ conflicts: fold.conflicts,
5398
+ refused: fold.refused
5399
+ };
5400
+ }
5401
+ function compareoutputs(input) {
5402
+ if (input.subject.trim() === "") throw new Error("The comparison needs its subject.");
5403
+ if (input.outputs.length < 2) throw new Error("The comparison contrasts at least two competing outputs.");
5404
+ const differences = input.outputs.filter((output) => output.value !== input.outputs[0]?.value).map((output) => `The agent ${output.agentid} answers ${output.value} while the agent ${input.outputs[0].agentid} answers ${input.outputs[0].value}.`);
5405
+ return { id: input.id, subject: input.subject, outputs: input.outputs, differences, comparedat: input.now };
5406
+ }
5407
+ function interleavetimeline(actions) {
5408
+ return [...actions].sort((one, two) => one.at - two.at || (one.id < two.id ? -1 : 1));
5409
+ }
5410
+ function sharelesson(input) {
5411
+ if (input.statement.trim() === "") throw new Error("The lesson needs its statement in plain language.");
5412
+ if (input.verifiedby.trim() === "") throw new Error("The lesson needs its verifier; only a verified lesson lands on the board.");
5413
+ return postentry({ board: input.board, id: input.id, key: `lesson:${input.statement.trim().slice(0, 40)}`, value: `${input.statement.trim()} (verified by ${input.verifiedby.trim()})`, section: input.section ?? "findings", author: input.agentid.trim() === "" ? "user" : input.agentid, consentclass: input.consentclass ?? "read", now: input.now });
5414
+ }
5415
+ function swarmcosts(input) {
5416
+ return {
5417
+ agents: input.usage.length,
5418
+ tokens: input.usage.reduce((total, usage) => total + usage.tokens, 0),
5419
+ cost: input.usage.reduce((total, usage) => total + usage.cost, 0),
5420
+ steps: input.usage.reduce((total, usage) => total + usage.steps, 0),
5421
+ ...input.currency !== void 0 && input.currency.trim() !== "" ? { currency: input.currency } : {},
5422
+ computedat: input.now
5423
+ };
5424
+ }
5425
+ function replayagentrun(input) {
5426
+ 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 } : {} })));
5427
+ }
5428
+
5429
+ // environments.ts
5430
+ var offloadfamilies = [
5431
+ { task: "htmlsnapshot", kinds: ["readhtml", "parsehtml", "readertree", "readoutline", "classifypage"] },
5432
+ { task: "jsonpayload", kinds: ["readjson", "parsejson"] },
5433
+ { task: "tablerows", kinds: ["readtable", "scrapetable", "detecttables", "deduperows", "transformvalues"] },
5434
+ { task: "a11ytree", kinds: ["a11ytree"] },
5435
+ { task: "complexselector", kinds: ["resolvexpath", "deriveselector", "detectvirtual"] },
5436
+ { task: "stitchshots", kinds: ["contactsheet", "timelapse", "makethumbs"] }
5437
+ ];
5438
+ function offfamilyof(kind) {
5439
+ return offloadfamilies.find((family) => family.kinds.includes(kind))?.task;
5440
+ }
5441
+ function environmentsof(step) {
5442
+ if (markuprenderstep(step)) return ["sandboxframe"];
5443
+ if (step.kind === "evaluate") return ["isolatedworld"];
5444
+ if (offfamilyof(step.kind) !== void 0) return ["pagecontext", "offscreenworker"];
5445
+ return ["pagecontext"];
5446
+ }
5447
+ function defaultenvironment(step) {
5448
+ if (markuprenderstep(step)) return "sandboxframe";
5449
+ if (step.kind === "evaluate") return "isolatedworld";
5450
+ return "pagecontext";
5451
+ }
5452
+ function offamilyeligible(kind) {
5453
+ return offloadfamilies.some((family) => family.kinds.includes(kind));
5454
+ }
5455
+ function offloadkinds() {
5456
+ return offloadfamilies.map((family) => ({ task: family.task, kinds: [...family.kinds] }));
5457
+ }
5458
+ function markuprenderstep(step) {
5459
+ if (!step.options) return false;
5460
+ try {
5461
+ const parsed = JSON.parse(step.options);
5462
+ return Boolean(parsed && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.markup === "string" && parsed.markup.trim() !== "");
5463
+ } catch {
5464
+ return false;
5465
+ }
5466
+ }
5467
+ function environmentrequirementsof(kinds) {
5468
+ return kinds.map((kind) => {
5469
+ const bare = { kind };
5470
+ const environments = environmentsof(bare);
5471
+ return { kind, environments, defaultenvironment: defaultenvironment(bare) };
5472
+ });
5473
+ }
5474
+ function executorregistry() {
5475
+ return [
5476
+ { environment: "pagecontext", adapter: "pagebridge", description: "The page bridge executes dom actions inside the live page because page events only fire there." },
5477
+ { environment: "isolatedworld", adapter: "scriptingapi", description: "The scripting api injects step logic inside the isolated world where page globals stay unreachable from step code." },
5478
+ { 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." },
5479
+ { 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." }
5480
+ ];
5481
+ }
5482
+ function routeenvironment(step, input) {
5483
+ const allowed = environmentsof(step);
5484
+ const named = step.environment;
5485
+ if (named !== void 0) {
5486
+ 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.` };
5487
+ return { environment: named, fallback: false, reason: `The reviewed step names its ${named} environment and the ${step.kind} kind permits it.` };
5488
+ }
5489
+ if (markuprenderstep(step)) return { environment: "sandboxframe", fallback: false, reason: `The ${step.kind} step carries untrusted markup, so it renders inside the sandboxframe only.` };
5490
+ 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." };
5491
+ if (offamilyeligible(step.kind)) {
5492
+ 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.` };
5493
+ 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.` };
5494
+ return { environment: "offscreenworker", fallback: false, reason: `The ${step.kind} step offloads into the offscreen worker pool under the granted capability.` };
5495
+ }
5496
+ return { environment: "pagecontext", fallback: false, reason: `The ${step.kind} step keeps the pagecontext because page events only fire inside the live page.` };
5497
+ }
5498
+ function workerrequestof(input) {
5499
+ const task = offfamilyof(input.kind);
5500
+ if (task === void 0) throw new Error(`The ${input.kind} kind stays outside the offscreen worker pool families.`);
5501
+ if (input.payload.trim() === "") throw new Error("The worker request needs its payload reference.");
5502
+ return { id: input.id, runid: input.runid, stepid: input.stepid, task, payload: input.payload, transferables: transferablekeys(input.options ?? {}), sentat: input.sentat };
5503
+ }
5504
+ function transferablekeys(options) {
5505
+ return Object.keys(options).filter((key) => options[key] instanceof ArrayBuffer);
5506
+ }
5507
+ function workerresponseof(input) {
5508
+ if (input.summary.trim() === "") throw new Error("The worker answer needs its summary in plain language.");
5509
+ 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 };
5510
+ }
5511
+ function acceptworkerresponse(response) {
5512
+ if (!response.ok) return { done: true, partial: false, reason: `The worker refused the request ${response.requestid}: ${response.summary}` };
5513
+ 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.` };
5514
+ return { done: true, partial: false, ...response.result !== void 0 ? { result: response.result } : {}, reason: `The request ${response.requestid} completed inside the offscreen worker pool.` };
5515
+ }
5516
+ function poolplan(input) {
5517
+ if (input.size !== void 0) {
5518
+ 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." };
5519
+ const target = input.size;
5520
+ 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.` };
5521
+ 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.` };
5522
+ return { workers: target, added: 0, retired: 0, reason: `The pool holds the ${target} workers the user configured.` };
5523
+ }
5524
+ 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.` };
5525
+ 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"}.` };
5526
+ return { workers: input.current, added: 0, retired: 0, reason: `The ${input.current} workers match the ${input.pending} pending parses; the pool stays unchanged.` };
5527
+ }
5528
+ function openoffscreen(input) {
5529
+ if (input.document.trim() === "") throw new Error("The offscreen document needs its user configured path.");
5530
+ if (input.reasons.length === 0) throw new Error("The offscreen document needs the reasons the user reviewed.");
5531
+ if (input.justification.trim() === "") throw new Error("The offscreen document needs its justification in plain language.");
5532
+ const open = input.registry.find((entry2) => entry2.runid === input.runid && entry2.closedat === void 0);
5533
+ if (open) return { registry: input.registry, entry: open, reused: true };
5534
+ const entry = { document: input.document, runid: input.runid, reasons: [...input.reasons], justification: input.justification, createdat: input.now };
5535
+ return { registry: [entry, ...input.registry], entry, reused: false };
5536
+ }
5537
+ function closeoffscreen(registry, runid, now) {
5538
+ const open = registry.find((entry) => entry.runid === runid && entry.closedat === void 0);
5539
+ if (!open) return { registry, closed: false };
5540
+ return { registry: registry.map((entry) => entry === open ? { ...entry, closedat: now } : entry), closed: true };
5541
+ }
5542
+ function isolatedinjection(step) {
5543
+ if (step.kind !== "evaluate") throw new Error("The isolated world injection serves the evaluate kind only.");
5544
+ if (!step.value || step.value.trim() === "") throw new Error("The evaluate step needs its reviewed expression.");
5545
+ let args = [];
5546
+ if (step.options) {
5547
+ try {
5548
+ const parsed = JSON.parse(step.options);
5549
+ if (Array.isArray(parsed)) args = parsed.filter((item) => typeof item === "string");
5550
+ } catch {
5551
+ }
5552
+ }
5553
+ return { world: "ISOLATED", code: step.value, args };
5554
+ }
5555
+
4876
5556
  // httpclient.ts
4877
5557
  var httpkinds = ["fetchurl", "parsejson", "parsehtml", "callrest", "callgraphql"];
4878
5558
  var redirectstatuses = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
@@ -9495,6 +10175,48 @@ function blackboardconsentgrade(entry) {
9495
10175
  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.` };
9496
10176
  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.` };
9497
10177
  }
10178
+ function stepenvironmentvalid(step) {
10179
+ 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." };
10180
+ const allowed = environmentsof(step);
10181
+ 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.` };
10182
+ 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.` };
10183
+ }
10184
+ function environmentgrantgate(step, grants) {
10185
+ 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.` };
10186
+ const environment = step.environment ?? defaultenvironment(step);
10187
+ 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.` };
10188
+ return { allowed: true, reason: `The ${environment} environment of the ${step.kind} step sits inside the ${grants.join(", ")} the session granted.` };
10189
+ }
10190
+ function offscreencapabilitygate(input) {
10191
+ if (input.environment !== "offscreenworker") return { allowed: true, reason: `The ${input.environment} environment needs no offscreen capability grant.` };
10192
+ 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." };
10193
+ return { allowed: true, reason: "The offscreen worker pool runs under the user granted offscreen capability." };
10194
+ }
10195
+ function keepalivegate(input) {
10196
+ if (!input.session) return { allowed: false, reason: "The keepalive port opens only inside an active session." };
10197
+ if (input.session.stoppedat !== void 0) return { allowed: false, reason: "The keepalive port stays closed for a stopped session." };
10198
+ if (input.session.pausedat !== void 0) return { allowed: false, reason: "The keepalive port stays closed while the session pauses; a resumed run reattaches it." };
10199
+ if (input.now > input.session.expiresat) return { allowed: false, reason: "The keepalive port stays closed for an expired session." };
10200
+ 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." };
10201
+ return { allowed: true, reason: `The approved plan ${input.plan.id} of the active session holds the keepalive port open for its whole run.` };
10202
+ }
10203
+ function keepaliveintervalvalid(interval) {
10204
+ if (!Number.isFinite(interval) || interval <= 0) return { allowed: false, reason: "The keepalive heartbeat interval stays a positive user value in milliseconds." };
10205
+ 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.` };
10206
+ }
10207
+ function workerpoolsizevalid(size) {
10208
+ 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." };
10209
+ 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." };
10210
+ return { allowed: true, reason: `The worker pool size ${size} stays the user configured value; no engine cap exists.` };
10211
+ }
10212
+ function sandboxorigingate(input) {
10213
+ if (input.origin.trim() === "") return { allowed: false, reason: "The sandbox render needs the source origin of its untrusted markup." };
10214
+ 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(", ")}.` };
10215
+ 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.` };
10216
+ }
10217
+ function environmentrequirements() {
10218
+ return environmentrequirementsof([...allowedactions]);
10219
+ }
9498
10220
 
9499
10221
  // llm.ts
9500
10222
  var defaultrefusalmarkers = ["i cannot", "i can't", "i'm unable", "refusal:", "cannot comply"];
@@ -9855,7 +10577,7 @@ function budgetcheck(input) {
9855
10577
  }
9856
10578
 
9857
10579
  // version.ts
9858
- var packageversion = "1.1.58";
10580
+ var packageversion = "1.1.60";
9859
10581
 
9860
10582
  // types.ts
9861
10583
  var protocolversion = packageversion;
@@ -10495,6 +11217,175 @@ function bumprevision(route, now) {
10495
11217
  return { ...route, revision: route.revision + 1, updatedat: now };
10496
11218
  }
10497
11219
 
11220
+ // orchestration.ts
11221
+ function electleader(input) {
11222
+ const live = input.agents.filter((agent) => agent.state !== "stopped");
11223
+ if (live.length === 0) throw new Error("The swarm holds no live agent; the leader election waits for the user to register one.");
11224
+ const rule = input.rule ?? { kind: "first" };
11225
+ let leader;
11226
+ if (rule.kind === "named") {
11227
+ if (rule.agentid === void 0 || rule.agentid.trim() === "") throw new Error("The named election rule needs the agent id the user named.");
11228
+ leader = live.find((agent) => agent.id === rule.agentid);
11229
+ if (!leader) throw new Error(`The named election rule names the agent ${rule.agentid} which is not a live agent of the swarm.`);
11230
+ } else {
11231
+ leader = live[live.length - 1];
11232
+ }
11233
+ if (!leader) throw new Error("The leader election found no live agent.");
11234
+ const leaderid = leader.id;
11235
+ const workers = live.filter((agent) => agent.id !== leaderid && agent.role === "worker");
11236
+ const critics = live.filter((agent) => agent.id !== leaderid && agent.role === "critic");
11237
+ const verifiers = live.filter((agent) => agent.id !== leaderid && agent.role === "verifier");
11238
+ return {
11239
+ id: input.id,
11240
+ leaderid: leader.id,
11241
+ workerids: workers.map((agent) => agent.id),
11242
+ criticids: critics.map((agent) => agent.id),
11243
+ verifierids: verifiers.map((agent) => agent.id),
11244
+ assignments: [],
11245
+ rule: { kind: rule.kind, ...rule.agentid !== void 0 ? { agentid: rule.agentid } : {} },
11246
+ electedat: input.now
11247
+ };
11248
+ }
11249
+ function assignwork(input) {
11250
+ if (input.topology.workerids.length === 0) throw new Error("The topology holds no worker; the user adds workers before the assignment.");
11251
+ const assignments = [];
11252
+ const tasks = input.tasks.filter((task) => task.state === "queued" || task.state === "claimed");
11253
+ tasks.forEach((task, index) => {
11254
+ const workerid = input.topology.workerids[index % input.topology.workerids.length];
11255
+ assignments.push({ workerid, taskid: task.id, slice: `${task.payload} (slice ${Math.floor(index / input.topology.workerids.length) + 1} of lane ${task.lane})`, assignedat: input.now });
11256
+ });
11257
+ return { ...input.topology, assignments };
11258
+ }
11259
+ function collectresults(input) {
11260
+ const gathered = input.topology.assignments.map((assignment) => {
11261
+ const output = input.outputs.find((entry) => entry.taskid === assignment.taskid && entry.workerid === assignment.workerid);
11262
+ return output ?? { workerid: assignment.workerid, taskid: assignment.taskid, state: "pending", summary: `The worker ${assignment.workerid} has not returned its slice of the task ${assignment.taskid} yet.` };
11263
+ });
11264
+ return { gathered, missing: gathered.filter((entry) => entry.state === "pending").map((entry) => `${entry.workerid}:${entry.taskid}`) };
11265
+ }
11266
+ function scaleworkers(input) {
11267
+ const live = input.agents.filter((agent) => agent.state === "active" && agent.role === "worker" && agent.id !== input.topology.leaderid);
11268
+ const current = input.topology.workerids.filter((workerid) => live.some((agent) => agent.id === workerid));
11269
+ const ceiling = input.bound;
11270
+ if (input.pending > current.length) {
11271
+ const available = live.filter((agent) => !current.includes(agent.id)).map((agent) => agent.id);
11272
+ const wanted = input.pending - current.length;
11273
+ const addable = ceiling === void 0 ? available.slice(0, wanted) : available.slice(0, Math.min(wanted, Math.max(ceiling - current.length, 0)));
11274
+ if (addable.length === 0) return { topology: input.topology, added: [], retired: [], reason: ceiling === void 0 ? `The load of ${input.pending} pending slices exceeds the ${current.length} workers but the swarm holds no further live worker role agent to add.` : `The load of ${input.pending} pending slices exceeds the ${current.length} workers but the user configured bound of ${ceiling} workers holds.` };
11275
+ return { topology: { ...input.topology, workerids: [...current, ...addable], electedat: input.topology.electedat, assignments: input.topology.assignments }, added: addable, retired: [], reason: `The load of ${input.pending} pending slices added the workers ${addable.join(", ")}; ${ceiling === void 0 ? "no bound is configured so the user scale stands alone" : `the user configured bound of ${ceiling} workers holds`}.` };
11276
+ }
11277
+ const keep = Math.max(input.pending, 0);
11278
+ if (current.length > keep) {
11279
+ const retired = current.slice(keep);
11280
+ return { topology: { ...input.topology, workerids: current.slice(0, keep), electedat: input.topology.electedat, assignments: input.topology.assignments.filter((assignment) => !retired.includes(assignment.workerid)) }, added: [], retired, reason: `The load of ${input.pending} pending slices retired the idle workers ${retired.join(", ")}.` };
11281
+ }
11282
+ return { topology: input.topology, added: [], retired: [], reason: `The load of ${input.pending} pending slices matches the ${current.length} workers; the scale stays unchanged.` };
11283
+ }
11284
+ function plannersplit(input) {
11285
+ if (input.planownerid.trim() === "" || input.runownerid.trim() === "") throw new Error("The planner executor split needs its plan owner and run owner agent ids.");
11286
+ if (input.planownerid === input.runownerid) throw new Error("The planner executor split keeps plan drafting and execution in different agents; one agent holds both sides never.");
11287
+ return { id: input.id, planownerid: input.planownerid, runownerid: input.runownerid, ...input.taskid !== void 0 && input.taskid.trim() !== "" ? { taskid: input.taskid } : {}, stepreports: [], splitat: input.now };
11288
+ }
11289
+ function reportstep(input) {
11290
+ if (input.stepid.trim() === "") throw new Error("The executor report needs its step id.");
11291
+ if (input.detail.trim() === "") throw new Error("The executor report needs its detail in plain language.");
11292
+ const report = { stepid: input.stepid.trim(), outcome: input.outcome, detail: input.detail, reportedat: input.now };
11293
+ return { ...input.split, stepreports: [...input.split.stepreports.filter((entry) => entry.stepid !== report.stepid), report] };
11294
+ }
11295
+ function requestreview(input) {
11296
+ if (input.subject.trim() === "") throw new Error("The review request needs its subject.");
11297
+ if (input.payload.trim() === "") throw new Error("The review request needs its payload.");
11298
+ if (input.toagentid.trim() === "" || input.toagentid === input.fromagentid) throw new Error("The review request names another reviewing agent, never its own requester.");
11299
+ const request = { id: input.id, fromagentid: input.fromagentid, toagentid: input.toagentid, subject: input.subject, payload: input.payload, state: "open", requestedat: input.now, ...input.timeoutms !== void 0 ? { timeoutat: input.now + input.timeoutms } : {} };
11300
+ return [request, ...input.requests];
11301
+ }
11302
+ function ackreview(input) {
11303
+ const request = input.requests.find((entry) => entry.id === input.id);
11304
+ if (!request) throw new Error(`The review request ${input.id} does not exist.`);
11305
+ if (request.state !== "open") throw new Error(`The review request ${input.id} is ${request.state}; only an open request receives its ack.`);
11306
+ return input.requests.map((entry) => entry.id === input.id ? { ...entry, state: "acked", ackedat: input.now } : entry);
11307
+ }
11308
+ function applyreview(input) {
11309
+ const request = input.requests.find((entry) => entry.id === input.id);
11310
+ if (!request) throw new Error(`The review request ${input.id} does not exist.`);
11311
+ if (request.state === "answered" || request.state === "timeout") throw new Error(`The review request ${input.id} is ${request.state}; an answered or timed out request never reviews again.`);
11312
+ if (request.toagentid !== input.reviewerid) throw new Error(`The review request ${input.id} routes to the agent ${request.toagentid}; the agent ${input.reviewerid} never answers in its place.`);
11313
+ if (input.verdict === "changes" && input.requiredchanges.length === 0) throw new Error("A changes verdict needs its required changes in plain language.");
11314
+ const review = { id: request.id, reviewerid: input.reviewerid, subjectagentid: request.fromagentid, ...input.taskid !== void 0 && input.taskid.trim() !== "" ? { taskid: input.taskid } : {}, verdict: input.verdict, issues: input.issues, requiredchanges: input.requiredchanges, reviewedat: input.now };
11315
+ return { review, requests: input.requests.map((entry) => entry.id === input.id ? { ...entry, state: "answered", answeredat: input.now } : entry) };
11316
+ }
11317
+ function sweepreviews(input) {
11318
+ const expired = input.requests.filter((request) => request.state === "open" || request.state === "acked").filter((request) => request.timeoutat !== void 0 && input.now > request.timeoutat);
11319
+ if (expired.length === 0) return { requests: input.requests, timedout: [] };
11320
+ const ids = new Set(expired.map((request) => request.id));
11321
+ return { requests: input.requests.map((request) => ids.has(request.id) ? { ...request, state: "timeout" } : request), timedout: [...ids] };
11322
+ }
11323
+ function checkclaim(input) {
11324
+ if (input.claim.trim() === "") throw new Error("The verifier check needs its claim in plain language.");
11325
+ if (input.method.trim() === "") throw new Error("The verifier check needs the method the user configured.");
11326
+ if (input.claimagentid.trim() === "") throw new Error("The verifier check names the agent whose claim it checks.");
11327
+ return { id: input.id, verifierid: input.verifierid, claimagentid: input.claimagentid, ...input.taskid !== void 0 && input.taskid.trim() !== "" ? { taskid: input.taskid } : {}, claim: input.claim, method: input.method, outcome: input.outcome, ...input.evidence !== void 0 && input.evidence.trim() !== "" ? { evidence: input.evidence } : {}, checkedat: input.now };
11328
+ }
11329
+ function boardstate(input) {
11330
+ const lanes = input.agents.filter((agent) => agent.state !== "stopped").map((agent) => {
11331
+ const assignment = input.topology?.assignments.find((entry) => entry.workerid === agent.id);
11332
+ const claim2 = input.queue.claims.find((record2) => record2.agentid === agent.id && input.queue.items.some((item) => item.id === record2.taskid && item.state === "claimed"));
11333
+ const task = claim2 !== void 0 ? input.queue.items.find((item) => item.id === claim2.taskid) : void 0;
11334
+ const lane = task?.lane ?? (agent.role === "critic" || agent.role === "verifier" ? agent.role : agent.role === "planner" ? "planning" : "idle");
11335
+ return { agentid: agent.id, name: agent.name, role: agent.role, state: agent.state, lane, ...task !== void 0 ? { currenttask: task.payload } : assignment !== void 0 ? { currenttask: assignment.slice } : {}, milestones: input.milestones?.[agent.id] ?? [] };
11336
+ });
11337
+ return { id: `board:${input.now}`, lanes, builtat: input.now };
11338
+ }
11339
+ function escalate(input) {
11340
+ if (input.subject.trim() === "") throw new Error("The escalation needs its subject.");
11341
+ if (input.context.trim() === "") throw new Error("The escalation needs its full context in plain language; the user decides on what the agent saw.");
11342
+ if (input.agentid.trim() === "") throw new Error("The escalation names the agent whose decision it lifts.");
11343
+ return { id: input.id, agentid: input.agentid, subject: input.subject, context: input.context, state: "open", raisedat: input.now };
11344
+ }
11345
+ function resolveescalation(input) {
11346
+ if (input.escalation.state === "decided") throw new Error("The escalation already carries its user decision.");
11347
+ if (input.decision.trim() === "") throw new Error("The escalation decision needs the words the user wrote.");
11348
+ return { ...input.escalation, state: "decided", decision: input.decision, decidedat: input.now };
11349
+ }
11350
+ function arbitrate(input) {
11351
+ if (input.claims.length === 0) return [];
11352
+ if (input.rule.strategy === "priority") {
11353
+ const order = input.rule.priorityorder;
11354
+ return [...input.claims].sort((one, two) => {
11355
+ const oneindex = order.indexOf(one.agentid);
11356
+ const twoindex = order.indexOf(two.agentid);
11357
+ return (oneindex === -1 ? order.length : oneindex) - (twoindex === -1 ? order.length : twoindex) || one.claimedat - two.claimedat;
11358
+ }).map((claim2) => claim2.agentid);
11359
+ }
11360
+ if (input.rule.strategy === "age") return [...input.claims].sort((one, two) => one.claimedat - two.claimedat || (one.agentid < two.agentid ? -1 : 1)).map((claim2) => claim2.agentid);
11361
+ if (input.leaderid === void 0) throw new Error("The leader arbitration strategy needs the elected leader of the topology.");
11362
+ return [...input.claims].sort((one, two) => (one.agentid === input.leaderid ? -1 : 1) - (two.agentid === input.leaderid ? -1 : 1) || one.claimedat - two.claimedat).map((claim2) => claim2.agentid);
11363
+ }
11364
+ function openconsensus(input) {
11365
+ if (input.subject.trim() === "") throw new Error("The consensus round needs its subject in plain language.");
11366
+ if (!Number.isInteger(input.quorum) || input.quorum < 1) throw new Error("The consensus round needs its quorum as a positive whole number the user configured.");
11367
+ return { id: input.id, subject: input.subject, votes: [], quorum: input.quorum, state: "open", openedat: input.now };
11368
+ }
11369
+ function castvote(input) {
11370
+ if (input.round.state !== "open") throw new Error(`The consensus round ${input.round.id} is ${input.round.state}; a closed round collects no vote.`);
11371
+ if (input.round.votes.some((entry) => entry.agentid === input.agentid)) throw new Error(`The agent ${input.agentid} already voted in the round ${input.round.id}.`);
11372
+ const votes = [...input.round.votes, { agentid: input.agentid, vote: input.vote, votedat: input.now }];
11373
+ const yes = votes.filter((entry) => entry.vote === "yes").length;
11374
+ const no = votes.filter((entry) => entry.vote === "no").length;
11375
+ if (yes >= input.round.quorum) return { ...input.round, votes, state: "carried", closedat: input.now };
11376
+ if (no >= input.round.quorum) return { ...input.round, votes, state: "failed", closedat: input.now };
11377
+ return { ...input.round, votes };
11378
+ }
11379
+ function consensusstate(round) {
11380
+ return {
11381
+ yes: round.votes.filter((entry) => entry.vote === "yes").length,
11382
+ no: round.votes.filter((entry) => entry.vote === "no").length,
11383
+ abstain: round.votes.filter((entry) => entry.vote === "abstain").length,
11384
+ quorum: round.quorum,
11385
+ state: round.state
11386
+ };
11387
+ }
11388
+
10498
11389
  // promptlibrary.ts
10499
11390
  function templatevariables(body) {
10500
11391
  const names = [];
@@ -10537,6 +11428,44 @@ function removetemplate(templates, name) {
10537
11428
  return templates.filter((template) => template.name !== name);
10538
11429
  }
10539
11430
 
11431
+ // sandboxframe.ts
11432
+ function stripscripts(markup) {
11433
+ 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();
11434
+ }
11435
+ function nonceof(seed) {
11436
+ const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
11437
+ let hash = 0;
11438
+ for (let index = 0; index < seed.length; index += 1) hash = hash * 31 + seed.charCodeAt(index) >>> 0;
11439
+ let nonce = "";
11440
+ let state = hash === 0 ? 2654435769 : hash;
11441
+ for (let index = 0; index < 16; index += 1) {
11442
+ state = state * 1664525 + 1013904223 >>> 0;
11443
+ nonce += alphabet[state % alphabet.length];
11444
+ }
11445
+ return nonce;
11446
+ }
11447
+ function sandboxrenderof(input) {
11448
+ if (input.markup.trim() === "") throw new Error("The sandbox render needs its untrusted markup.");
11449
+ if (input.sourceorigin.trim() === "") throw new Error("The sandbox render needs the source origin of its untrusted markup.");
11450
+ if (input.stepid.trim() === "") throw new Error("The sandbox render names the reviewed step it renders for.");
11451
+ return { id: input.id, nonce: nonceof(`${input.id}:${input.now}`), markup: stripscripts(input.markup), sourceorigin: input.sourceorigin, stepid: input.stepid, renderedat: input.now };
11452
+ }
11453
+ function rendermessage(render) {
11454
+ return { channel: "devthinksandbox", type: "render", nonce: render.nonce, markup: render.markup };
11455
+ }
11456
+ function acceptrenderresult(input) {
11457
+ if (input.message.channel !== "devthinksandbox") return { accepted: false, reason: "The sandbox message travels the devthinksandbox channel only." };
11458
+ if (input.message.type !== "renderresult") return { accepted: false, reason: "The sandbox message answers with the renderresult type only." };
11459
+ const render = input.renders.find((entry) => entry.nonce === input.message.nonce && entry.renderedat <= input.now);
11460
+ if (!render) return { accepted: false, reason: "The sandbox message carries no nonce of a known render; a stale or replayed message never passes." };
11461
+ const text2 = (input.message.text ?? "").replace(/<[^>]*>/g, "");
11462
+ 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 };
11463
+ 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.` };
11464
+ }
11465
+ function renderprovenance(render) {
11466
+ return { origin: render.sourceorigin, stepid: render.stepid, environment: "sandboxframe" };
11467
+ }
11468
+
10540
11469
  // taskqueue.ts
10541
11470
  function emptyqueue(input = {}) {
10542
11471
  return { lanes: input.lanes ?? [], priorities: input.priorities ?? [], completionpolicy: input.completionpolicy ?? "all", items: [], claims: [] };
@@ -11367,6 +12296,70 @@ function swarmstatereport(state) {
11367
12296
  function agenteventframe(event) {
11368
12297
  return { jsonrpc: "2.0", method: "agents/notify", params: { eventid: event.id, kind: event.kind, ...event.agentid !== void 0 ? { agentid: event.agentid } : {}, ...event.taskid !== void 0 ? { taskid: event.taskid } : {}, summary: event.summary, at: event.at } };
11369
12298
  }
12299
+ function boardstatesnapshot(board) {
12300
+ return {
12301
+ version: protocolversion,
12302
+ board: {
12303
+ id: board.id,
12304
+ builtat: board.builtat,
12305
+ lanes: board.lanes.map((lane) => ({ agentid: lane.agentid, name: lane.name, role: lane.role, state: lane.state, lane: lane.lane, ...lane.currenttask !== void 0 ? { currenttask: lane.currenttask } : {}, milestones: lane.milestones.map((milestone) => ({ label: milestone.label, done: milestone.done, ...milestone.at !== void 0 ? { at: milestone.at } : {} })) }))
12306
+ }
12307
+ };
12308
+ }
12309
+ function handoffframe(record2) {
12310
+ return {
12311
+ jsonrpc: "2.0",
12312
+ method: "agents/handoff",
12313
+ params: {
12314
+ id: record2.id,
12315
+ from: record2.fromagentid,
12316
+ to: record2.toagentid,
12317
+ ...record2.tabid !== void 0 ? { tabid: record2.tabid } : {},
12318
+ taskstate: record2.taskstate,
12319
+ state: record2.state,
12320
+ ...record2.transferredat !== void 0 ? { transferredat: record2.transferredat } : {},
12321
+ ...record2.resumedat !== void 0 ? { resumedat: record2.resumedat } : {}
12322
+ }
12323
+ };
12324
+ }
12325
+ function reviewframe(input) {
12326
+ return {
12327
+ jsonrpc: "2.0",
12328
+ method: "agents/review",
12329
+ params: {
12330
+ id: input.request.id,
12331
+ from: input.request.fromagentid,
12332
+ to: input.request.toagentid,
12333
+ subject: input.request.subject,
12334
+ state: input.request.state,
12335
+ ...input.request.ackedat !== void 0 ? { ackedat: input.request.ackedat } : {},
12336
+ ...input.request.answeredat !== void 0 ? { answeredat: input.request.answeredat } : {},
12337
+ ...input.review !== void 0 ? { verdict: input.review.verdict, issues: input.review.issues, requiredchanges: input.review.requiredchanges } : {}
12338
+ }
12339
+ };
12340
+ }
12341
+ function environmentgrammar() {
12342
+ return {
12343
+ version: protocolversion,
12344
+ kinds: environmentrequirements(),
12345
+ notes: [
12346
+ "The evaluate kind runs inside the isolated world only where page globals stay unreachable from step code.",
12347
+ "A step whose reviewed options carry untrusted markup renders inside the sandboxframe only, with scripts and event handlers stripped before the render.",
12348
+ "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.",
12349
+ "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."
12350
+ ]
12351
+ };
12352
+ }
12353
+ function environmentreport(input) {
12354
+ return {
12355
+ version: protocolversion,
12356
+ environments: Object.entries(input.environments).map(([stepid, environment]) => ({ stepid, environment })),
12357
+ turnarounds: Object.entries(input.turnarounds ?? {}).map(([stepid, milliseconds]) => ({ stepid, milliseconds })),
12358
+ offscreen: input.offscreen ?? [],
12359
+ workers: input.workers ?? 0,
12360
+ ...input.keepalive !== void 0 ? { keepalive: input.keepalive } : {}
12361
+ };
12362
+ }
11370
12363
 
11371
12364
  // workfloweditor.ts
11372
12365
  var palettecategories = ["actions", "controlflow", "waits", "variables", "triggers"];
@@ -12072,6 +13065,11 @@ function yamlscalarvalue(text2) {
12072
13065
  return text2;
12073
13066
  }
12074
13067
  export {
13068
+ acceptrenderresult,
13069
+ acceptworkerresponse,
13070
+ ackreview,
13071
+ acquirelock,
13072
+ acquirerunlock,
12075
13073
  activelayers,
12076
13074
  addedge,
12077
13075
  addnode,
@@ -12099,14 +13097,17 @@ export {
12099
13097
  applylayer,
12100
13098
  applyoverride,
12101
13099
  applyretry,
13100
+ applyreview,
12102
13101
  applyruntimeout,
12103
13102
  applytimeout,
12104
13103
  approvalframes,
12105
13104
  approvalprompt,
13105
+ arbitrate,
12106
13106
  argkind,
12107
13107
  armrule,
12108
13108
  assetentries,
12109
13109
  assignrole,
13110
+ assignwork,
12110
13111
  attachcdpsession,
12111
13112
  attachtargetof,
12112
13113
  attachtimeline,
@@ -12117,6 +13118,7 @@ export {
12117
13118
  autointervalof,
12118
13119
  backoffdelay,
12119
13120
  batchreport,
13121
+ beatrun,
12120
13122
  bindlocalhost,
12121
13123
  bindparam,
12122
13124
  bindvariables,
@@ -12130,6 +13132,8 @@ export {
12130
13132
  blockingduration,
12131
13133
  blockinvocationof,
12132
13134
  blockruleof,
13135
+ boardstate,
13136
+ boardstatesnapshot,
12133
13137
  boardsummary,
12134
13138
  bodyfilterof,
12135
13139
  bodymatches,
@@ -12174,6 +13178,7 @@ export {
12174
13178
  capturestitched,
12175
13179
  capturetargets,
12176
13180
  capturevisible,
13181
+ castvote,
12177
13182
  cdpallowlistof,
12178
13183
  cdpdomains,
12179
13184
  cdpeventruleof,
@@ -12183,19 +13188,25 @@ export {
12183
13188
  channeloptionsof,
12184
13189
  channelorigin,
12185
13190
  checkallowlist,
13191
+ checkclaim,
12186
13192
  choosebranch,
12187
13193
  claim,
12188
13194
  claimheartbeat,
12189
13195
  classifyintent,
12190
13196
  closechannel,
12191
13197
  closeidlechannels,
13198
+ closeoffscreen,
13199
+ closerun,
12192
13200
  collectmessages,
13201
+ collectresults,
12193
13202
  commandguard,
13203
+ compareoutputs,
12194
13204
  complete,
12195
13205
  composeworkflow,
12196
13206
  conditionof,
12197
13207
  confirmmanualrun,
12198
13208
  connectclient,
13209
+ consensusstate,
12199
13210
  consolecapture,
12200
13211
  consoleconsentcovers,
12201
13212
  consolediff,
@@ -12227,6 +13238,7 @@ export {
12227
13238
  dedupeimages,
12228
13239
  defaultapprovalwindowms,
12229
13240
  defaultchallengelifetimems,
13241
+ defaultenvironment,
12230
13242
  defaultheartbeatms,
12231
13243
  defaulthttpstream,
12232
13244
  defaultidlewindowms,
@@ -12259,6 +13271,7 @@ export {
12259
13271
  editorstate,
12260
13272
  editstep,
12261
13273
  egressconsentgate,
13274
+ electleader,
12262
13275
  emptyboard,
12263
13276
  emptyqueue,
12264
13277
  emugate,
@@ -12271,23 +13284,34 @@ export {
12271
13284
  enqueue,
12272
13285
  enqueuerequest,
12273
13286
  entryfresh,
13287
+ environmentgrammar,
13288
+ environmentgrantgate,
13289
+ environmentreport,
13290
+ environmentrequirements,
13291
+ environmentrequirementsof,
13292
+ environmentsof,
12274
13293
  errorcapture,
12275
13294
  errorreportresponse,
13295
+ escalate,
12276
13296
  evaluatecondition,
12277
13297
  evaluatetrigger,
12278
13298
  eventnotification,
12279
13299
  eventresponse,
12280
13300
  eventrulematches,
12281
13301
  exchangesreport,
13302
+ executorregistry,
12282
13303
  expandblocks,
12283
13304
  expandtemplate,
12284
13305
  expireapprovals,
12285
13306
  expirelayers,
13307
+ expirelocks,
12286
13308
  expireprofilerecords,
13309
+ expirerunlocks,
12287
13310
  expiresessions,
12288
13311
  expiretokens,
12289
13312
  exportcontentreview,
12290
13313
  exportpresetlibrary,
13314
+ exportrunstate,
12291
13315
  exportsessionfile,
12292
13316
  exportworkflow,
12293
13317
  expressioneval,
@@ -12322,6 +13346,7 @@ export {
12322
13346
  guardoutput,
12323
13347
  guardverdictgate,
12324
13348
  handleframe,
13349
+ handoffframe,
12325
13350
  headerfilterof,
12326
13351
  headeruleof,
12327
13352
  heapintervalallowed,
@@ -12346,6 +13371,7 @@ export {
12346
13371
  inflightreport,
12347
13372
  inheritconsent,
12348
13373
  initialize,
13374
+ interleavetimeline,
12349
13375
  iscdpkind,
12350
13376
  iscontrolflowkind,
12351
13377
  iscontrolkind,
@@ -12354,6 +13380,7 @@ export {
12354
13380
  isformkind,
12355
13381
  islocalorigin,
12356
13382
  isnetwatchkind,
13383
+ isolatedinjection,
12357
13384
  isprofilekind,
12358
13385
  issessionkind,
12359
13386
  issocketkind,
@@ -12366,6 +13393,8 @@ export {
12366
13393
  isworkflowkind,
12367
13394
  joinbranches,
12368
13395
  jsonpathrulesof,
13396
+ keepalivegate,
13397
+ keepaliveintervalvalid,
12369
13398
  killall,
12370
13399
  killswitchgate,
12371
13400
  lanereport,
@@ -12387,6 +13416,7 @@ export {
12387
13416
  locationconsentgate,
12388
13417
  locationpresetof,
12389
13418
  locationrangevalid,
13419
+ lockkey,
12390
13420
  loglevels,
12391
13421
  longtaskcapture,
12392
13422
  loopof,
@@ -12396,7 +13426,9 @@ export {
12396
13426
  mapresponse,
12397
13427
  mapurlof,
12398
13428
  markbreakpoint,
13429
+ markpending,
12399
13430
  markprovider,
13431
+ markuprenderstep,
12400
13432
  matchmessage,
12401
13433
  matchurl,
12402
13434
  matchurlpattern,
@@ -12404,6 +13436,7 @@ export {
12404
13436
  mediaentries,
12405
13437
  mediakinds,
12406
13438
  mediareport,
13439
+ mergeresults,
12407
13440
  messageegressgrade,
12408
13441
  messagefilterof,
12409
13442
  methoddomain,
@@ -12434,12 +13467,20 @@ export {
12434
13467
  newsessionrecord,
12435
13468
  newworkflowrun,
12436
13469
  nextrequest,
13470
+ nonceof,
12437
13471
  normalizeendpoint,
12438
13472
  oauthflowof,
12439
13473
  observationmodeof,
12440
13474
  observationresponse,
12441
13475
  observeevents,
13476
+ offfamilyof,
13477
+ offloadkinds,
13478
+ offscreencapabilitygate,
12442
13479
  openchannel,
13480
+ openconsensus,
13481
+ openoffscreen,
13482
+ openrun,
13483
+ openseal,
12443
13484
  openstreamchannel,
12444
13485
  opentabagent,
12445
13486
  outcomeresponse,
@@ -12485,11 +13526,14 @@ export {
12485
13526
  planallowlist,
12486
13527
  plandraftreviewgate,
12487
13528
  planlint,
13529
+ plannersplit,
12488
13530
  pollcursorof,
12489
13531
  polldecision,
12490
13532
  pollurl,
13533
+ poolplan,
12491
13534
  popscope,
12492
13535
  postentry,
13536
+ preparehandoff,
12493
13537
  privatemime,
12494
13538
  profilegrantgranted,
12495
13539
  profilereport,
@@ -12504,6 +13548,7 @@ export {
12504
13548
  providervalid,
12505
13549
  proxygate,
12506
13550
  proxyrouteof,
13551
+ prunerunstates,
12507
13552
  publishmessage,
12508
13553
  pushscope,
12509
13554
  quarantinereport,
@@ -12519,12 +13564,17 @@ export {
12519
13564
  readentries,
12520
13565
  readpath,
12521
13566
  readstream,
13567
+ reattachrun,
12522
13568
  receivemessage,
12523
13569
  receivemessages,
12524
13570
  reconnectwaits,
12525
13571
  recordagentusage,
13572
+ recordenvironment,
12526
13573
  recordingoptionsof,
13574
+ recordturnaround,
13575
+ recordurl,
12527
13576
  recordwatchvalue,
13577
+ recoveryplan,
12528
13578
  redactconsoletext,
12529
13579
  redactedcookies,
12530
13580
  redactparams,
@@ -12538,24 +13588,32 @@ export {
12538
13588
  registeragent,
12539
13589
  rejectioncapture,
12540
13590
  relayframe,
13591
+ releaselock,
13592
+ releaserunlock,
12541
13593
  removeedge,
12542
13594
  removenode,
12543
13595
  removetemplate,
13596
+ rendermessage,
12544
13597
  renderminimap,
13598
+ renderprovenance,
12545
13599
  rendertemplate,
12546
13600
  rendertoolbriefs,
12547
13601
  reordersteps,
12548
13602
  repeatuntilof,
12549
13603
  replannonfail,
12550
13604
  replanreviewgate,
13605
+ replayagentrun,
12551
13606
  replaytrace,
12552
13607
  replayurl,
13608
+ reportstep,
12553
13609
  requestbody,
13610
+ requestreview,
12554
13611
  requeue,
12555
13612
  requireapproval,
12556
13613
  resolutionverdict,
12557
13614
  resolveapproval,
12558
13615
  resolvedrisk,
13616
+ resolveescalation,
12559
13617
  resolverecipients,
12560
13618
  resolveroute,
12561
13619
  resolvetool,
@@ -12568,6 +13626,7 @@ export {
12568
13626
  restoreplanof,
12569
13627
  restorereviewgranted,
12570
13628
  resumeall,
13629
+ resumehandoff,
12571
13630
  resumeone,
12572
13631
  retireentries,
12573
13632
  retireentry,
@@ -12577,6 +13636,7 @@ export {
12577
13636
  revertplanof,
12578
13637
  revertrule,
12579
13638
  reviewedkinds,
13639
+ reviewframe,
12580
13640
  revocationruleof,
12581
13641
  revokeclient,
12582
13642
  rewritesourcelocation,
@@ -12584,6 +13644,7 @@ export {
12584
13644
  roledefaults,
12585
13645
  rotatelogs,
12586
13646
  rotationruleof,
13647
+ routeenvironment,
12587
13648
  routesfor,
12588
13649
  routevalid,
12589
13650
  rpcerrorcodeof,
@@ -12608,13 +13669,18 @@ export {
12608
13669
  runworkflow,
12609
13670
  safetyresponse,
12610
13671
  samplingframes,
13672
+ sandboxorigingate,
13673
+ sandboxrenderof,
12611
13674
  savetemplate,
12612
13675
  saveworkflow,
12613
13676
  scaledrect,
13677
+ scaleworkers,
13678
+ scanconflicts,
12614
13679
  schedulecron,
12615
13680
  scheduleinterval,
12616
13681
  scopecheck,
12617
13682
  scopegate,
13683
+ sealrunstate,
12618
13684
  seamweights,
12619
13685
  searchfields,
12620
13686
  searchqueryof,
@@ -12630,6 +13696,7 @@ export {
12630
13696
  serializearg,
12631
13697
  serializecdpcommand,
12632
13698
  serializeframe,
13699
+ serializesteps,
12633
13700
  serverbindgate,
12634
13701
  servercapabilities,
12635
13702
  serverenablementgate,
@@ -12644,6 +13711,7 @@ export {
12644
13711
  sessionrestoregate,
12645
13712
  sessiontabof,
12646
13713
  setvariable,
13714
+ sharelesson,
12647
13715
  shareworkflow,
12648
13716
  shiftentryof,
12649
13717
  signalsreport,
@@ -12665,6 +13733,7 @@ export {
12665
13733
  starttls,
12666
13734
  statusclassof,
12667
13735
  steal,
13736
+ stepenvironmentvalid,
12668
13737
  stepmodeof,
12669
13738
  steptemplateof,
12670
13739
  stepwindows,
@@ -12675,13 +13744,17 @@ export {
12675
13744
  streamsummaries,
12676
13745
  streamwindowof,
12677
13746
  stripguardrails,
13747
+ stripscripts,
12678
13748
  structurederrorreport,
12679
13749
  submitreviewgranted,
12680
13750
  subscriptionframes,
12681
13751
  subscriptionoptionsof,
13752
+ swarmcosts,
12682
13753
  swarmoverview,
13754
+ swarmreport,
12683
13755
  swarmstateof,
12684
13756
  swarmstatereport,
13757
+ sweepreviews,
12685
13758
  tabreportresponse,
12686
13759
  targetgate,
12687
13760
  taskcounts,
@@ -12726,6 +13799,8 @@ export {
12726
13799
  tracestart,
12727
13800
  tracetofile,
12728
13801
  trailreport,
13802
+ transferablekeys,
13803
+ transferhandoff,
12729
13804
  transformgrammar,
12730
13805
  triggereventcatalog,
12731
13806
  triggerfamilies,
@@ -12771,6 +13846,9 @@ export {
12771
13846
  whileof,
12772
13847
  wireformat,
12773
13848
  wizardreport,
13849
+ workerpoolsizevalid,
13850
+ workerrequestof,
13851
+ workerresponseof,
12774
13852
  workflowblockof,
12775
13853
  workflowfileversion,
12776
13854
  workflowgate,
@@ -12779,6 +13857,7 @@ export {
12779
13857
  workflowreport,
12780
13858
  workflowstepof,
12781
13859
  workstealgrade,
13860
+ zombiesweep,
12782
13861
  zoomcanvas
12783
13862
  };
12784
13863
  //# sourceMappingURL=index.js.map