@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
@@ -1824,6 +1824,8 @@ function watchdogpass(input) {
1824
1824
  function roledefaults(role) {
1825
1825
  if (role === "planner") return { toolnamespaces: ["workflow", "memory", "system"], description: "Planners compose reviewed plans and read memory; they never act on the page themselves." };
1826
1826
  if (role === "observer") return { toolnamespaces: ["memory", "system"], description: "Observers read the shared memory and the system reports only." };
1827
+ 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." };
1828
+ 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." };
1827
1829
  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.` };
1828
1830
  }
1829
1831
  function registeragent(input) {
@@ -1912,6 +1914,170 @@ function swarmoverview(input) {
1912
1914
  };
1913
1915
  }
1914
1916
 
1917
+ // runstate.ts
1918
+ function openrun(input) {
1919
+ if (input.runid.trim() === "" || input.sessionid.trim() === "") throw new Error("The run state needs its run and session ids.");
1920
+ if (!Number.isFinite(input.interval) || input.interval <= 0) throw new Error("The keepalive heartbeat interval stays a positive user value in milliseconds.");
1921
+ return {
1922
+ runid: input.runid,
1923
+ sessionid: input.sessionid,
1924
+ planid: input.planid,
1925
+ profileid: input.profileid,
1926
+ state: "active",
1927
+ urlhistory: [],
1928
+ environments: {},
1929
+ turnarounds: {},
1930
+ 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.` }] },
1931
+ updatedat: input.now
1932
+ };
1933
+ }
1934
+ function beatrun(state, now) {
1935
+ if (state.keepalive.state !== "active") throw new Error(`The run ${state.runid} is ${state.keepalive.state}; a stopped run emits no heartbeat.`);
1936
+ return {
1937
+ ...state,
1938
+ keepalive: { ...state.keepalive, beats: state.keepalive.beats + 1, lastbeatat: now, events: [...state.keepalive.events, { kind: "heartbeat", at: now }].slice(-200) },
1939
+ updatedat: now
1940
+ };
1941
+ }
1942
+ function closerun(state, now) {
1943
+ if (state.keepalive.state === "stopped") throw new Error(`The run ${state.runid} already stopped its keepalive port.`);
1944
+ return {
1945
+ ...state,
1946
+ state: "completed",
1947
+ 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) },
1948
+ updatedat: now
1949
+ };
1950
+ }
1951
+ function reattachrun(state, now) {
1952
+ if (state.keepalive.state !== "active") throw new Error(`The run ${state.runid} is ${state.keepalive.state}; a stopped run never reattaches.`);
1953
+ return {
1954
+ ...state,
1955
+ state: "recovered",
1956
+ 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) },
1957
+ updatedat: now
1958
+ };
1959
+ }
1960
+ function recordurl(state, entry) {
1961
+ if (entry.url.trim() === "") throw new Error("The url history entry needs its url.");
1962
+ const record2 = { url: entry.url, stepid: entry.stepid, at: entry.now };
1963
+ return { ...state, urlhistory: [...state.urlhistory.filter((item) => !(item.url === record2.url && item.stepid === record2.stepid)), record2], updatedat: entry.now };
1964
+ }
1965
+ function recordenvironment(state, input) {
1966
+ if (input.stepid.trim() === "") throw new Error("The environment record needs its step id.");
1967
+ const provenance = { origin: input.origin, stepid: input.stepid, environment: input.environment };
1968
+ return { ...state, environments: { ...state.environments, [input.stepid]: input.environment }, lastprovenance: provenance, updatedat: input.now };
1969
+ }
1970
+ function recordturnaround(state, input) {
1971
+ if (input.stepid.trim() === "") throw new Error("The turnaround record needs its step id.");
1972
+ if (!Number.isFinite(input.milliseconds) || input.milliseconds < 0) throw new Error("The worker turnaround stays a non-negative duration in milliseconds.");
1973
+ return { ...state, turnarounds: { ...state.turnarounds, [input.stepid]: input.milliseconds }, updatedat: input.now };
1974
+ }
1975
+ function markpending(state, stepid, now) {
1976
+ return { ...state, ...stepid !== void 0 && stepid.trim() !== "" ? { pendingstepid: stepid } : {}, updatedat: now };
1977
+ }
1978
+ function recoveryplan(state) {
1979
+ if (state.state === "completed") return { runid: state.runid, recoverable: false, reason: `The run ${state.runid} completed before the restart; nothing resumes.` };
1980
+ 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.` };
1981
+ 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.` };
1982
+ 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.` };
1983
+ }
1984
+ function zombiesweep(input) {
1985
+ if (!Number.isFinite(input.interval) || input.interval <= 0) throw new Error("The zombie sweep needs its heartbeat interval as a positive user value.");
1986
+ if (!Number.isInteger(input.missedlimit) || input.missedlimit < 1) throw new Error("The zombie tolerance stays a positive whole number of silent intervals.");
1987
+ const silentfor = input.interval * input.missedlimit;
1988
+ const zombies = input.states.filter((state) => state.keepalive.state === "active" && input.now - state.keepalive.lastbeatat > silentfor);
1989
+ if (zombies.length === 0) return { states: input.states, reaped: [] };
1990
+ const ids = new Set(zombies.map((state) => state.runid));
1991
+ 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] };
1992
+ }
1993
+ function acquirerunlock(input) {
1994
+ if (input.sessionid.trim() === "" || input.runid.trim() === "") throw new Error("The run lock needs its session and run ids.");
1995
+ const live = input.locks.filter((lock2) => lock2.sessionid === input.sessionid && (lock2.expiresat === void 0 || lock2.expiresat > input.now));
1996
+ const held = live.find((lock2) => lock2.runid !== input.runid);
1997
+ 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.` };
1998
+ const own = input.locks.find((lock2) => lock2.sessionid === input.sessionid && lock2.runid === input.runid);
1999
+ if (own) return { locks: input.locks, acquired: true, reason: `The run ${input.runid} of the session ${input.sessionid} already holds its lock.` };
2000
+ const lock = { sessionid: input.sessionid, runid: input.runid, holder: input.holder, acquiredat: input.now, ...input.expiresat !== void 0 ? { expiresat: input.expiresat } : {} };
2001
+ 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.` };
2002
+ }
2003
+ function releaserunlock(input) {
2004
+ const lock = input.locks.find((entry) => entry.sessionid === input.sessionid);
2005
+ 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}.` };
2006
+ 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}.` };
2007
+ }
2008
+ function expirerunlocks(locks, now) {
2009
+ const expired = locks.filter((lock) => lock.expiresat !== void 0 && now > lock.expiresat);
2010
+ if (expired.length === 0) return { locks, expired: [] };
2011
+ const ids = new Set(expired.map((lock) => lock.sessionid));
2012
+ return { locks: locks.filter((lock) => !ids.has(lock.sessionid)), expired: [...ids] };
2013
+ }
2014
+ function serializesteps(input) {
2015
+ const shared = /* @__PURE__ */ new Set();
2016
+ const counts = /* @__PURE__ */ new Map();
2017
+ for (const branch of input.branches) for (const step of branch.steps) counts.set(step.tabid, (counts.get(step.tabid) ?? 0) + 1);
2018
+ for (const [tabid2, count] of counts) if (count > 1) shared.add(tabid2);
2019
+ const order = [];
2020
+ let cursor = 0;
2021
+ for (const branch of input.branches) {
2022
+ for (const step of branch.steps) {
2023
+ if (shared.has(step.tabid)) {
2024
+ order.push({ branchid: branch.branchid, stepid: step.stepid, tabid: step.tabid, order: cursor });
2025
+ cursor += 1;
2026
+ }
2027
+ }
2028
+ }
2029
+ for (const branch of input.branches) {
2030
+ for (const step of branch.steps) {
2031
+ if (!shared.has(step.tabid)) {
2032
+ order.push({ branchid: branch.branchid, stepid: step.stepid, tabid: step.tabid, order: cursor });
2033
+ cursor += 1;
2034
+ }
2035
+ }
2036
+ }
2037
+ return order;
2038
+ }
2039
+ async function sealrunstate(state) {
2040
+ const payload = JSON.stringify(state);
2041
+ const digest = await sha256(payload);
2042
+ return { payload, algorithm: "sha-256", digest, sealedat: state.updatedat };
2043
+ }
2044
+ async function openseal(sealed) {
2045
+ const digest = await sha256(sealed.payload);
2046
+ if (digest !== sealed.digest) throw new Error("The sealed run state fails its integrity digest; a tampered run state never reaches the recovery.");
2047
+ const parsed = JSON.parse(sealed.payload);
2048
+ if (typeof parsed.runid !== "string" || typeof parsed.sessionid !== "string") throw new Error("The sealed run state carries no run record.");
2049
+ return parsed;
2050
+ }
2051
+ async function sha256(value) {
2052
+ const bytes = new TextEncoder().encode(value);
2053
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
2054
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
2055
+ }
2056
+ function prunerunstates(input) {
2057
+ 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." };
2058
+ 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.` };
2059
+ const removable = input.states.filter((state) => state.state === "completed" || state.state === "reaped").sort((one, two) => one.updatedat - two.updatedat);
2060
+ const states = [...input.states];
2061
+ const pruned = [];
2062
+ for (const candidate of removable) {
2063
+ pruned.push(candidate.runid);
2064
+ const index = states.findIndex((state) => state.runid === candidate.runid);
2065
+ if (index >= 0) states.splice(index, 1);
2066
+ if (pruned.length >= Math.max(1, Math.ceil(input.states.length / 2))) break;
2067
+ }
2068
+ 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.` };
2069
+ }
2070
+ function exportrunstate(states, now) {
2071
+ return {
2072
+ runs: states.length,
2073
+ urls: states.reduce((total, state) => total + state.urlhistory.length, 0),
2074
+ environments: states.reduce((total, state) => total + Object.keys(state.environments).length, 0),
2075
+ offloaded: states.reduce((total, state) => total + Object.values(state.turnarounds).length, 0),
2076
+ beats: states.reduce((total, state) => total + state.keepalive.beats, 0),
2077
+ exportedat: now
2078
+ };
2079
+ }
2080
+
1915
2081
  // memory.ts
1916
2082
  var sessionmemory = class {
1917
2083
  constructor(adapter) {
@@ -4133,6 +4299,244 @@ var sessionmemory = class {
4133
4299
  const mailboxes = await this.getmailboxes();
4134
4300
  return swarmoverview({ agents, queue: queue ?? { lanes: [], priorities: [], completionpolicy: "all", items: [], claims: [] }, mailboxes });
4135
4301
  }
4302
+ /** Returns the leader worker topology of the 1.1.59 swarm with its leader, worker, critic and verifier lanes and its worker assignments. */
4303
+ async gettopology() {
4304
+ return this.adapter.get("swarmtopology");
4305
+ }
4306
+ /** Replaces the stored leader worker topology after one election, assignment, collection or scaling change. */
4307
+ async settopology(topology) {
4308
+ return this.adapter.set("swarmtopology", topology);
4309
+ }
4310
+ /** Returns the stored planner executor splits of the 1.1.59 swarm with their step reports. */
4311
+ async getplannersplits() {
4312
+ return await this.adapter.get("swarmsplits") ?? [];
4313
+ }
4314
+ /** Replaces the stored planner executor splits after one split or one executor step report. */
4315
+ async setplannersplits(splits) {
4316
+ return this.adapter.set("swarmsplits", splits);
4317
+ }
4318
+ /** Records one critic review of an agent output, newest first. */
4319
+ async addcriticreview(review) {
4320
+ await this.adapter.set("swarmreviews", [review, ...await this.adapter.get("swarmreviews") ?? []]);
4321
+ }
4322
+ /** Returns the recorded critic reviews, newest first. */
4323
+ async getcriticreviews() {
4324
+ return await this.adapter.get("swarmreviews") ?? [];
4325
+ }
4326
+ /** Records one verifier check of a result claim, newest first. */
4327
+ async addverifiercheck(check) {
4328
+ await this.adapter.set("swarmverifierchecks", [check, ...await this.adapter.get("swarmverifierchecks") ?? []]);
4329
+ }
4330
+ /** Returns the recorded verifier checks with their pass and fail outcomes, newest first. */
4331
+ async getverifierchecks() {
4332
+ return await this.adapter.get("swarmverifierchecks") ?? [];
4333
+ }
4334
+ /** Replaces the stored review requests routed between agents after one request, ack, answer or timeout. */
4335
+ async setreviewrequests(requests) {
4336
+ return this.adapter.set("swarmreviewrequests", requests);
4337
+ }
4338
+ /** Returns the stored review requests routed between agents. */
4339
+ async getreviewrequests() {
4340
+ return await this.adapter.get("swarmreviewrequests") ?? [];
4341
+ }
4342
+ /** Records one tab handoff with its packaged task state and its resumed state. */
4343
+ async addhandoff(record2) {
4344
+ await this.adapter.set("swarmhandoffs", [record2, ...await this.adapter.get("swarmhandoffs") ?? []].filter((entry, index, all) => all.findIndex((candidate) => candidate.id === entry.id) === index));
4345
+ }
4346
+ /** Replaces one stored handoff record after its transfer or resume. */
4347
+ async updatehandoff(record2) {
4348
+ await this.adapter.set("swarmhandoffs", (await this.adapter.get("swarmhandoffs") ?? []).map((entry) => entry.id === record2.id ? record2 : entry));
4349
+ }
4350
+ /** Returns the handoff log of tab transfers between agents, newest first. */
4351
+ async gethandoffs() {
4352
+ return await this.adapter.get("swarmhandoffs") ?? [];
4353
+ }
4354
+ /** Replaces the stored resource locks after one acquire, release or expiry sweep. */
4355
+ async setlocks(locks) {
4356
+ return this.adapter.set("swarmlocks", locks);
4357
+ }
4358
+ /** Returns the held resource locks with their holders and expiries. */
4359
+ async getlocks() {
4360
+ return await this.adapter.get("swarmlocks") ?? [];
4361
+ }
4362
+ /** Records one conflict scan report of overlapping writes, newest first. */
4363
+ async addconflictscan(scan) {
4364
+ await this.adapter.set("swarmconflicts", [scan, ...await this.adapter.get("swarmconflicts") ?? []]);
4365
+ }
4366
+ /** Returns the recorded conflict scan reports, newest first. */
4367
+ async getconflictscans() {
4368
+ return await this.adapter.get("swarmconflicts") ?? [];
4369
+ }
4370
+ /** Stores the merged result report with its mergeentry provenance. */
4371
+ async setreport(report) {
4372
+ return this.adapter.set("swarmreport", report);
4373
+ }
4374
+ /** Returns the stored merged result report across agents. */
4375
+ async getreport() {
4376
+ return this.adapter.get("swarmreport");
4377
+ }
4378
+ /** Records one progressboard snapshot under the user configured retention window; an absent window keeps every snapshot. */
4379
+ async addboardsnapshot(board) {
4380
+ const retention = (await this.getsettings())?.boardretention;
4381
+ await this.adapter.set("swarmboards", [board, ...await this.adapter.get("swarmboards") ?? []].slice(0, retention ?? 100));
4382
+ }
4383
+ /** Returns the stored progressboard snapshots, newest first. */
4384
+ async getboardsnapshots() {
4385
+ return await this.adapter.get("swarmboards") ?? [];
4386
+ }
4387
+ /** Records one escalation lifted to the user, newest first. */
4388
+ async addescalation(escalation) {
4389
+ await this.adapter.set("swarmescalations", [escalation, ...await this.adapter.get("swarmescalations") ?? []]);
4390
+ }
4391
+ /** Replaces one stored escalation after its user decision. */
4392
+ async updateescalation(escalation) {
4393
+ await this.adapter.set("swarmescalations", (await this.adapter.get("swarmescalations") ?? []).map((entry) => entry.id === escalation.id ? escalation : entry));
4394
+ }
4395
+ /** Returns the escalations awaiting the user and the decided ones, newest first. */
4396
+ async getescalations() {
4397
+ return await this.adapter.get("swarmescalations") ?? [];
4398
+ }
4399
+ /** Records one consensus round or replaces the stored one after a vote. */
4400
+ async setconsensusround(round) {
4401
+ const rounds = await this.adapter.get("swarmconsensus") ?? [];
4402
+ await this.adapter.set("swarmconsensus", rounds.some((entry) => entry.id === round.id) ? rounds.map((entry) => entry.id === round.id ? round : entry) : [round, ...rounds]);
4403
+ }
4404
+ /** Returns the consensus rounds with their votes and quorum states, newest first. */
4405
+ async getconsensusrounds() {
4406
+ return await this.adapter.get("swarmconsensus") ?? [];
4407
+ }
4408
+ /** Appends one action to the interleaved timeline of swarm actions, oldest first under a window of 500. */
4409
+ async addswarmaction(action) {
4410
+ await this.adapter.set("swarmtimeline", [...await this.adapter.get("swarmtimeline") ?? [], action].slice(-500));
4411
+ }
4412
+ /** Returns the interleaved timeline of swarm actions with the optional agent and kind filters, oldest first. */
4413
+ async getswarmtimeline(filters) {
4414
+ const actions = await this.adapter.get("swarmtimeline") ?? [];
4415
+ 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);
4416
+ }
4417
+ /** Stores one shared cost accounting snapshot of the swarm, newest first. */
4418
+ async addswarmcost(cost) {
4419
+ await this.adapter.set("swarmcosts", [cost, ...await this.adapter.get("swarmcosts") ?? []].slice(0, 100));
4420
+ }
4421
+ /** Returns the stored shared cost accounting snapshots of the swarm, newest first. */
4422
+ async getswarmcosts() {
4423
+ return await this.adapter.get("swarmcosts") ?? [];
4424
+ }
4425
+ /**
4426
+ * Execution environment persistence of the 1.1.60 family.
4427
+ * 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.
4428
+ * The adapter seam keeps every accessor a one line storage delegation so a future worker state backend replaces the adapter only.
4429
+ */
4430
+ /** Returns the environment grant list of the active session; an absent list keeps the documented default posture. */
4431
+ async getenvironmentgrants() {
4432
+ return (await this.getsession())?.environmentgrants;
4433
+ }
4434
+ /** Replaces the environment grant list of the active session so the environment grants join the origin grants in the session record. */
4435
+ async setenvironmentgrants(grants) {
4436
+ const session = await this.getsession();
4437
+ if (!session) throw new Error("The environment grants need an active session to join.");
4438
+ await this.setsession({ ...session, environmentgrants: grants });
4439
+ }
4440
+ /** 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. */
4441
+ async setrunstate(profileid, state) {
4442
+ const sealed = await sealrunstate(state);
4443
+ const index = await this.adapter.get("runstateindex") ?? [];
4444
+ await this.adapter.set(`runstate:${profileid}`, sealed);
4445
+ if (!index.includes(profileid)) await this.adapter.set("runstateindex", [...index, profileid]);
4446
+ }
4447
+ /** Opens the sealed run state of one profile; a missing or tampered seal returns undefined so the recovery never trusts a broken record. */
4448
+ async getrunstate(profileid) {
4449
+ const sealed = await this.adapter.get(`runstate:${profileid}`);
4450
+ if (!sealed) return void 0;
4451
+ try {
4452
+ return await openseal(sealed);
4453
+ } catch {
4454
+ return void 0;
4455
+ }
4456
+ }
4457
+ /** 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. */
4458
+ async removerunstate(profileid) {
4459
+ const index = await this.adapter.get("runstateindex") ?? [];
4460
+ await this.adapter.set("runstateindex", index.filter((entry) => entry !== profileid));
4461
+ await this.adapter.set(`runstate:${profileid}`, { payload: "", algorithm: "sha-256", digest: "", sealedat: 0 });
4462
+ }
4463
+ /** Lists the stored run state records of every profile, oldest update first. */
4464
+ async listrunstates() {
4465
+ const index = await this.adapter.get("runstateindex") ?? [];
4466
+ const states = [];
4467
+ for (const profileid of index) {
4468
+ const state = await this.getrunstate(profileid);
4469
+ if (state) states.push(state);
4470
+ }
4471
+ return states.sort((one, two) => one.updatedat - two.updatedat);
4472
+ }
4473
+ /** 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. */
4474
+ async expirerunstates(window2, now) {
4475
+ if (window2 === void 0) return await this.listrunstates();
4476
+ const index = await this.adapter.get("runstateindex") ?? [];
4477
+ const kept = [];
4478
+ for (const profileid of index) {
4479
+ const state = await this.getrunstate(profileid);
4480
+ if (!state) continue;
4481
+ if (now - state.updatedat > window2 && state.keepalive.state === "stopped") {
4482
+ const summary = { runid: state.runid, sessionid: state.sessionid, planid: state.planid, profileid: state.profileid, state: "expired", urlhistory: [], environments: {}, turnarounds: {}, keepalive: state.keepalive, updatedat: now };
4483
+ const sealed = await sealrunstate(summary);
4484
+ await this.adapter.set(`runstate:${profileid}`, sealed);
4485
+ } else {
4486
+ kept.push(state);
4487
+ }
4488
+ }
4489
+ return kept;
4490
+ }
4491
+ /** Records one worker spawn or teardown event with its provenance beside the step outcomes. */
4492
+ async addworkerevent(event) {
4493
+ await this.adapter.set("workerevents", [event, ...await this.adapter.get("workerevents") ?? []].slice(0, 500));
4494
+ }
4495
+ /** Returns the recorded worker spawn and teardown events, newest first. */
4496
+ async getworkerevents() {
4497
+ return await this.adapter.get("workerevents") ?? [];
4498
+ }
4499
+ /** Records one spawned offscreen document with its reasons and justification in the registry. */
4500
+ async addoffscreenentry(entry) {
4501
+ await this.adapter.set("offscreenregistry", [entry, ...await this.adapter.get("offscreenregistry") ?? []]);
4502
+ }
4503
+ /** Replaces one registry entry after its offscreen document closes. */
4504
+ async updateoffscreenentry(entry) {
4505
+ await this.adapter.set("offscreenregistry", (await this.adapter.get("offscreenregistry") ?? []).map((candidate) => candidate.runid === entry.runid ? entry : candidate));
4506
+ }
4507
+ /** Returns the offscreen document registry with the reasons and justification of every spawn. */
4508
+ async getoffscreenentries() {
4509
+ return await this.adapter.get("offscreenregistry") ?? [];
4510
+ }
4511
+ /** Records one sandbox render with its provenance, source origin and nonce. */
4512
+ async addsandboxrender(render) {
4513
+ await this.adapter.set("sandboxrenders", [render, ...await this.adapter.get("sandboxrenders") ?? []].slice(0, 500));
4514
+ }
4515
+ /** Returns the recorded sandbox renders with their provenance, newest first. */
4516
+ async getsandboxrenders() {
4517
+ return await this.adapter.get("sandboxrenders") ?? [];
4518
+ }
4519
+ /** Replaces the stored run locks after one acquisition, release or expiry sweep. */
4520
+ async setrunlocks(locks) {
4521
+ return this.adapter.set("runlocks", locks);
4522
+ }
4523
+ /** Returns the held run locks with their sessions, runs and expiries. */
4524
+ async getrunlocks() {
4525
+ return await this.adapter.get("runlocks") ?? [];
4526
+ }
4527
+ /** Tracks the storage quota usage of the run state: the last measured bytes stay beside the user configured ceiling so the pruning reads both. */
4528
+ async trackrunstatequota(used) {
4529
+ const settings = await this.getsettings();
4530
+ await this.adapter.set("runstatequota", { used, ...settings?.runstatebytes !== void 0 ? { ceiling: settings.runstatebytes } : {}, trackedat: Date.now() });
4531
+ }
4532
+ /** Returns the last tracked storage quota usage of the run state with its ceiling when the user configured one. */
4533
+ async getrunstatequota() {
4534
+ return this.adapter.get("runstatequota");
4535
+ }
4536
+ /** Exports every stored run state as one single audit record through the runstate export envelope. */
4537
+ async exportrunstates() {
4538
+ return exportrunstate(await this.listrunstates(), Date.now());
4539
+ }
4136
4540
  };
4137
4541
  function mediakindof(record2) {
4138
4542
  if ("pages" in record2) return "pdf";
@@ -4176,6 +4580,121 @@ function randomid() {
4176
4580
  return crypto.randomUUID();
4177
4581
  }
4178
4582
 
4583
+ // environments.ts
4584
+ var offloadfamilies = [
4585
+ { task: "htmlsnapshot", kinds: ["readhtml", "parsehtml", "readertree", "readoutline", "classifypage"] },
4586
+ { task: "jsonpayload", kinds: ["readjson", "parsejson"] },
4587
+ { task: "tablerows", kinds: ["readtable", "scrapetable", "detecttables", "deduperows", "transformvalues"] },
4588
+ { task: "a11ytree", kinds: ["a11ytree"] },
4589
+ { task: "complexselector", kinds: ["resolvexpath", "deriveselector", "detectvirtual"] },
4590
+ { task: "stitchshots", kinds: ["contactsheet", "timelapse", "makethumbs"] }
4591
+ ];
4592
+ function offfamilyof(kind) {
4593
+ return offloadfamilies.find((family) => family.kinds.includes(kind))?.task;
4594
+ }
4595
+ function environmentsof(step) {
4596
+ if (markuprenderstep(step)) return ["sandboxframe"];
4597
+ if (step.kind === "evaluate") return ["isolatedworld"];
4598
+ if (offfamilyof(step.kind) !== void 0) return ["pagecontext", "offscreenworker"];
4599
+ return ["pagecontext"];
4600
+ }
4601
+ function defaultenvironment(step) {
4602
+ if (markuprenderstep(step)) return "sandboxframe";
4603
+ if (step.kind === "evaluate") return "isolatedworld";
4604
+ return "pagecontext";
4605
+ }
4606
+ function offamilyeligible(kind) {
4607
+ return offloadfamilies.some((family) => family.kinds.includes(kind));
4608
+ }
4609
+ function markuprenderstep(step) {
4610
+ if (!step.options) return false;
4611
+ try {
4612
+ const parsed = JSON.parse(step.options);
4613
+ return Boolean(parsed && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.markup === "string" && parsed.markup.trim() !== "");
4614
+ } catch {
4615
+ return false;
4616
+ }
4617
+ }
4618
+ function environmentrequirementsof(kinds) {
4619
+ return kinds.map((kind) => {
4620
+ const bare = { kind };
4621
+ const environments = environmentsof(bare);
4622
+ return { kind, environments, defaultenvironment: defaultenvironment(bare) };
4623
+ });
4624
+ }
4625
+ function executorregistry() {
4626
+ return [
4627
+ { environment: "pagecontext", adapter: "pagebridge", description: "The page bridge executes dom actions inside the live page because page events only fire there." },
4628
+ { environment: "isolatedworld", adapter: "scriptingapi", description: "The scripting api injects step logic inside the isolated world where page globals stay unreachable from step code." },
4629
+ { 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." },
4630
+ { 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." }
4631
+ ];
4632
+ }
4633
+ function routeenvironment(step, input) {
4634
+ const allowed = environmentsof(step);
4635
+ const named = step.environment;
4636
+ if (named !== void 0) {
4637
+ 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.` };
4638
+ return { environment: named, fallback: false, reason: `The reviewed step names its ${named} environment and the ${step.kind} kind permits it.` };
4639
+ }
4640
+ if (markuprenderstep(step)) return { environment: "sandboxframe", fallback: false, reason: `The ${step.kind} step carries untrusted markup, so it renders inside the sandboxframe only.` };
4641
+ 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." };
4642
+ if (offamilyeligible(step.kind)) {
4643
+ 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.` };
4644
+ 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.` };
4645
+ return { environment: "offscreenworker", fallback: false, reason: `The ${step.kind} step offloads into the offscreen worker pool under the granted capability.` };
4646
+ }
4647
+ return { environment: "pagecontext", fallback: false, reason: `The ${step.kind} step keeps the pagecontext because page events only fire inside the live page.` };
4648
+ }
4649
+ function workerrequestof(input) {
4650
+ const task = offfamilyof(input.kind);
4651
+ if (task === void 0) throw new Error(`The ${input.kind} kind stays outside the offscreen worker pool families.`);
4652
+ if (input.payload.trim() === "") throw new Error("The worker request needs its payload reference.");
4653
+ return { id: input.id, runid: input.runid, stepid: input.stepid, task, payload: input.payload, transferables: transferablekeys(input.options ?? {}), sentat: input.sentat };
4654
+ }
4655
+ function transferablekeys(options) {
4656
+ return Object.keys(options).filter((key) => options[key] instanceof ArrayBuffer);
4657
+ }
4658
+ function poolplan(input) {
4659
+ if (input.size !== void 0) {
4660
+ 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." };
4661
+ const target = input.size;
4662
+ 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.` };
4663
+ 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.` };
4664
+ return { workers: target, added: 0, retired: 0, reason: `The pool holds the ${target} workers the user configured.` };
4665
+ }
4666
+ 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.` };
4667
+ 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"}.` };
4668
+ return { workers: input.current, added: 0, retired: 0, reason: `The ${input.current} workers match the ${input.pending} pending parses; the pool stays unchanged.` };
4669
+ }
4670
+ function openoffscreen(input) {
4671
+ if (input.document.trim() === "") throw new Error("The offscreen document needs its user configured path.");
4672
+ if (input.reasons.length === 0) throw new Error("The offscreen document needs the reasons the user reviewed.");
4673
+ if (input.justification.trim() === "") throw new Error("The offscreen document needs its justification in plain language.");
4674
+ const open = input.registry.find((entry2) => entry2.runid === input.runid && entry2.closedat === void 0);
4675
+ if (open) return { registry: input.registry, entry: open, reused: true };
4676
+ const entry = { document: input.document, runid: input.runid, reasons: [...input.reasons], justification: input.justification, createdat: input.now };
4677
+ return { registry: [entry, ...input.registry], entry, reused: false };
4678
+ }
4679
+ function closeoffscreen(registry, runid, now) {
4680
+ const open = registry.find((entry) => entry.runid === runid && entry.closedat === void 0);
4681
+ if (!open) return { registry, closed: false };
4682
+ return { registry: registry.map((entry) => entry === open ? { ...entry, closedat: now } : entry), closed: true };
4683
+ }
4684
+ function isolatedinjection(step) {
4685
+ if (step.kind !== "evaluate") throw new Error("The isolated world injection serves the evaluate kind only.");
4686
+ if (!step.value || step.value.trim() === "") throw new Error("The evaluate step needs its reviewed expression.");
4687
+ let args = [];
4688
+ if (step.options) {
4689
+ try {
4690
+ const parsed = JSON.parse(step.options);
4691
+ if (Array.isArray(parsed)) args = parsed.filter((item) => typeof item === "string");
4692
+ } catch {
4693
+ }
4694
+ }
4695
+ return { world: "ISOLATED", code: step.value, args };
4696
+ }
4697
+
4179
4698
  // toolcatalog.ts
4180
4699
  var toolcatalogversion = 1;
4181
4700
  var toolnamespaces = ["browser", "workflow", "memory", "system"];
@@ -8940,6 +9459,110 @@ function blackboardconsentgrade(entry) {
8940
9459
  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.` };
8941
9460
  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.` };
8942
9461
  }
9462
+ function leaderelectionvalid(input) {
9463
+ if (input.rule.kind !== "first" && input.rule.kind !== "named") return { allowed: false, reason: "The leader election rule stays first or named as the user configured it." };
9464
+ if (input.rule.kind === "named") {
9465
+ if (input.rule.agentid === void 0 || input.rule.agentid.trim() === "") return { allowed: false, reason: "The named leader election rule needs the agent id the user named." };
9466
+ if (!input.agents.some((agent) => agent.id === input.rule.agentid && agent.state !== "stopped")) return { allowed: false, reason: `The named leader election rule names the agent ${input.rule.agentid} which is not a live agent of the swarm.` };
9467
+ }
9468
+ return { allowed: true, reason: input.rule.kind === "first" ? "The first registration rule elects the leader exactly as the user configured." : `The named rule elects the agent ${input.rule.agentid} exactly as the user configured.` };
9469
+ }
9470
+ function criticreviewgrade(review) {
9471
+ if (review.reviewerid.trim() === "") return { allowed: false, reason: "The critic review needs its reviewing agent." };
9472
+ if (review.subjectagentid.trim() === "") return { allowed: false, reason: "The critic review needs the subject agent whose output it reviews." };
9473
+ if (review.verdict !== "approve" && review.verdict !== "changes" && review.verdict !== "reject") return { allowed: false, reason: "The critic review carries one of the three verdicts approve, changes or reject." };
9474
+ if (review.verdict === "changes" && review.requiredchanges.length === 0) return { allowed: false, reason: "A changes verdict needs its required changes in plain language." };
9475
+ if (review.verdict === "reject" && review.issues.length === 0) return { allowed: false, reason: "A reject verdict needs the issues the critic found." };
9476
+ return { allowed: true, reason: `The critic review of the output of ${review.subjectagentid} stays read only: the critic ${review.reviewerid} returns its ${review.verdict} verdict and never acts on the page; the rework still passes the same human review.` };
9477
+ }
9478
+ function verifiermethodgrade(input) {
9479
+ if (input.method.trim() === "") return { allowed: false, reason: "The verifier check needs the method it used." };
9480
+ if (input.allowed.length > 0 && !input.allowed.includes(input.method)) return { allowed: false, reason: `The verifier method ${input.method} is not one of the methods the user allowed: ${input.allowed.join(", ")}.` };
9481
+ return { allowed: true, reason: input.allowed.length === 0 ? `The verifier method ${input.method} runs under the documented open method list the user chose not to narrow.` : `The verifier method ${input.method} sits inside the methods the user allowed.` };
9482
+ }
9483
+ function handoffgrantgate(input) {
9484
+ if (input.record.toagentid.trim() === "" || input.record.fromagentid.trim() === "") return { allowed: false, reason: "The handoff names its transferring and receiving agents." };
9485
+ if (input.toscope === void 0) return { allowed: true, reason: `The receiving agent ${input.record.toagentid} carries no narrowed scope, so the handoff stays unbounded inside the original session grants.` };
9486
+ const outside = input.toscope.origins.filter((origin) => input.sessiongrants.length > 0 && !input.sessiongrants.includes(origin));
9487
+ if (outside.length > 0) return { allowed: false, reason: `The handoff to ${input.record.toagentid} would need the origins ${outside.join(", ")} which the session grant list does not carry; a tab transfer never widens the session grants.` };
9488
+ return { allowed: true, reason: `The handoff from ${input.record.fromagentid} to ${input.record.toagentid} preserves the original session grants; the receiving scope stays inside them.` };
9489
+ }
9490
+ function lockscopevalid(lock) {
9491
+ if (lock.key.trim() === "") return { allowed: false, reason: "The resource lock needs its key." };
9492
+ if (lock.origin.trim() === "" || lock.selector.trim() === "") return { allowed: false, reason: "The resource lock names exactly one origin and one selector; a lock never spans unrelated origins." };
9493
+ if (lock.key !== `${lock.origin}|${lock.selector}`) return { allowed: false, reason: "The lock key must compose of its one origin and its one selector so the scope never spans unrelated origins." };
9494
+ if (lock.kind !== "exclusive" && lock.kind !== "shared") return { allowed: false, reason: "The lock kind stays exclusive or shared." };
9495
+ return { allowed: true, reason: `The lock ${lock.key} spans exactly one target of one origin for the holder ${lock.holder}.` };
9496
+ }
9497
+ function conflictresolutiongrade(rule) {
9498
+ if (rule !== "first" && rule !== "last" && rule !== "preferagent" && rule !== "fail") return { allowed: false, reason: "The conflict resolution rule stays first, last, preferagent or fail as the user configured it." };
9499
+ if (rule === "last" || rule === "preferagent") return { allowed: true, reason: `The ${rule} conflict resolution rule overwrites one parallel value with another, so it grades sensitive and the merged report still passes the human review.` };
9500
+ return { allowed: true, reason: `The ${rule} conflict resolution rule keeps or refuses the parallel values without overwriting, so it grades read side.` };
9501
+ }
9502
+ function escalationgate(escalation) {
9503
+ if (escalation.agentid.trim() === "") return { allowed: false, reason: "The escalation names the agent whose decision it lifts." };
9504
+ if (escalation.subject.trim() === "") return { allowed: false, reason: "The escalation needs its subject." };
9505
+ if (escalation.context.trim() === "") return { allowed: false, reason: "The escalation needs its full context in plain language; the user decides on what the agent saw." };
9506
+ if (escalation.state === "decided" && (escalation.decision === void 0 || escalation.decision.trim() === "")) return { allowed: false, reason: "A decided escalation carries the decision the user wrote." };
9507
+ return { allowed: true, reason: `The escalation of ${escalation.agentid} stays human decided: the agent lifts the stalled decision with its full context and the user alone writes the outcome.` };
9508
+ }
9509
+ function consensusquorumvalid(input) {
9510
+ if (!Number.isInteger(input.quorum) || input.quorum < 1) return { allowed: false, reason: "The consensus quorum stays a positive whole number the user configured." };
9511
+ if (input.quorum > input.voters) return { allowed: false, reason: `The consensus quorum ${input.quorum} exceeds the ${input.voters} voting agents the user counted; an unreachable quorum never carries.` };
9512
+ return { allowed: true, reason: `The consensus quorum ${input.quorum} of ${input.voters} voting agents stays the user configured value with no engine default.` };
9513
+ }
9514
+ function workerscalevalid(bound) {
9515
+ if (bound === void 0) return { allowed: true, reason: "No worker bound is configured, so the worker scale stays the user choice alone with no engine cap." };
9516
+ if (!Number.isFinite(bound) || bound < 1) return { allowed: false, reason: "The worker scale bound stays a positive user value; no engine cap exists." };
9517
+ return { allowed: true, reason: `The worker scale bound ${bound} stays the user configured value; the scaling never passes it and no engine cap exists.` };
9518
+ }
9519
+ function mergeegressgrade(input) {
9520
+ if (input.report.title.trim() === "") return { allowed: false, reason: "The merged report needs its title before any export." };
9521
+ if (input.carriespagecontent) return { allowed: true, reason: `The export of the report ${input.report.title} carries page content from the sources ${input.report.sources.join(", ")} and grades as a data egress event in the audit trail.` };
9522
+ return { allowed: true, reason: `The export of the report ${input.report.title} carries no page content and stays a plain report export.` };
9523
+ }
9524
+ function stepenvironmentvalid(step) {
9525
+ 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." };
9526
+ const allowed = environmentsof(step);
9527
+ 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.` };
9528
+ 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.` };
9529
+ }
9530
+ function environmentgrantgate(step, grants) {
9531
+ 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.` };
9532
+ const environment = step.environment ?? defaultenvironment(step);
9533
+ 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.` };
9534
+ return { allowed: true, reason: `The ${environment} environment of the ${step.kind} step sits inside the ${grants.join(", ")} the session granted.` };
9535
+ }
9536
+ function offscreencapabilitygate(input) {
9537
+ if (input.environment !== "offscreenworker") return { allowed: true, reason: `The ${input.environment} environment needs no offscreen capability grant.` };
9538
+ 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." };
9539
+ return { allowed: true, reason: "The offscreen worker pool runs under the user granted offscreen capability." };
9540
+ }
9541
+ function keepalivegate(input) {
9542
+ if (!input.session) return { allowed: false, reason: "The keepalive port opens only inside an active session." };
9543
+ if (input.session.stoppedat !== void 0) return { allowed: false, reason: "The keepalive port stays closed for a stopped session." };
9544
+ if (input.session.pausedat !== void 0) return { allowed: false, reason: "The keepalive port stays closed while the session pauses; a resumed run reattaches it." };
9545
+ if (input.now > input.session.expiresat) return { allowed: false, reason: "The keepalive port stays closed for an expired session." };
9546
+ 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." };
9547
+ return { allowed: true, reason: `The approved plan ${input.plan.id} of the active session holds the keepalive port open for its whole run.` };
9548
+ }
9549
+ function keepaliveintervalvalid(interval) {
9550
+ if (!Number.isFinite(interval) || interval <= 0) return { allowed: false, reason: "The keepalive heartbeat interval stays a positive user value in milliseconds." };
9551
+ 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.` };
9552
+ }
9553
+ function workerpoolsizevalid(size) {
9554
+ 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." };
9555
+ 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." };
9556
+ return { allowed: true, reason: `The worker pool size ${size} stays the user configured value; no engine cap exists.` };
9557
+ }
9558
+ function sandboxorigingate(input) {
9559
+ if (input.origin.trim() === "") return { allowed: false, reason: "The sandbox render needs the source origin of its untrusted markup." };
9560
+ 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(", ")}.` };
9561
+ 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.` };
9562
+ }
9563
+ function environmentrequirements() {
9564
+ return environmentrequirementsof([...allowedactions]);
9565
+ }
8943
9566
 
8944
9567
  // progress.ts
8945
9568
  function emptyprogress(planid, now) {
@@ -8954,6 +9577,14 @@ function recordoutcome(progress, planid, outcome, now) {
8954
9577
  const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
8955
9578
  return { ...base, outcomes: [...base.outcomes ?? [], outcome], updatedat: now };
8956
9579
  }
9580
+ function recordenvironment2(progress, planid, stepid, environment, now) {
9581
+ const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
9582
+ return { ...base, environments: { ...base.environments ?? {}, [stepid]: environment }, updatedat: now };
9583
+ }
9584
+ function recordturnaround2(progress, planid, stepid, milliseconds, now) {
9585
+ const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
9586
+ return { ...base, turnarounds: { ...base.turnarounds ?? {}, [stepid]: milliseconds }, updatedat: now };
9587
+ }
8957
9588
  function iscomplete(progress, plan) {
8958
9589
  if (!progress || progress.planid !== plan.id) return false;
8959
9590
  const required = plan.steps.map((step) => step.id);
@@ -9119,7 +9750,7 @@ function recordtoolcall(progress, planid, stepid, entry, now) {
9119
9750
  }
9120
9751
 
9121
9752
  // version.ts
9122
- var packageversion = "1.1.58";
9753
+ var packageversion = "1.1.60";
9123
9754
 
9124
9755
  // types.ts
9125
9756
  var protocolversion = packageversion;
@@ -10083,6 +10714,16 @@ function runhistoryquery(value) {
10083
10714
  function runhistoryreport(input) {
10084
10715
  return { version: protocolversion, entries: input.entries, query: input.query ?? {} };
10085
10716
  }
10717
+ function environmentreport(input) {
10718
+ return {
10719
+ version: protocolversion,
10720
+ environments: Object.entries(input.environments).map(([stepid, environment]) => ({ stepid, environment })),
10721
+ turnarounds: Object.entries(input.turnarounds ?? {}).map(([stepid, milliseconds]) => ({ stepid, milliseconds })),
10722
+ offscreen: input.offscreen ?? [],
10723
+ workers: input.workers ?? 0,
10724
+ ...input.keepalive !== void 0 ? { keepalive: input.keepalive } : {}
10725
+ };
10726
+ }
10086
10727
 
10087
10728
  // capture.ts
10088
10729
  var capturekinds = ["shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet"];
@@ -11517,6 +12158,376 @@ function budgetcheck(input) {
11517
12158
  return { allowed: true, halted: false, asksuser: false };
11518
12159
  }
11519
12160
 
12161
+ // orchestration.ts
12162
+ function electleader(input) {
12163
+ const live = input.agents.filter((agent) => agent.state !== "stopped");
12164
+ if (live.length === 0) throw new Error("The swarm holds no live agent; the leader election waits for the user to register one.");
12165
+ const rule = input.rule ?? { kind: "first" };
12166
+ let leader;
12167
+ if (rule.kind === "named") {
12168
+ if (rule.agentid === void 0 || rule.agentid.trim() === "") throw new Error("The named election rule needs the agent id the user named.");
12169
+ leader = live.find((agent) => agent.id === rule.agentid);
12170
+ if (!leader) throw new Error(`The named election rule names the agent ${rule.agentid} which is not a live agent of the swarm.`);
12171
+ } else {
12172
+ leader = live[live.length - 1];
12173
+ }
12174
+ if (!leader) throw new Error("The leader election found no live agent.");
12175
+ const leaderid = leader.id;
12176
+ const workers = live.filter((agent) => agent.id !== leaderid && agent.role === "worker");
12177
+ const critics = live.filter((agent) => agent.id !== leaderid && agent.role === "critic");
12178
+ const verifiers = live.filter((agent) => agent.id !== leaderid && agent.role === "verifier");
12179
+ return {
12180
+ id: input.id,
12181
+ leaderid: leader.id,
12182
+ workerids: workers.map((agent) => agent.id),
12183
+ criticids: critics.map((agent) => agent.id),
12184
+ verifierids: verifiers.map((agent) => agent.id),
12185
+ assignments: [],
12186
+ rule: { kind: rule.kind, ...rule.agentid !== void 0 ? { agentid: rule.agentid } : {} },
12187
+ electedat: input.now
12188
+ };
12189
+ }
12190
+ function assignwork(input) {
12191
+ if (input.topology.workerids.length === 0) throw new Error("The topology holds no worker; the user adds workers before the assignment.");
12192
+ const assignments = [];
12193
+ const tasks = input.tasks.filter((task) => task.state === "queued" || task.state === "claimed");
12194
+ tasks.forEach((task, index) => {
12195
+ const workerid = input.topology.workerids[index % input.topology.workerids.length];
12196
+ 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 });
12197
+ });
12198
+ return { ...input.topology, assignments };
12199
+ }
12200
+ function collectresults(input) {
12201
+ const gathered = input.topology.assignments.map((assignment) => {
12202
+ const output = input.outputs.find((entry) => entry.taskid === assignment.taskid && entry.workerid === assignment.workerid);
12203
+ 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.` };
12204
+ });
12205
+ return { gathered, missing: gathered.filter((entry) => entry.state === "pending").map((entry) => `${entry.workerid}:${entry.taskid}`) };
12206
+ }
12207
+ function scaleworkers(input) {
12208
+ const live = input.agents.filter((agent) => agent.state === "active" && agent.role === "worker" && agent.id !== input.topology.leaderid);
12209
+ const current = input.topology.workerids.filter((workerid) => live.some((agent) => agent.id === workerid));
12210
+ const ceiling = input.bound;
12211
+ if (input.pending > current.length) {
12212
+ const available = live.filter((agent) => !current.includes(agent.id)).map((agent) => agent.id);
12213
+ const wanted = input.pending - current.length;
12214
+ const addable = ceiling === void 0 ? available.slice(0, wanted) : available.slice(0, Math.min(wanted, Math.max(ceiling - current.length, 0)));
12215
+ 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.` };
12216
+ 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`}.` };
12217
+ }
12218
+ const keep = Math.max(input.pending, 0);
12219
+ if (current.length > keep) {
12220
+ const retired = current.slice(keep);
12221
+ 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(", ")}.` };
12222
+ }
12223
+ return { topology: input.topology, added: [], retired: [], reason: `The load of ${input.pending} pending slices matches the ${current.length} workers; the scale stays unchanged.` };
12224
+ }
12225
+ function plannersplit(input) {
12226
+ if (input.planownerid.trim() === "" || input.runownerid.trim() === "") throw new Error("The planner executor split needs its plan owner and run owner agent ids.");
12227
+ 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.");
12228
+ return { id: input.id, planownerid: input.planownerid, runownerid: input.runownerid, ...input.taskid !== void 0 && input.taskid.trim() !== "" ? { taskid: input.taskid } : {}, stepreports: [], splitat: input.now };
12229
+ }
12230
+ function reportstep(input) {
12231
+ if (input.stepid.trim() === "") throw new Error("The executor report needs its step id.");
12232
+ if (input.detail.trim() === "") throw new Error("The executor report needs its detail in plain language.");
12233
+ const report = { stepid: input.stepid.trim(), outcome: input.outcome, detail: input.detail, reportedat: input.now };
12234
+ return { ...input.split, stepreports: [...input.split.stepreports.filter((entry) => entry.stepid !== report.stepid), report] };
12235
+ }
12236
+ function requestreview(input) {
12237
+ if (input.subject.trim() === "") throw new Error("The review request needs its subject.");
12238
+ if (input.payload.trim() === "") throw new Error("The review request needs its payload.");
12239
+ if (input.toagentid.trim() === "" || input.toagentid === input.fromagentid) throw new Error("The review request names another reviewing agent, never its own requester.");
12240
+ 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 } : {} };
12241
+ return [request, ...input.requests];
12242
+ }
12243
+ function ackreview(input) {
12244
+ const request = input.requests.find((entry) => entry.id === input.id);
12245
+ if (!request) throw new Error(`The review request ${input.id} does not exist.`);
12246
+ if (request.state !== "open") throw new Error(`The review request ${input.id} is ${request.state}; only an open request receives its ack.`);
12247
+ return input.requests.map((entry) => entry.id === input.id ? { ...entry, state: "acked", ackedat: input.now } : entry);
12248
+ }
12249
+ function applyreview(input) {
12250
+ const request = input.requests.find((entry) => entry.id === input.id);
12251
+ if (!request) throw new Error(`The review request ${input.id} does not exist.`);
12252
+ 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.`);
12253
+ 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.`);
12254
+ if (input.verdict === "changes" && input.requiredchanges.length === 0) throw new Error("A changes verdict needs its required changes in plain language.");
12255
+ 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 };
12256
+ return { review, requests: input.requests.map((entry) => entry.id === input.id ? { ...entry, state: "answered", answeredat: input.now } : entry) };
12257
+ }
12258
+ function sweepreviews(input) {
12259
+ const expired = input.requests.filter((request) => request.state === "open" || request.state === "acked").filter((request) => request.timeoutat !== void 0 && input.now > request.timeoutat);
12260
+ if (expired.length === 0) return { requests: input.requests, timedout: [] };
12261
+ const ids = new Set(expired.map((request) => request.id));
12262
+ return { requests: input.requests.map((request) => ids.has(request.id) ? { ...request, state: "timeout" } : request), timedout: [...ids] };
12263
+ }
12264
+ function checkclaim(input) {
12265
+ if (input.claim.trim() === "") throw new Error("The verifier check needs its claim in plain language.");
12266
+ if (input.method.trim() === "") throw new Error("The verifier check needs the method the user configured.");
12267
+ if (input.claimagentid.trim() === "") throw new Error("The verifier check names the agent whose claim it checks.");
12268
+ 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 };
12269
+ }
12270
+ function boardstate(input) {
12271
+ const lanes = input.agents.filter((agent) => agent.state !== "stopped").map((agent) => {
12272
+ const assignment = input.topology?.assignments.find((entry) => entry.workerid === agent.id);
12273
+ const claim2 = input.queue.claims.find((record2) => record2.agentid === agent.id && input.queue.items.some((item) => item.id === record2.taskid && item.state === "claimed"));
12274
+ const task = claim2 !== void 0 ? input.queue.items.find((item) => item.id === claim2.taskid) : void 0;
12275
+ const lane = task?.lane ?? (agent.role === "critic" || agent.role === "verifier" ? agent.role : agent.role === "planner" ? "planning" : "idle");
12276
+ 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] ?? [] };
12277
+ });
12278
+ return { id: `board:${input.now}`, lanes, builtat: input.now };
12279
+ }
12280
+ function escalate(input) {
12281
+ if (input.subject.trim() === "") throw new Error("The escalation needs its subject.");
12282
+ if (input.context.trim() === "") throw new Error("The escalation needs its full context in plain language; the user decides on what the agent saw.");
12283
+ if (input.agentid.trim() === "") throw new Error("The escalation names the agent whose decision it lifts.");
12284
+ return { id: input.id, agentid: input.agentid, subject: input.subject, context: input.context, state: "open", raisedat: input.now };
12285
+ }
12286
+ function resolveescalation(input) {
12287
+ if (input.escalation.state === "decided") throw new Error("The escalation already carries its user decision.");
12288
+ if (input.decision.trim() === "") throw new Error("The escalation decision needs the words the user wrote.");
12289
+ return { ...input.escalation, state: "decided", decision: input.decision, decidedat: input.now };
12290
+ }
12291
+ function arbitrate(input) {
12292
+ if (input.claims.length === 0) return [];
12293
+ if (input.rule.strategy === "priority") {
12294
+ const order = input.rule.priorityorder;
12295
+ return [...input.claims].sort((one, two) => {
12296
+ const oneindex = order.indexOf(one.agentid);
12297
+ const twoindex = order.indexOf(two.agentid);
12298
+ return (oneindex === -1 ? order.length : oneindex) - (twoindex === -1 ? order.length : twoindex) || one.claimedat - two.claimedat;
12299
+ }).map((claim2) => claim2.agentid);
12300
+ }
12301
+ 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);
12302
+ if (input.leaderid === void 0) throw new Error("The leader arbitration strategy needs the elected leader of the topology.");
12303
+ 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);
12304
+ }
12305
+ function openconsensus(input) {
12306
+ if (input.subject.trim() === "") throw new Error("The consensus round needs its subject in plain language.");
12307
+ 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.");
12308
+ return { id: input.id, subject: input.subject, votes: [], quorum: input.quorum, state: "open", openedat: input.now };
12309
+ }
12310
+ function castvote(input) {
12311
+ if (input.round.state !== "open") throw new Error(`The consensus round ${input.round.id} is ${input.round.state}; a closed round collects no vote.`);
12312
+ 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}.`);
12313
+ const votes = [...input.round.votes, { agentid: input.agentid, vote: input.vote, votedat: input.now }];
12314
+ const yes = votes.filter((entry) => entry.vote === "yes").length;
12315
+ const no = votes.filter((entry) => entry.vote === "no").length;
12316
+ if (yes >= input.round.quorum) return { ...input.round, votes, state: "carried", closedat: input.now };
12317
+ if (no >= input.round.quorum) return { ...input.round, votes, state: "failed", closedat: input.now };
12318
+ return { ...input.round, votes };
12319
+ }
12320
+ function consensusstate(round) {
12321
+ return {
12322
+ yes: round.votes.filter((entry) => entry.vote === "yes").length,
12323
+ no: round.votes.filter((entry) => entry.vote === "no").length,
12324
+ abstain: round.votes.filter((entry) => entry.vote === "abstain").length,
12325
+ quorum: round.quorum,
12326
+ state: round.state
12327
+ };
12328
+ }
12329
+
12330
+ // blackboard.ts
12331
+ var blackboardsections = ["goals", "facts", "findings", "scratch"];
12332
+ function emptyboard(sections) {
12333
+ return { sections: sections ?? blackboardsections, entries: [] };
12334
+ }
12335
+ function postentry(input) {
12336
+ if (input.key.trim() === "") throw new Error("The blackboard entry needs its key.");
12337
+ if (input.value.trim() === "") throw new Error("The blackboard entry needs its value.");
12338
+ if (input.author.trim() === "") throw new Error("The blackboard entry needs its author.");
12339
+ if (!blackboardsections.includes(input.section)) throw new Error(`The section ${input.section} is not one of the shared blackboard sections.`);
12340
+ if (input.board.entries.some((entry2) => entry2.id === input.id)) throw new Error(`The blackboard entry id ${input.id} already exists.`);
12341
+ if (input.valuekind === "json") {
12342
+ try {
12343
+ JSON.parse(input.value);
12344
+ } catch {
12345
+ throw new Error("The json blackboard entry needs a well-formed json value.");
12346
+ }
12347
+ }
12348
+ const entry = { id: input.id, key: input.key.trim(), valuekind: input.valuekind ?? "text", value: input.value, author: input.author, section: input.section, consentclass: input.consentclass ?? "read", postedat: input.now };
12349
+ return { ...input.board, sections: input.board.sections.includes(input.section) ? input.board.sections : [...input.board.sections, input.section], entries: [entry, ...input.board.entries] };
12350
+ }
12351
+ function entryfresh(entry, now, window2) {
12352
+ if (entry.retiredat !== void 0) return false;
12353
+ if (window2 === void 0) return true;
12354
+ return now - entry.postedat <= window2;
12355
+ }
12356
+ function readentries(input) {
12357
+ return input.board.entries.filter((entry) => entry.retiredat === void 0).filter((entry) => input.section === void 0 || entry.section === input.section).filter((entry) => entryfresh(entry, input.now, input.freshness)).sort((one, two) => two.postedat - one.postedat);
12358
+ }
12359
+ function retireentries(input) {
12360
+ if (input.board.retirementwindow === void 0) return { board: input.board, retired: [] };
12361
+ const stale = input.board.entries.filter((entry) => entry.retiredat === void 0 && input.now - entry.postedat > input.board.retirementwindow);
12362
+ if (stale.length === 0) return { board: input.board, retired: [] };
12363
+ const staleids = new Set(stale.map((entry) => entry.id));
12364
+ return {
12365
+ board: { ...input.board, entries: input.board.entries.map((entry) => staleids.has(entry.id) ? { ...entry, retiredat: input.now } : entry) },
12366
+ retired: [...staleids]
12367
+ };
12368
+ }
12369
+ function retireentry(input) {
12370
+ const entry = input.board.entries.find((candidate) => candidate.id === input.entryid);
12371
+ if (!entry) throw new Error(`The blackboard entry ${input.entryid} does not exist.`);
12372
+ if (entry.retiredat !== void 0) throw new Error(`The blackboard entry ${entry.key} is already retired.`);
12373
+ return { ...input.board, entries: input.board.entries.map((candidate) => candidate.id === input.entryid ? { ...candidate, retiredat: input.now } : candidate) };
12374
+ }
12375
+ function boardsummary(board, now) {
12376
+ return board.sections.map((section) => {
12377
+ const live = board.entries.filter((entry) => entry.section === section && entry.retiredat === void 0);
12378
+ return { section, entries: live.length, authors: [...new Set(live.map((entry) => entry.author))], ...live.length > 0 ? { freshestat: Math.max(...live.map((entry) => entry.postedat)) } : {} };
12379
+ });
12380
+ }
12381
+
12382
+ // coordination.ts
12383
+ function lockkey(origin, selector) {
12384
+ return `${origin}|${selector}`;
12385
+ }
12386
+ function preparehandoff(input) {
12387
+ if (!input.agents.some((agent) => agent.id === input.fromagentid)) throw new Error(`The handoff names the transferring agent ${input.fromagentid} which is not registered.`);
12388
+ if (!input.agents.some((agent) => agent.id === input.toagentid)) throw new Error(`The handoff names the receiving agent ${input.toagentid} which is not registered.`);
12389
+ if (input.fromagentid === input.toagentid) throw new Error("A handoff moves a task between two different agents; an agent never hands off to itself.");
12390
+ if (input.taskstate.trim() === "") throw new Error("The handoff needs its packaged task state in plain language; the resume continues exactly from it.");
12391
+ const from = input.agents.find((agent) => agent.id === input.fromagentid);
12392
+ const tabid2 = input.tabid ?? from.tabid;
12393
+ if (tabid2 === void 0) throw new Error("The handoff needs its tab id; the transferring agent holds no tab to hand off.");
12394
+ return { id: input.id, fromagentid: input.fromagentid, toagentid: input.toagentid, tabid: tabid2, taskstate: input.taskstate, state: "prepared", ...input.reason !== void 0 && input.reason.trim() !== "" ? { reason: input.reason } : {}, createdat: input.now };
12395
+ }
12396
+ function transferhandoff(input) {
12397
+ const record2 = input.handoffs.find((entry) => entry.id === input.id);
12398
+ if (!record2) throw new Error(`The handoff ${input.id} does not exist.`);
12399
+ if (record2.state !== "prepared") throw new Error(`The handoff ${record2.id} is ${record2.state}; only a prepared handoff transfers.`);
12400
+ const receiver = input.agents.find((agent) => agent.id === record2.toagentid);
12401
+ if (!receiver) throw new Error(`The receiving agent ${record2.toagentid} is not registered.`);
12402
+ if (receiver.state === "stopped") throw new Error(`The receiving agent ${receiver.name} is stopped; the handoff waits for its resume or another receiver.`);
12403
+ const holder = input.agents.find((agent) => agent.tabid === record2.tabid && agent.id !== record2.fromagentid && agent.state !== "stopped");
12404
+ if (holder) throw new Error(`Tab ${record2.tabid} already holds the agent ${holder.name}; one tab binds one agent.`);
12405
+ const agents = input.agents.map((agent) => {
12406
+ if (agent.id === record2.fromagentid) {
12407
+ const { tabid: tabid2, ...rest } = agent;
12408
+ void tabid2;
12409
+ return rest;
12410
+ }
12411
+ if (agent.id === record2.toagentid && record2.tabid !== void 0) return { ...agent, tabid: record2.tabid };
12412
+ return agent;
12413
+ });
12414
+ return { agents, handoffs: input.handoffs.map((entry) => entry.id === input.id ? { ...entry, state: "transferred", transferredat: input.now } : entry) };
12415
+ }
12416
+ function resumehandoff(input) {
12417
+ const record2 = input.handoffs.find((entry) => entry.id === input.id);
12418
+ if (!record2) throw new Error(`The handoff ${input.id} does not exist.`);
12419
+ if (record2.state !== "transferred") throw new Error(`The handoff ${record2.id} is ${record2.state}; only a transferred handoff resumes.`);
12420
+ return { ...record2, state: "resumed", resumedat: input.now };
12421
+ }
12422
+ function acquirelock(input) {
12423
+ if (input.holder.trim() === "") throw new Error("The lock needs its holder agent id.");
12424
+ if (input.origin.trim() === "") throw new Error("The lock needs its origin; a lock never spans unrelated origins.");
12425
+ if (input.selector.trim() === "") throw new Error("The lock needs its selector of the origin.");
12426
+ const kind = input.kind ?? "exclusive";
12427
+ const key = lockkey(input.origin.trim(), input.selector.trim());
12428
+ const held = input.locks.filter((lock2) => lock2.key === key);
12429
+ if (held.some((lock2) => lock2.holder === input.holder)) return { locks: input.locks, acquired: false, reason: `The agent ${input.holder} already holds the lock ${key}.` };
12430
+ if (held.length > 0) {
12431
+ 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.` };
12432
+ }
12433
+ 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 } : {} };
12434
+ return { locks: [...input.locks, lock], acquired: true, reason: `The ${kind} lock ${key} went to the agent ${input.holder}.` };
12435
+ }
12436
+ function releaselock(input) {
12437
+ const lock = input.locks.find((entry) => entry.key === input.key && entry.holder === input.holder);
12438
+ if (!lock) return { locks: input.locks, released: false };
12439
+ return { locks: input.locks.filter((entry) => entry.key !== input.key || entry.holder !== input.holder), released: true };
12440
+ }
12441
+ function expirelocks(input) {
12442
+ const stale = input.locks.filter((lock) => lock.expiresat !== void 0 && input.now > lock.expiresat);
12443
+ if (stale.length === 0) return { locks: input.locks, expired: [] };
12444
+ const keys = new Set(stale.map((lock) => `${lock.key}:${lock.holder}`));
12445
+ return { locks: input.locks.filter((lock) => !keys.has(`${lock.key}:${lock.holder}`)), expired: [...keys] };
12446
+ }
12447
+ function scanconflicts(input) {
12448
+ const targets = /* @__PURE__ */ new Map();
12449
+ for (const writer of input.writers) {
12450
+ const key = lockkey(writer.origin, writer.selector);
12451
+ targets.set(key, [...targets.get(key) ?? [], writer]);
12452
+ }
12453
+ 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) }));
12454
+ const overlappingagents = new Set(overlaps.flatMap((entry) => entry.writers));
12455
+ return {
12456
+ id: input.id,
12457
+ writers: input.writers,
12458
+ overlaps,
12459
+ 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),
12460
+ clean: overlaps.length === 0,
12461
+ scannedat: input.now
12462
+ };
12463
+ }
12464
+ function mergeresults(input) {
12465
+ const keys = /* @__PURE__ */ new Map();
12466
+ for (const entry of input.entries) {
12467
+ keys.set(entry.key, [...keys.get(entry.key) ?? [], entry]);
12468
+ }
12469
+ const conflicts = [];
12470
+ const merged = [];
12471
+ for (const [key, entries] of keys) {
12472
+ const ordered = [...entries].sort((one, two) => one.mergedat - two.mergedat);
12473
+ if (ordered.length === 1) {
12474
+ merged.push(ordered[0]);
12475
+ continue;
12476
+ }
12477
+ if (input.rule === "fail") {
12478
+ conflicts.push(`The key ${key} carries ${ordered.length} parallel values from ${ordered.map((entry) => entry.agentid).join(", ")}; the fail rule refuses the fold.`);
12479
+ continue;
12480
+ }
12481
+ 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];
12482
+ 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(", ")}.`;
12483
+ conflicts.push(`The key ${key}: ${note}`);
12484
+ merged.push({ ...winner, id: `${winner.id}:merged`, conflict: note });
12485
+ }
12486
+ return { entries: merged, conflicts, refused: input.rule === "fail" && conflicts.length > 0 };
12487
+ }
12488
+ function swarmreport(input) {
12489
+ if (input.title.trim() === "") throw new Error("The report needs its title.");
12490
+ const fold = mergeresults({ entries: input.outputs, rule: input.rule, ...input.preferagent !== void 0 ? { preferagent: input.preferagent } : {}, now: input.now });
12491
+ const groups = /* @__PURE__ */ new Map();
12492
+ for (const entry of fold.entries) {
12493
+ const group = entry.taskid ?? "general";
12494
+ groups.set(group, [...groups.get(group) ?? [], entry]);
12495
+ }
12496
+ 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))] }));
12497
+ return {
12498
+ 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 },
12499
+ conflicts: fold.conflicts,
12500
+ refused: fold.refused
12501
+ };
12502
+ }
12503
+ function compareoutputs(input) {
12504
+ if (input.subject.trim() === "") throw new Error("The comparison needs its subject.");
12505
+ if (input.outputs.length < 2) throw new Error("The comparison contrasts at least two competing outputs.");
12506
+ 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}.`);
12507
+ return { id: input.id, subject: input.subject, outputs: input.outputs, differences, comparedat: input.now };
12508
+ }
12509
+ function interleavetimeline(actions) {
12510
+ return [...actions].sort((one, two) => one.at - two.at || (one.id < two.id ? -1 : 1));
12511
+ }
12512
+ function sharelesson(input) {
12513
+ if (input.statement.trim() === "") throw new Error("The lesson needs its statement in plain language.");
12514
+ if (input.verifiedby.trim() === "") throw new Error("The lesson needs its verifier; only a verified lesson lands on the board.");
12515
+ 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 });
12516
+ }
12517
+ function swarmcosts(input) {
12518
+ return {
12519
+ agents: input.usage.length,
12520
+ tokens: input.usage.reduce((total, usage) => total + usage.tokens, 0),
12521
+ cost: input.usage.reduce((total, usage) => total + usage.cost, 0),
12522
+ steps: input.usage.reduce((total, usage) => total + usage.steps, 0),
12523
+ ...input.currency !== void 0 && input.currency.trim() !== "" ? { currency: input.currency } : {},
12524
+ computedat: input.now
12525
+ };
12526
+ }
12527
+ function replayagentrun(input) {
12528
+ 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 } : {} })));
12529
+ }
12530
+
11520
12531
  // taskqueue.ts
11521
12532
  function emptyqueue(input = {}) {
11522
12533
  return { lanes: input.lanes ?? [], priorities: input.priorities ?? [], completionpolicy: input.completionpolicy ?? "all", items: [], claims: [] };
@@ -11612,58 +12623,6 @@ function taskcounts(queue) {
11612
12623
  };
11613
12624
  }
11614
12625
 
11615
- // blackboard.ts
11616
- var blackboardsections = ["goals", "facts", "findings", "scratch"];
11617
- function emptyboard(sections) {
11618
- return { sections: sections ?? blackboardsections, entries: [] };
11619
- }
11620
- function postentry(input) {
11621
- if (input.key.trim() === "") throw new Error("The blackboard entry needs its key.");
11622
- if (input.value.trim() === "") throw new Error("The blackboard entry needs its value.");
11623
- if (input.author.trim() === "") throw new Error("The blackboard entry needs its author.");
11624
- if (!blackboardsections.includes(input.section)) throw new Error(`The section ${input.section} is not one of the shared blackboard sections.`);
11625
- if (input.board.entries.some((entry2) => entry2.id === input.id)) throw new Error(`The blackboard entry id ${input.id} already exists.`);
11626
- if (input.valuekind === "json") {
11627
- try {
11628
- JSON.parse(input.value);
11629
- } catch {
11630
- throw new Error("The json blackboard entry needs a well-formed json value.");
11631
- }
11632
- }
11633
- const entry = { id: input.id, key: input.key.trim(), valuekind: input.valuekind ?? "text", value: input.value, author: input.author, section: input.section, consentclass: input.consentclass ?? "read", postedat: input.now };
11634
- return { ...input.board, sections: input.board.sections.includes(input.section) ? input.board.sections : [...input.board.sections, input.section], entries: [entry, ...input.board.entries] };
11635
- }
11636
- function entryfresh(entry, now, window2) {
11637
- if (entry.retiredat !== void 0) return false;
11638
- if (window2 === void 0) return true;
11639
- return now - entry.postedat <= window2;
11640
- }
11641
- function readentries(input) {
11642
- return input.board.entries.filter((entry) => entry.retiredat === void 0).filter((entry) => input.section === void 0 || entry.section === input.section).filter((entry) => entryfresh(entry, input.now, input.freshness)).sort((one, two) => two.postedat - one.postedat);
11643
- }
11644
- function retireentries(input) {
11645
- if (input.board.retirementwindow === void 0) return { board: input.board, retired: [] };
11646
- const stale = input.board.entries.filter((entry) => entry.retiredat === void 0 && input.now - entry.postedat > input.board.retirementwindow);
11647
- if (stale.length === 0) return { board: input.board, retired: [] };
11648
- const staleids = new Set(stale.map((entry) => entry.id));
11649
- return {
11650
- board: { ...input.board, entries: input.board.entries.map((entry) => staleids.has(entry.id) ? { ...entry, retiredat: input.now } : entry) },
11651
- retired: [...staleids]
11652
- };
11653
- }
11654
- function retireentry(input) {
11655
- const entry = input.board.entries.find((candidate) => candidate.id === input.entryid);
11656
- if (!entry) throw new Error(`The blackboard entry ${input.entryid} does not exist.`);
11657
- if (entry.retiredat !== void 0) throw new Error(`The blackboard entry ${entry.key} is already retired.`);
11658
- return { ...input.board, entries: input.board.entries.map((candidate) => candidate.id === input.entryid ? { ...candidate, retiredat: input.now } : candidate) };
11659
- }
11660
- function boardsummary(board, now) {
11661
- return board.sections.map((section) => {
11662
- const live = board.entries.filter((entry) => entry.section === section && entry.retiredat === void 0);
11663
- return { section, entries: live.length, authors: [...new Set(live.map((entry) => entry.author))], ...live.length > 0 ? { freshestat: Math.max(...live.map((entry) => entry.postedat)) } : {} };
11664
- });
11665
- }
11666
-
11667
12626
  // agentmailbox.ts
11668
12627
  function mailboxof(mailboxes, agentid) {
11669
12628
  return mailboxes.find((mailbox) => mailbox.agentid === agentid) ?? { agentid, inbox: [], outbox: [], unread: 0 };
@@ -11706,6 +12665,41 @@ function unreadcount(mailboxes, agentid) {
11706
12665
  return mailboxof(mailboxes, agentid).unread;
11707
12666
  }
11708
12667
 
12668
+ // sandboxframe.ts
12669
+ function stripscripts(markup) {
12670
+ 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();
12671
+ }
12672
+ function nonceof(seed) {
12673
+ const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
12674
+ let hash = 0;
12675
+ for (let index = 0; index < seed.length; index += 1) hash = hash * 31 + seed.charCodeAt(index) >>> 0;
12676
+ let nonce = "";
12677
+ let state = hash === 0 ? 2654435769 : hash;
12678
+ for (let index = 0; index < 16; index += 1) {
12679
+ state = state * 1664525 + 1013904223 >>> 0;
12680
+ nonce += alphabet[state % alphabet.length];
12681
+ }
12682
+ return nonce;
12683
+ }
12684
+ function sandboxrenderof(input) {
12685
+ if (input.markup.trim() === "") throw new Error("The sandbox render needs its untrusted markup.");
12686
+ if (input.sourceorigin.trim() === "") throw new Error("The sandbox render needs the source origin of its untrusted markup.");
12687
+ if (input.stepid.trim() === "") throw new Error("The sandbox render names the reviewed step it renders for.");
12688
+ return { id: input.id, nonce: nonceof(`${input.id}:${input.now}`), markup: stripscripts(input.markup), sourceorigin: input.sourceorigin, stepid: input.stepid, renderedat: input.now };
12689
+ }
12690
+ function rendermessage(render) {
12691
+ return { channel: "devthinksandbox", type: "render", nonce: render.nonce, markup: render.markup };
12692
+ }
12693
+ function acceptrenderresult(input) {
12694
+ if (input.message.channel !== "devthinksandbox") return { accepted: false, reason: "The sandbox message travels the devthinksandbox channel only." };
12695
+ if (input.message.type !== "renderresult") return { accepted: false, reason: "The sandbox message answers with the renderresult type only." };
12696
+ const render = input.renders.find((entry) => entry.nonce === input.message.nonce && entry.renderedat <= input.now);
12697
+ if (!render) return { accepted: false, reason: "The sandbox message carries no nonce of a known render; a stale or replayed message never passes." };
12698
+ const text2 = (input.message.text ?? "").replace(/<[^>]*>/g, "");
12699
+ 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 };
12700
+ return { accepted: true, result, reason: `The render result of the step ${render.stepid} answers the nonce of its render; the text stays inside the frame.` };
12701
+ }
12702
+
11709
12703
  // modelroute.ts
11710
12704
  function routevalid(route) {
11711
12705
  if (route.kind.trim() === "") return { allowed: false, reason: "The model route needs its task kind." };
@@ -13011,6 +14005,260 @@ async function dispatchpagestep(step, tabid2, origin, plan) {
13011
14005
  await harvestdialogs(session, activeplan, step.id, tabid2);
13012
14006
  return result[0]?.result;
13013
14007
  }
14008
+ var roadmapheartbeat = 3e4;
14009
+ var roadmapzombieintervals = 3;
14010
+ var runstateprofile = "default";
14011
+ var keepaliveport;
14012
+ var workerpoolcounts = /* @__PURE__ */ new Map();
14013
+ async function offscreengranted() {
14014
+ try {
14015
+ return await chrome.permissions.contains({ permissions: ["offscreen"] });
14016
+ } catch {
14017
+ return false;
14018
+ }
14019
+ }
14020
+ async function offscreendeclaration() {
14021
+ try {
14022
+ const response = await fetch(chrome.runtime.getURL("manifest.json"));
14023
+ const manifest = await response.json();
14024
+ if (!manifest.offscreen?.document || !manifest.offscreen.reasons?.length || !manifest.offscreen.justification?.trim()) return void 0;
14025
+ return { document: manifest.offscreen.document, reasons: manifest.offscreen.reasons, justification: manifest.offscreen.justification };
14026
+ } catch {
14027
+ return void 0;
14028
+ }
14029
+ }
14030
+ async function requestoffscreengrant() {
14031
+ try {
14032
+ return await chrome.permissions.request({ permissions: ["offscreen"] });
14033
+ } catch {
14034
+ return false;
14035
+ }
14036
+ }
14037
+ async function ensureoffscreendocument(runid) {
14038
+ const offscreen = chrome.offscreen;
14039
+ if (!offscreen) return false;
14040
+ const declaration = await offscreendeclaration();
14041
+ if (!declaration) return false;
14042
+ const opened = openoffscreen({ registry: await memory.getoffscreenentries(), document: declaration.document, runid, reasons: declaration.reasons, justification: declaration.justification, now: Date.now() });
14043
+ if (!opened.reused) {
14044
+ await memory.addoffscreenentry(opened.entry);
14045
+ await audit("worker", `The offscreen document ${declaration.document} spawned for the run ${runid} under the reasons ${declaration.reasons.join(", ")} with the reviewed justification.`, {});
14046
+ }
14047
+ try {
14048
+ if (!await offscreen.hasDocument()) await offscreen.createDocument({ url: chrome.runtime.getURL(declaration.document), reasons: declaration.reasons, justification: declaration.justification });
14049
+ return true;
14050
+ } catch {
14051
+ return false;
14052
+ }
14053
+ }
14054
+ async function closeoffscreendocument(runid) {
14055
+ const offscreen = chrome.offscreen;
14056
+ const outcome = closeoffscreen(await memory.getoffscreenentries(), runid, Date.now());
14057
+ if (!outcome.closed) return;
14058
+ const entry = outcome.registry.find((candidate) => candidate.runid === runid && candidate.closedat !== void 0);
14059
+ if (entry) await memory.updateoffscreenentry(entry);
14060
+ try {
14061
+ if (offscreen && await offscreen.hasDocument()) await offscreen.closeDocument();
14062
+ } catch {
14063
+ }
14064
+ await audit("worker", `The offscreen document of the run ${runid} closed with its worker pool; the registry keeps the reasons and justification of the spawn for the audit.`, {});
14065
+ }
14066
+ function startkeepaliveport(runid) {
14067
+ if (keepaliveport) return;
14068
+ try {
14069
+ keepaliveport = chrome.runtime.connect({ name: `devthinkkeepalive:${runid}` });
14070
+ keepaliveport.onDisconnect.addListener(() => {
14071
+ keepaliveport = void 0;
14072
+ });
14073
+ } catch {
14074
+ }
14075
+ }
14076
+ async function openplanrun(session, plan) {
14077
+ const settings = await memory.getsettings();
14078
+ const interval = settings?.keepaliveinterval ?? roadmapheartbeat;
14079
+ const gate = keepalivegate({ session, plan, now: Date.now() });
14080
+ if (!gate.allowed) return;
14081
+ const existing = await memory.getrunstate(runstateprofile);
14082
+ if (existing && existing.keepalive.state === "active") return;
14083
+ const state = openrun({ runid: plan.id, sessionid: session.id, planid: plan.id, profileid: runstateprofile, interval, now: Date.now() });
14084
+ await memory.setrunstate(runstateprofile, state);
14085
+ const lock = acquirerunlock({ locks: await memory.getrunlocks(), sessionid: session.id, runid: plan.id, holder: "planrun", now: Date.now() });
14086
+ if (lock.acquired) await memory.setrunlocks(lock.locks);
14087
+ startkeepaliveport(plan.id);
14088
+ await audit("environment", `The run state of the plan ${plan.id} opened with its keepalive port beating every ${interval} milliseconds${lock.acquired ? " and the session locked against concurrent runs" : ""}.`, { sessionid: session.id, planid: plan.id });
14089
+ }
14090
+ async function markpendingstep(plan, stepid) {
14091
+ const state = await memory.getrunstate(runstateprofile);
14092
+ if (!state || state.runid !== plan.id || state.keepalive.state !== "active") return;
14093
+ await memory.setrunstate(runstateprofile, markpending(state, stepid, Date.now()));
14094
+ }
14095
+ async function recordstepenvironment(step, environment, session, plan, origin, turnaround) {
14096
+ if (session && plan) {
14097
+ await memory.setprogress(recordenvironment2(await memory.getprogress(), plan.id, step.id, environment, Date.now()));
14098
+ const state = await memory.getrunstate(runstateprofile);
14099
+ if (state && state.runid === plan.id) {
14100
+ let next = recordenvironment(state, { stepid: step.id, environment, origin, now: Date.now() });
14101
+ if (turnaround !== void 0) next = recordturnaround(next, { stepid: step.id, milliseconds: turnaround, now: Date.now() });
14102
+ if (["navigate", "openlink", "followlink", "back", "forward"].includes(step.kind) && step.value) next = recordurl(next, { url: step.value, stepid: step.id, now: Date.now() });
14103
+ await memory.setrunstate(runstateprofile, next);
14104
+ }
14105
+ }
14106
+ if (plan && turnaround !== void 0) await memory.setprogress(recordturnaround2(await memory.getprogress(), plan.id, step.id, turnaround, Date.now()));
14107
+ }
14108
+ async function closeplanrun(planid, sessionid) {
14109
+ const state = await memory.getrunstate(runstateprofile);
14110
+ if (!state || state.runid !== planid || state.keepalive.state !== "active") return;
14111
+ await memory.setrunstate(runstateprofile, closerun(state, Date.now()));
14112
+ const release = releaserunlock({ locks: await memory.getrunlocks(), sessionid, runid: planid, now: Date.now() });
14113
+ if (release.released) await memory.setrunlocks(release.locks);
14114
+ try {
14115
+ keepaliveport?.disconnect();
14116
+ } catch {
14117
+ }
14118
+ keepaliveport = void 0;
14119
+ await closeoffscreendocument(planid);
14120
+ await audit("environment", `The run state of the plan ${planid} closed at its terminal state and the keepalive port released.`, { ...sessionid !== "" ? { sessionid } : {}, planid });
14121
+ }
14122
+ async function executeisolatedevaluate(step, tabid2, origin) {
14123
+ const injection = isolatedinjection(step);
14124
+ const result = await chrome.scripting.executeScript({ target: { tabId: tabid2 }, world: "ISOLATED", func: (code, args, expectedorigin) => {
14125
+ if (location.origin !== expectedorigin) return { ok: false, summary: "Page origin changed before the evaluate step ran." };
14126
+ try {
14127
+ let outcome;
14128
+ try {
14129
+ outcome = new Function(`"use strict"; return (${code});`)(...args);
14130
+ } catch {
14131
+ outcome = new Function(`"use strict"; ${code}`)(...args);
14132
+ }
14133
+ return { ok: true, summary: `Reviewed expression returned ${outcome === void 0 ? "no value" : "a value"} inside the isolated world.`, details: { result: String(outcome) } };
14134
+ } catch (error) {
14135
+ return { ok: false, summary: `Reviewed expression failed inside the isolated world: ${error instanceof Error ? error.message : String(error)}` };
14136
+ }
14137
+ }, args: [injection.code, injection.args, origin] });
14138
+ return result[0]?.result ?? { ok: false, summary: "The isolated world returned no result." };
14139
+ }
14140
+ async function executesandboxrender(step, session, plan, origin) {
14141
+ const options = stepoptions2(step);
14142
+ const markup = typeof options.markup === "string" ? options.markup : "";
14143
+ const sourceorigin = typeof options.sourceorigin === "string" ? options.sourceorigin : origin;
14144
+ const settings = await memory.getsettings();
14145
+ const origingate = sandboxorigingate({ origin: sourceorigin, allowed: settings?.sandboxorigins ?? [] });
14146
+ if (!origingate.allowed) throw new Error(origingate.reason);
14147
+ const render = sandboxrenderof({ id: randomid(), markup, sourceorigin, stepid: step.id, now: Date.now() });
14148
+ await memory.addsandboxrender(render);
14149
+ const hostready = await ensureoffscreendocument(plan?.id ?? step.id);
14150
+ let answer;
14151
+ if (hostready) {
14152
+ try {
14153
+ answer = await chrome.runtime.sendMessage({ kind: "offscreen", action: "sandboxrender", render: { id: render.id, nonce: render.nonce, markup: render.markup, sourceorigin: render.sourceorigin, stepid: render.stepid } });
14154
+ } catch {
14155
+ }
14156
+ }
14157
+ if (!answer) {
14158
+ try {
14159
+ answer = await chrome.runtime.sendMessage({ kind: "environments", action: "sandboxhost", render: rendermessage(render) });
14160
+ } catch {
14161
+ }
14162
+ }
14163
+ if (!answer) throw new Error("The sandbox frame needs an open host surface: grant the offscreen capability or keep the review panel open so the sandboxed page can render the untrusted markup.");
14164
+ const accepted = acceptrenderresult({ renders: [render], message: { channel: "devthinksandbox", type: "renderresult", nonce: render.nonce, ok: answer.ok, ...answer.text !== void 0 ? { text: answer.text } : {}, summary: answer.summary }, now: Date.now() });
14165
+ if (!accepted.accepted || !accepted.result) throw new Error(accepted.reason);
14166
+ await audit("sandbox", `The step ${step.id} rendered untrusted markup from ${sourceorigin} inside the sandbox frame under the nonce ${render.nonce}; scripts and handlers were stripped before the render and the text never reentered the dom outside the frame.`, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {}, stepid: step.id });
14167
+ await recordstepenvironment(step, "sandboxframe", session, plan, sourceorigin, void 0);
14168
+ return { ok: accepted.result.ok, summary: accepted.result.summary, details: { text: accepted.result.text, nonce: render.nonce, sourceorigin } };
14169
+ }
14170
+ async function offloadparsetoworker(step, output, session, plan, origin) {
14171
+ const runid = plan?.id ?? step.id;
14172
+ const ready = await ensureoffscreendocument(runid);
14173
+ if (!ready) return { output, turnaround: void 0 };
14174
+ const payload = JSON.stringify({ summary: output?.summary ?? "", details: output?.details ?? {} });
14175
+ const request = workerrequestof({ id: randomid(), runid, stepid: step.id, kind: step.kind, payload, options: stepoptions2(step), sentat: Date.now() });
14176
+ const started = Date.now();
14177
+ let answer;
14178
+ try {
14179
+ answer = await chrome.runtime.sendMessage({ kind: "offscreen", action: "parse", request: { id: request.id, runid: request.runid, stepid: request.stepid, task: request.task, payload: request.payload, transferables: request.transferables } });
14180
+ } catch {
14181
+ }
14182
+ const turnaround = Date.now() - started;
14183
+ if (!answer) return { output, turnaround: void 0 };
14184
+ const provenance = { origin, stepid: step.id, environment: "offscreenworker" };
14185
+ await memory.addworkerevent({ id: randomid(), runid, kind: "spawn", workers: workerpoolcounts.get(runid) ?? 1, reason: `The ${request.task} parse of the step ${step.id} ran inside the offscreen worker pool with the transferable keys ${request.transferables.length > 0 ? request.transferables.join(", ") : "none"}.`, provenance, at: Date.now() });
14186
+ await audit("worker", `The ${request.task} parse of the step ${step.id} ran inside the offscreen worker pool and answered in ${turnaround} milliseconds${answer.ok === false ? " with a refusal the inline parse covers" : ""}.`, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {}, stepid: step.id });
14187
+ const parsed = { ok: answer.ok !== false && (output?.ok ?? true), summary: answer.summary ?? output?.summary ?? "The offscreen worker answered the parse.", details: { ...output?.details ?? {}, workerparse: answer.result ?? "", task: request.task, turnaround } };
14188
+ return { output: parsed, turnaround };
14189
+ }
14190
+ async function environmentviewof() {
14191
+ const session = await memory.getsession();
14192
+ const settings = await memory.getsettings();
14193
+ const states = await memory.listrunstates();
14194
+ const swept = zombiesweep({ states, now: Date.now(), interval: settings?.keepaliveinterval ?? roadmapheartbeat, missedlimit: settings?.zombieintervals ?? roadmapzombieintervals });
14195
+ const open = states.find((state) => state.keepalive.state === "active") ?? states[0];
14196
+ const workers = (await chrome.runtime.sendMessage({ kind: "offscreen", action: "pool" }).catch(() => void 0))?.workers ?? 0;
14197
+ return {
14198
+ report: environmentreport({ environments: open?.environments ?? {}, turnarounds: open?.turnarounds ?? {}, offscreen: await memory.getoffscreenentries(), workers, ...open !== void 0 ? { keepalive: { runid: open.keepalive.runid, state: open.keepalive.state, beats: open.keepalive.beats, lastbeatat: open.keepalive.lastbeatat, portopen: open.keepalive.portopen } } : {} }),
14199
+ ...session?.environmentgrants !== void 0 ? { grants: session.environmentgrants } : {},
14200
+ requirements: environmentrequirements(),
14201
+ registry: executorregistry(),
14202
+ offscreengranted: await offscreengranted(),
14203
+ parseoffload: settings?.parseoffload === true,
14204
+ ...settings?.workerpoolsize !== void 0 ? { workerpoolsize: settings.workerpoolsize } : {},
14205
+ ...settings?.sandboxorigins !== void 0 ? { sandboxorigins: settings.sandboxorigins } : {},
14206
+ workerevents: (await memory.getworkerevents()).map((event) => ({ id: event.id, runid: event.runid, kind: event.kind, workers: event.workers, reason: event.reason, at: event.at })),
14207
+ runstates: states.map((state) => ({ runid: state.runid, sessionid: state.sessionid, state: state.state, beats: state.keepalive.beats, lastbeatat: state.keepalive.lastbeatat, ...state.pendingstepid !== void 0 ? { pendingstepid: state.pendingstepid } : {}, urls: state.urlhistory.length })),
14208
+ locks: await memory.getrunlocks(),
14209
+ zombies: swept.reaped,
14210
+ urls: (open?.urlhistory ?? []).slice(-50),
14211
+ ...open !== void 0 ? { recovery: recoveryplan(open) } : {},
14212
+ ...open !== void 0 && open.state === "recovered" ? { restartnotice: `The service worker restarted and the run ${open.runid} reattached from its persisted run state; the pending step ${open.pendingstepid ?? "none"} waits for the user.` } : {}
14213
+ };
14214
+ }
14215
+ async function keepalivetick() {
14216
+ const settings = await memory.getsettings();
14217
+ const interval = settings?.keepaliveinterval ?? roadmapheartbeat;
14218
+ const tolerance = settings?.zombieintervals ?? roadmapzombieintervals;
14219
+ for (const state of await memory.listrunstates()) {
14220
+ if (state.keepalive.state !== "active") continue;
14221
+ await memory.setrunstate(state.profileid, beatrun(state, Date.now()));
14222
+ startkeepaliveport(state.runid);
14223
+ }
14224
+ const swept = zombiesweep({ states: await memory.listrunstates(), now: Date.now(), interval, missedlimit: tolerance });
14225
+ for (const runid of swept.reaped) {
14226
+ await closeoffscreendocument(runid);
14227
+ await audit("environment", `The zombie reaper closed the run ${runid} whose heartbeat fell silent past the ${tolerance} tolerated interval${tolerance === 1 ? "" : "s"}; the popup offers the reap action.`, {});
14228
+ }
14229
+ for (const state of swept.states) await memory.setrunstate(state.profileid, state);
14230
+ await memory.expirerunstates(settings?.runstateretention, Date.now());
14231
+ const locks = expirerunlocks(await memory.getrunlocks(), Date.now());
14232
+ if (locks.expired.length > 0) await memory.setrunlocks(locks.locks);
14233
+ try {
14234
+ const estimate = await navigator.storage.estimate();
14235
+ if (estimate.usage !== void 0) await memory.trackrunstatequota(estimate.usage);
14236
+ if (settings?.runstatebytes !== void 0) {
14237
+ const quota = await memory.getrunstatequota();
14238
+ if (quota && quota.used > settings.runstatebytes) {
14239
+ const pruned = prunerunstates({ states: await memory.listrunstates(), used: quota.used, ceiling: settings.runstatebytes });
14240
+ for (const runid of pruned.pruned) {
14241
+ const state = await memory.listrunstates();
14242
+ const target = state.find((entry) => entry.runid === runid);
14243
+ if (target) await memory.removerunstate(target.profileid);
14244
+ }
14245
+ if (pruned.pruned.length > 0) await audit("environment", pruned.reason, {});
14246
+ }
14247
+ }
14248
+ } catch {
14249
+ }
14250
+ }
14251
+ async function restorerunstates() {
14252
+ const settings = await memory.getsettings();
14253
+ const states = await memory.listrunstates();
14254
+ for (const state of states) {
14255
+ if (state.keepalive.state !== "active") continue;
14256
+ await memory.setrunstate(state.profileid, reattachrun(state, Date.now()));
14257
+ startkeepaliveport(state.runid);
14258
+ await audit("environment", `The service worker restarted and the run ${state.runid} reattached its keepalive port from the persisted run state; ${recoveryplan(state).reason}`, {});
14259
+ }
14260
+ void settings;
14261
+ }
13014
14262
  async function startsession() {
13015
14263
  const { tab, origin } = await activecontext();
13016
14264
  const session = { id: randomid(), tabid: tab.id, origin, startedat: Date.now(), expiresat: Date.now() + sessionduration, grants: [origin] };
@@ -17889,6 +19137,18 @@ async function executestep(stepid) {
17889
19137
  async function executeaction(step, session, plan, tabid2, origin, settings, verdicts, mode) {
17890
19138
  const gate = canexecute({ session, plan, step, tabid: tabid2, origin, ...verdicts !== void 0 && verdicts.length > 0 ? { verdicts } : {}, ...settings !== void 0 ? { settings } : {} });
17891
19139
  if (!gate.allowed) throw new Error(gate.reason);
19140
+ const environmentverdict = stepenvironmentvalid(step);
19141
+ if (!environmentverdict.allowed) throw new Error(environmentverdict.reason);
19142
+ const environmentgrantverdict = environmentgrantgate(step, session?.environmentgrants);
19143
+ if (!environmentgrantverdict.allowed) throw new Error(environmentgrantverdict.reason);
19144
+ const offgranted = await offscreengranted();
19145
+ const routing = routeenvironment(step, { offload: settings?.parseoffload === true, granted: offgranted });
19146
+ const capabilityverdict = offscreencapabilitygate({ environment: routing.environment, granted: offgranted });
19147
+ if (!capabilityverdict.allowed) throw new Error(capabilityverdict.reason);
19148
+ if (routing.fallback && plan) await audit("environment", `The ${step.kind} step ${step.id} fell back to inline parsing inside the page because the offscreen capability grant stays absent.`, { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid: step.id });
19149
+ if (session && plan && plan.state === "approved" && mode === "plan") await openplanrun(session, plan);
19150
+ if (plan && plan.state === "approved") await markpendingstep(plan, step.id);
19151
+ if (routing.environment === "sandboxframe") return executesandboxrender(step, session, plan, origin);
17892
19152
  const capability = requiredcapability(step.kind);
17893
19153
  if (capability) {
17894
19154
  const granted = await chrome.permissions.contains({ permissions: [capability] });
@@ -17976,8 +19236,12 @@ async function executeaction(step, session, plan, tabid2, origin, settings, verd
17976
19236
  return output;
17977
19237
  };
17978
19238
  const runplan = plan;
19239
+ const isolatedresult = routing.environment === "isolatedworld" ? await executeisolatedevaluate(step, tabid2, origin) : void 0;
19240
+ let workerturnaround;
17979
19241
  const capturepolicystate = await runcapturepolicy();
17980
- if (capturepolicystate === "beforeafter" && session && step.risk !== "read" && beforeafterwrapallowed(step.kind)) {
19242
+ if (isolatedresult !== void 0) {
19243
+ output = isolatedresult;
19244
+ } else if (capturepolicystate === "beforeafter" && session && step.risk !== "read" && beforeafterwrapallowed(step.kind)) {
17981
19245
  const before = await grabstateshot(step, session, runplan, tabid2, "before");
17982
19246
  output = await dispatchreviewedstep();
17983
19247
  if (output?.ok) {
@@ -17993,8 +19257,14 @@ async function executeaction(step, session, plan, tabid2, origin, settings, verd
17993
19257
  } else {
17994
19258
  output = await dispatchreviewedstep();
17995
19259
  }
19260
+ if (routing.environment === "offscreenworker" && workerturnaround === void 0) {
19261
+ const offloaded = await offloadparsetoworker(step, output, session, plan, origin);
19262
+ output = offloaded.output ?? output;
19263
+ workerturnaround = offloaded.turnaround;
19264
+ }
17996
19265
  if (["navigate", "back", "forward"].includes(step.kind)) await recordnavigation(step, session, tabid2);
17997
19266
  await recordevidence(step, output, session, plan, origin);
19267
+ await recordstepenvironment(step, routing.environment, session, plan, origin, workerturnaround);
17998
19268
  if (output?.ok && plan && typeof output.details?.tabid === "number") {
17999
19269
  await memory.setprogress(assigntasktab(await memory.getprogress(), plan.id, output.details.tabid, Date.now()));
18000
19270
  }
@@ -18003,7 +19273,7 @@ async function executeaction(step, session, plan, tabid2, origin, settings, verd
18003
19273
  if (resolved) {
18004
19274
  await memory.addresolution({ stepid: step.id, mode: resolved.mode, selector: resolved.selector, label: resolved.label, at: Date.now() });
18005
19275
  }
18006
- const outcome = { stepid: step.id, ok: Boolean(output?.ok), summary, ...output?.details ? { details: output.details } : {}, at: Date.now() };
19276
+ const outcome = { stepid: step.id, ok: Boolean(output?.ok), summary, environment: routing.environment, ...output?.details ? { details: output.details } : {}, at: Date.now() };
18007
19277
  const auditkind = stepauditkind(step, Boolean(output?.ok));
18008
19278
  await audit(auditkind, summary, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {}, stepid: step.id });
18009
19279
  await memory.addoutcome(outcome);
@@ -18035,6 +19305,7 @@ async function executeaction(step, session, plan, tabid2, origin, settings, verd
18035
19305
  });
18036
19306
  const done = { ...plan, state: "completed", completedat: Date.now() };
18037
19307
  await memory.setplan(done);
19308
+ await closeplanrun(done.id, session?.id ?? "");
18038
19309
  await audit("complete", "Every reviewed step of the approved plan has executed.", { ...session ? { sessionid: session.id } : {}, planid: done.id });
18039
19310
  }
18040
19311
  }
@@ -18220,7 +19491,7 @@ async function handlerequest(message, sender) {
18220
19491
  const livetab = session ? await chrome.tabs.get(session.tabid).catch(() => void 0) : void 0;
18221
19492
  const waitprofile = session ? waitprofiles.find((record2) => record2.origin === session.origin) : void 0;
18222
19493
  const livestate = { phase: livetab?.status === "loading" ? "loading" : "complete", ...navrecords[0] ? { finalurl: navrecords[0].finalurl, redirects: navrecords[0].chain } : {} };
18223
- return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), cdpsessions: await memory.getcdpsessions(), cdpcommands: await memory.getcdpcommands(), cdpeventrules: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watchexpressions: await memory.getwatchexpressions(), scriptoverrides: await memory.getscriptoverrides(), debuggergrants: await memory.getdebuggergrants(), pauseretention: runsettings?.pauseretention, breakpointceiling: runsettings?.breakpointceiling, cdpattached: [...activecdpsessions.values()].filter((active) => active.session.detachedat === void 0).length, profileretention: runsettings?.profileretention, traceceiling: runsettings?.traceceiling, profile: profilereport({ flows: await memory.getflowmetrics(), heaps: await memory.getheaprecords(), samples: await memory.getgrowsamples(), trends: await memory.gettrends(), profiles: await memory.getcpuprofiles(), shifts: await memory.getshiftentries(), traces: await memory.gettracerecords(), sourcemaps: await memory.getsourcemaps(), consents: await memory.getsourcemapconsents() }), profileactive: activememorytrackers.size + activeprofiletargets.size, profiletargets: [...activeprofiletargets.values()].flatMap((entry) => entry.targets), socketsactive: activesockets.size, emulation: emulationreport({ ...plan && await loademulationstate(plan.id) !== void 0 ? { state: await loademulationstate(plan.id) } : {}, devices: await memory.getdevicepresets(), networks: await memory.getnetworkpresets(), locations: await memory.getlocationpresets(), agents: await memory.getagentpresets(), blackbox: await memory.getblackboxrules(), permissions: await memory.getpermissionoverrides(), consents: await memory.getlocationconsents() }), emulatedlayers: plan ? layernames(await loademulationstate(plan.id)) : [], emulationretention: runsettings?.emulationretention, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), sessionmemory: sessionreport({ records: sessionrecords, events: await memory.getsessionevents(), folders: await memory.getsessionfolders(), diffs: await memory.getsessiondiffs(), ...autosnapshot !== void 0 ? { auto: autosnapshot.interval } : {}, ...crashed ? { crashed: true } : {} }), autosnapshotstate: autosnapshot, sessionretention: runsettings?.sessionretention, trigger: triggerlist({ rules: await memory.gettriggerules(), workflows: await memory.listworkflows(), queue: await memory.gettriggerqueue() }), triggerretention: runsettings?.triggerretention, workflow: workflowreport({ workflows: await memory.listworkflows(), runs: await memory.listworkflowruns(), templates: await memory.getsteptemplates(), ...newestworkflowrun !== void 0 ? { log: await memory.getrunlog(newestworkflowrun.id), scopes: await memory.getrunscopes(newestworkflowrun.id), provenance: await memory.getworkflowprovenance(newestworkflowrun.id), control: await memory.listcontroldecisions(newestworkflowrun.id) } : {} }), runlogretention: runsettings?.runlogretention, runhistoryretention: runsettings?.runhistoryretention, editor: editorstate({ versions: await memory.listworkflowversions(), diffs: await memory.listversiondiffs(), history: await memory.gethistory(), overrides: await memory.listsiteoverrides(), imports: (await memory.listworkflowimports()).map((entry) => ({ id: entry.id, workflowid: entry.record.id, name: entry.record.name, version: entry.record.version, steps: entry.record.steps.length, risk: entry.record.risk, importedat: entry.importedat, ...entry.filename !== void 0 ? { filename: entry.filename } : {} })), backgroundruns: await memory.getbackgroundruns(), watchdog: { ...runsettings?.watchdog !== void 0 ? { config: runsettings.watchdog } : {}, events: await memory.listwatchdogevents() } }), ...taskstate !== void 0 ? { taskstate } : {}, ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {}, mcp: await mcpstateof(), llm: await llmstateof(), swarm: await swarmstateof() };
19494
+ return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), cdpsessions: await memory.getcdpsessions(), cdpcommands: await memory.getcdpcommands(), cdpeventrules: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watchexpressions: await memory.getwatchexpressions(), scriptoverrides: await memory.getscriptoverrides(), debuggergrants: await memory.getdebuggergrants(), pauseretention: runsettings?.pauseretention, breakpointceiling: runsettings?.breakpointceiling, cdpattached: [...activecdpsessions.values()].filter((active) => active.session.detachedat === void 0).length, profileretention: runsettings?.profileretention, traceceiling: runsettings?.traceceiling, profile: profilereport({ flows: await memory.getflowmetrics(), heaps: await memory.getheaprecords(), samples: await memory.getgrowsamples(), trends: await memory.gettrends(), profiles: await memory.getcpuprofiles(), shifts: await memory.getshiftentries(), traces: await memory.gettracerecords(), sourcemaps: await memory.getsourcemaps(), consents: await memory.getsourcemapconsents() }), profileactive: activememorytrackers.size + activeprofiletargets.size, profiletargets: [...activeprofiletargets.values()].flatMap((entry) => entry.targets), socketsactive: activesockets.size, emulation: emulationreport({ ...plan && await loademulationstate(plan.id) !== void 0 ? { state: await loademulationstate(plan.id) } : {}, devices: await memory.getdevicepresets(), networks: await memory.getnetworkpresets(), locations: await memory.getlocationpresets(), agents: await memory.getagentpresets(), blackbox: await memory.getblackboxrules(), permissions: await memory.getpermissionoverrides(), consents: await memory.getlocationconsents() }), emulatedlayers: plan ? layernames(await loademulationstate(plan.id)) : [], emulationretention: runsettings?.emulationretention, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), sessionmemory: sessionreport({ records: sessionrecords, events: await memory.getsessionevents(), folders: await memory.getsessionfolders(), diffs: await memory.getsessiondiffs(), ...autosnapshot !== void 0 ? { auto: autosnapshot.interval } : {}, ...crashed ? { crashed: true } : {} }), autosnapshotstate: autosnapshot, sessionretention: runsettings?.sessionretention, trigger: triggerlist({ rules: await memory.gettriggerules(), workflows: await memory.listworkflows(), queue: await memory.gettriggerqueue() }), triggerretention: runsettings?.triggerretention, workflow: workflowreport({ workflows: await memory.listworkflows(), runs: await memory.listworkflowruns(), templates: await memory.getsteptemplates(), ...newestworkflowrun !== void 0 ? { log: await memory.getrunlog(newestworkflowrun.id), scopes: await memory.getrunscopes(newestworkflowrun.id), provenance: await memory.getworkflowprovenance(newestworkflowrun.id), control: await memory.listcontroldecisions(newestworkflowrun.id) } : {} }), runlogretention: runsettings?.runlogretention, runhistoryretention: runsettings?.runhistoryretention, editor: editorstate({ versions: await memory.listworkflowversions(), diffs: await memory.listversiondiffs(), history: await memory.gethistory(), overrides: await memory.listsiteoverrides(), imports: (await memory.listworkflowimports()).map((entry) => ({ id: entry.id, workflowid: entry.record.id, name: entry.record.name, version: entry.record.version, steps: entry.record.steps.length, risk: entry.record.risk, importedat: entry.importedat, ...entry.filename !== void 0 ? { filename: entry.filename } : {} })), backgroundruns: await memory.getbackgroundruns(), watchdog: { ...runsettings?.watchdog !== void 0 ? { config: runsettings.watchdog } : {}, events: await memory.listwatchdogevents() } }), ...taskstate !== void 0 ? { taskstate } : {}, ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {}, mcp: await mcpstateof(), llm: await llmstateof(), swarm: await swarmstateof(), environments: await environmentviewof() };
18224
19495
  }
18225
19496
  case "capabilities":
18226
19497
  return refreshcapabilities();
@@ -20820,6 +22091,7 @@ async function handlerequest(message, sender) {
20820
22091
  const context = agentruncontext({ agent, task: claimed.task, now });
20821
22092
  await memory.addagentevent(agenteventof({ id: randomid(), kind: "claimed", agentid, taskid: claimed.task.id, summary: `The agent ${agent.name} claimed the task ${claimed.task.payload} from the lane ${claimed.task.lane}; the run context ${context.run.id} reuses the workflow engine.`, now }));
20822
22093
  await audit("swarm", `The agent ${agent.name} claimed the highest priority task of the lane ${claimed.task.lane}; the per agent run context ${context.run.id} reuses the workflow engine and the proposal still passes the human review.`, {});
22094
+ await memory.addswarmaction({ id: randomid(), kind: "claim", agentid, summary: `The agent ${agent.name} claimed the task ${claimed.task.payload} from the lane ${claimed.task.lane}.`, at: now });
20823
22095
  }
20824
22096
  return swarmstateof();
20825
22097
  }
@@ -20835,6 +22107,7 @@ async function handlerequest(message, sender) {
20835
22107
  if (stolen.task !== void 0) {
20836
22108
  await memory.addagentevent(agenteventof({ id: randomid(), kind: "stole", agentid, taskid: stolen.task.id, summary: `The ${input2.role} agent ${agentid} stole the task ${stolen.task.payload} from the lane ${stolen.task.lane}; ${grade.reason ?? ""}`, now }));
20837
22109
  await audit("swarm", `The ${input2.role} agent ${agentid} stole a task from the lane ${stolen.task.lane} inside the approved swarm; the lane ownership rules the user configured held.`, {});
22110
+ await memory.addswarmaction({ id: randomid(), kind: "steal", agentid, summary: `The ${input2.role} agent ${agentid} stole the task ${stolen.task.payload} from the lane ${stolen.task.lane}.`, at: now });
20838
22111
  }
20839
22112
  return swarmstateof();
20840
22113
  }
@@ -20849,6 +22122,7 @@ async function handlerequest(message, sender) {
20849
22122
  await memory.setagentusage(nextusage);
20850
22123
  await memory.addagentevent(agenteventof({ id: randomid(), kind: "completed", taskid, ...holder !== void 0 ? { agentid: holder.agentid } : {}, summary: `The task ${completedtask?.payload ?? taskid} completed and its claim released.`, now }));
20851
22124
  await audit("swarm", `The task ${taskid} completed${holder !== void 0 ? ` under the agent ${holder.agentid}` : ""} and released its claim; the per agent usage counters moved with it.`, {});
22125
+ await memory.addswarmaction({ id: randomid(), kind: "complete", ...holder !== void 0 ? { agentid: holder.agentid } : {}, summary: `The task ${completedtask?.payload ?? taskid} completed and its claim released.`, at: now });
20852
22126
  return swarmstateof();
20853
22127
  }
20854
22128
  if (input2.cancel === true && input2.taskid !== void 0) {
@@ -20930,6 +22204,483 @@ async function handlerequest(message, sender) {
20930
22204
  }
20931
22205
  throw new Error("The swarm blackboard request carries no post, retire or sweep action.");
20932
22206
  }
22207
+ case "swarmleader": {
22208
+ const input2 = message;
22209
+ const now = Date.now();
22210
+ if (input2.elect !== void 0) {
22211
+ const agents = await memory.getagents();
22212
+ const rule = input2.elect.rule === "named" ? { kind: "named", agentid: input2.elect.agentid ?? "" } : { kind: "first" };
22213
+ const verdict = leaderelectionvalid({ rule, agents });
22214
+ if (!verdict.allowed) throw new Error(verdict.reason);
22215
+ const topology2 = electleader({ agents, id: randomid(), rule, now });
22216
+ await memory.settopology(topology2);
22217
+ await memory.addswarmaction({ id: randomid(), kind: "elect", agentid: topology2.leaderid, summary: `The ${rule.kind} election rule the user configured elected the agent ${topology2.leaderid} leader of the swarm with ${topology2.workerids.length} workers, ${topology2.criticids.length} critics and ${topology2.verifierids.length} verifiers.`, at: now });
22218
+ await memory.addagentevent(agenteventof({ id: randomid(), kind: "assign", agentid: topology2.leaderid, summary: `The leader election picked ${topology2.leaderid} by the ${rule.kind} rule; the topology records the lanes.`, now }));
22219
+ await audit("swarm", `The user elected the agent ${topology2.leaderid} leader by the ${rule.kind} rule; the topology holds ${topology2.workerids.length} workers, ${topology2.criticids.length} critics and ${topology2.verifierids.length} verifiers, and the leader only organizes work that still passes the same review.`, {});
22220
+ return swarmstateof();
22221
+ }
22222
+ const topology = await memory.gettopology();
22223
+ if (input2.assign !== void 0) {
22224
+ if (!topology) throw new Error("The swarm holds no topology; the user elects a leader before the assignment.");
22225
+ const queue = await queueof();
22226
+ const wanted = input2.assign.taskids ?? [];
22227
+ const tasks = queue.items.filter((task) => wanted.length === 0 || wanted.includes(task.id));
22228
+ const next = assignwork({ topology, tasks, now });
22229
+ await memory.settopology(next);
22230
+ await memory.addswarmaction({ id: randomid(), kind: "assign", agentid: next.leaderid, summary: `The leader sliced ${next.assignments.length} task${next.assignments.length === 1 ? "" : "s"} across the ${next.workerids.length} workers of the topology.`, at: now });
22231
+ await audit("swarm", `The leader ${next.leaderid} assigned ${next.assignments.length} task slice${next.assignments.length === 1 ? "" : "s"} across the ${next.workerids.length} workers; every slice still passes the same plan review before anything executes.`, {});
22232
+ return swarmstateof();
22233
+ }
22234
+ if (input2.collect !== void 0) {
22235
+ if (!topology) throw new Error("The swarm holds no topology; the user elects a leader before the collection.");
22236
+ const gathered = collectresults({ topology, outputs: input2.collect.outputs ?? [] });
22237
+ await memory.addswarmaction({ id: randomid(), kind: "collect", agentid: topology.leaderid, summary: `The leader gathered ${gathered.gathered.length - gathered.missing.length} worker output${gathered.gathered.length - gathered.missing.length === 1 ? "" : "s"} with ${gathered.missing.length} still pending.`, at: now });
22238
+ await audit("swarm", `The leader ${topology.leaderid} collected ${gathered.gathered.length} worker outputs with ${gathered.missing.length} pending; the missing slices stay visible to the user.`, {});
22239
+ return { ...await swarmstateof(), collected: gathered.gathered, missing: gathered.missing };
22240
+ }
22241
+ if (input2.scale !== void 0) {
22242
+ if (!topology) throw new Error("The swarm holds no topology; the user elects a leader before the scaling.");
22243
+ const settings = await memory.getsettings();
22244
+ const bound = input2.scale.bound ?? settings?.swarmworkers;
22245
+ const verdict = workerscalevalid(bound);
22246
+ if (!verdict.allowed) throw new Error(verdict.reason);
22247
+ const agents = await memory.getagents();
22248
+ const outcome = scaleworkers({ topology, agents, pending: input2.scale.pending ?? 0, ...bound !== void 0 ? { bound } : {}, now });
22249
+ await memory.settopology(outcome.topology);
22250
+ await memory.addswarmaction({ id: randomid(), kind: "scale", agentid: topology.leaderid, summary: outcome.reason, at: now });
22251
+ await audit("swarm", `The worker lane scaled: ${outcome.reason}`, {});
22252
+ return { ...await swarmstateof(), scaleadded: outcome.added, scaleretired: outcome.retired };
22253
+ }
22254
+ if (input2.split !== void 0) {
22255
+ const split = plannersplit({ id: randomid(), planownerid: input2.split.planownerid?.trim() ?? "", runownerid: input2.split.runownerid?.trim() ?? "", ...input2.split.taskid !== void 0 && input2.split.taskid.trim() !== "" ? { taskid: input2.split.taskid.trim() } : {}, now });
22256
+ await memory.setplannersplits([...(await memory.getplannersplits()).filter((entry) => entry.id !== split.id), split]);
22257
+ await memory.addswarmaction({ id: randomid(), kind: "split", summary: `The task ${split.taskid ?? "of the swarm"} split between the planner ${split.planownerid} and the executor ${split.runownerid}; the executor reports every step outcome back.`, at: now });
22258
+ await audit("swarm", `The user split the task ${split.taskid ?? ""} between the planner agent ${split.planownerid} and the executor agent ${split.runownerid}; the executor reports every step outcome back to the planner.`, {});
22259
+ return swarmstateof();
22260
+ }
22261
+ if (input2.stepreport !== void 0) {
22262
+ const splitid = input2.stepreport.splitid?.trim() ?? "";
22263
+ const split = (await memory.getplannersplits()).find((entry) => entry.id === splitid);
22264
+ if (!split) throw new Error(`The planner executor split ${splitid} does not exist.`);
22265
+ const outcome = input2.stepreport.outcome === "failed" ? "failed" : "done";
22266
+ const next = reportstep({ split, stepid: input2.stepreport.stepid?.trim() ?? "", outcome, detail: input2.stepreport.detail?.trim() ?? "", now });
22267
+ await memory.setplannersplits((await memory.getplannersplits()).map((entry) => entry.id === split.id ? next : entry));
22268
+ await memory.addswarmaction({ id: randomid(), kind: "stepreport", agentid: split.runownerid, summary: `The executor ${split.runownerid} reported the step ${next.stepreports[next.stepreports.length - 1]?.stepid ?? ""} ${outcome} back to the planner ${split.planownerid}.`, at: now });
22269
+ await audit("swarm", `The executor ${split.runownerid} reported the step ${input2.stepreport.stepid ?? ""} ${outcome} back to the planner ${split.planownerid}; the split record keeps every step outcome.`, {});
22270
+ return swarmstateof();
22271
+ }
22272
+ if (input2.milestone !== void 0) {
22273
+ const agentid = input2.milestone.agentid?.trim() ?? "";
22274
+ if (agentid === "" || input2.milestone.label === void 0 || input2.milestone.label.trim() === "") throw new Error("The milestone needs its agent and its label.");
22275
+ const board = boardstate({ agents: await memory.getagents(), queue: await queueof(), ...topology !== void 0 ? { topology } : {}, now });
22276
+ const milestone = { label: input2.milestone.label.trim(), done: input2.milestone.done !== false, ...input2.milestone.done !== false ? { at: now } : {} };
22277
+ await memory.addboardsnapshot({ ...board, lanes: board.lanes.map((lane) => lane.agentid === agentid ? { ...lane, milestones: [...lane.milestones.filter((entry) => entry.label !== milestone.label), milestone] } : lane) });
22278
+ await memory.addswarmaction({ id: randomid(), kind: "milestone", agentid, summary: `The agent ${agentid} reached the milestone ${milestone.label}.`, at: now });
22279
+ await audit("swarm", `The agent ${agentid} reported the milestone ${milestone.label}; the progressboard snapshot stores it under the user configured retention.`, {});
22280
+ return swarmstateof();
22281
+ }
22282
+ throw new Error("The swarm leader request carries no elect, assign, collect, scale, split, stepreport or milestone action.");
22283
+ }
22284
+ case "swarmreview": {
22285
+ const input2 = message;
22286
+ const now = Date.now();
22287
+ if (input2.request !== void 0) {
22288
+ const requests = requestreview({ requests: await memory.getreviewrequests(), id: randomid(), fromagentid: input2.request.fromagentid?.trim() ?? "", toagentid: input2.request.toagentid?.trim() ?? "", subject: input2.request.subject ?? "", payload: input2.request.payload ?? "", ...input2.request.timeoutms !== void 0 ? { timeoutms: input2.request.timeoutms } : {}, now });
22289
+ await memory.setreviewrequests(requests);
22290
+ const requesterid = input2.request.fromagentid?.trim() ?? "";
22291
+ await memory.addswarmaction({ id: randomid(), kind: "review", ...requesterid !== "" ? { agentid: requesterid } : {}, summary: `The agent ${input2.request.fromagentid?.trim() ?? ""} routed the review of ${input2.request.subject ?? ""} to the agent ${input2.request.toagentid?.trim() ?? ""}.`, at: now });
22292
+ await audit("swarm", `The review request of ${input2.request.subject ?? ""} was routed from ${input2.request.fromagentid ?? ""} to ${input2.request.toagentid ?? ""}${input2.request.timeoutms !== void 0 ? ` with the user configured answer window ${input2.request.timeoutms}ms` : ""}; the review stays read only over the agent output.`, {});
22293
+ return swarmstateof();
22294
+ }
22295
+ if (input2.ack !== void 0) {
22296
+ const requests = ackreview({ requests: await memory.getreviewrequests(), id: input2.ack.trim(), now });
22297
+ await memory.setreviewrequests(requests);
22298
+ await audit("swarm", `The review request ${input2.ack} was acked; the answer still waits.`, {});
22299
+ return swarmstateof();
22300
+ }
22301
+ if (input2.apply !== void 0) {
22302
+ const verdict = input2.apply.verdict === "approve" || input2.apply.verdict === "reject" ? input2.apply.verdict : "changes";
22303
+ const outcome = applyreview({ requests: await memory.getreviewrequests(), id: input2.apply.id?.trim() ?? "", reviewerid: input2.apply.reviewerid?.trim() ?? "", verdict, issues: input2.apply.issues ?? [], requiredchanges: input2.apply.requiredchanges ?? [], ...input2.apply.taskid !== void 0 && input2.apply.taskid.trim() !== "" ? { taskid: input2.apply.taskid.trim() } : {}, now });
22304
+ const grade = criticreviewgrade(outcome.review);
22305
+ if (!grade.allowed) throw new Error(grade.reason);
22306
+ await memory.setreviewrequests(outcome.requests);
22307
+ await memory.addcriticreview(outcome.review);
22308
+ await memory.addswarmaction({ id: randomid(), kind: "review", agentid: outcome.review.reviewerid, summary: `The critic ${outcome.review.reviewerid} returned the ${verdict} verdict over the output of ${outcome.review.subjectagentid}.`, at: now });
22309
+ await audit("swarm", `The critic ${outcome.review.reviewerid} reviewed the output of ${outcome.review.subjectagentid} with the ${verdict} verdict${outcome.review.requiredchanges.length > 0 ? ` and ${outcome.review.requiredchanges.length} required change${outcome.review.requiredchanges.length === 1 ? "" : "s"}` : ""}; the critic reads only and the rework still passes the same human review.`, {});
22310
+ return swarmstateof();
22311
+ }
22312
+ if (input2.sweep === true) {
22313
+ const swept = sweepreviews({ requests: await memory.getreviewrequests(), now });
22314
+ await memory.setreviewrequests(swept.requests);
22315
+ if (swept.timedout.length > 0) await audit("swarm", `The review sweep timed out ${swept.timedout.length} unanswered request${swept.timedout.length === 1 ? "" : "s"} past the user configured window.`, {});
22316
+ return swarmstateof();
22317
+ }
22318
+ if (input2.verify !== void 0) {
22319
+ const settings = await memory.getsettings();
22320
+ const check = checkclaim({ id: randomid(), verifierid: input2.verify.verifierid?.trim() ?? "", claimagentid: input2.verify.claimagentid?.trim() ?? "", claim: input2.verify.claim ?? "", method: input2.verify.method?.trim() ?? "", outcome: input2.verify.outcome === "fail" ? "fail" : "pass", ...input2.verify.evidence !== void 0 && input2.verify.evidence.trim() !== "" ? { evidence: input2.verify.evidence } : {}, ...input2.verify.taskid !== void 0 && input2.verify.taskid.trim() !== "" ? { taskid: input2.verify.taskid.trim() } : {}, now });
22321
+ const grade = verifiermethodgrade({ method: check.method, allowed: settings?.verifiermethods ?? [] });
22322
+ if (!grade.allowed) throw new Error(grade.reason);
22323
+ await memory.addverifiercheck(check);
22324
+ await memory.addswarmaction({ id: randomid(), kind: "verify", agentid: check.verifierid, summary: `The verifier ${check.verifierid} marked the claim of ${check.claimagentid} ${check.outcome} by the ${check.method} method.`, at: now });
22325
+ await audit("swarm", `The verifier ${check.verifierid} checked the claim of ${check.claimagentid} by the ${check.method} method and marked it ${check.outcome}${check.evidence !== void 0 ? ` with the evidence ${check.evidence}` : ""}; the verifier check reads the page and never writes.`, {});
22326
+ return swarmstateof();
22327
+ }
22328
+ if (input2.escalate !== void 0) {
22329
+ const record2 = escalate({ id: randomid(), agentid: input2.escalate.agentid?.trim() ?? "", subject: input2.escalate.subject ?? "", context: input2.escalate.context ?? "", now });
22330
+ const gate = escalationgate(record2);
22331
+ if (!gate.allowed) throw new Error(gate.reason);
22332
+ await memory.addescalation(record2);
22333
+ await memory.addswarmaction({ id: randomid(), kind: "escalate", agentid: record2.agentid, summary: `The agent ${record2.agentid} lifted the stalled decision ${record2.subject} to the user with its full context.`, at: now });
22334
+ await audit("swarm", `The agent ${record2.agentid} escalated the decision ${record2.subject} to the user; the escalation stays human decided and the agent waits.`, {});
22335
+ return swarmstateof();
22336
+ }
22337
+ if (input2.decide !== void 0) {
22338
+ const record2 = (await memory.getescalations()).find((entry) => entry.id === input2.decide?.id?.trim());
22339
+ if (!record2) throw new Error(`The escalation ${input2.decide?.id ?? ""} does not exist.`);
22340
+ const resolved = resolveescalation({ escalation: record2, decision: input2.decide.decision ?? "", now });
22341
+ await memory.updateescalation(resolved);
22342
+ await memory.addswarmaction({ id: randomid(), kind: "escalate", agentid: record2.agentid, summary: `The user decided the escalation ${record2.subject}: ${resolved.decision}.`, at: now });
22343
+ await audit("swarm", `The user decided the escalation ${record2.subject} of the agent ${record2.agentid}; the decision is recorded and the agent continues from it.`, {});
22344
+ return swarmstateof();
22345
+ }
22346
+ if (input2.consensus !== void 0) {
22347
+ const settings = await memory.getsettings();
22348
+ const agents = await memory.getagents();
22349
+ const quorum = input2.consensus.quorum ?? settings?.swarmquorum;
22350
+ if (quorum === void 0) throw new Error("The consensus round needs its quorum; the user configures it or sets the swarmquorum run setting.");
22351
+ const voters = agents.filter((agent) => agent.state !== "stopped").length;
22352
+ const verdict = consensusquorumvalid({ quorum, voters });
22353
+ if (!verdict.allowed) throw new Error(verdict.reason);
22354
+ const round = openconsensus({ id: randomid(), subject: input2.consensus.subject ?? "", quorum, now });
22355
+ await memory.setconsensusround(round);
22356
+ await memory.addswarmaction({ id: randomid(), kind: "consensus", summary: `The consensus round on ${round.subject} opened with the user configured quorum ${quorum}.`, at: now });
22357
+ await audit("swarm", `The consensus round on ${round.subject} opened with the user configured quorum ${quorum} of ${voters} voting agents; the round carries when the yes votes reach it.`, {});
22358
+ return swarmstateof();
22359
+ }
22360
+ if (input2.vote !== void 0) {
22361
+ const round = (await memory.getconsensusrounds()).find((entry) => entry.id === input2.vote?.id?.trim());
22362
+ if (!round) throw new Error(`The consensus round ${input2.vote?.id ?? ""} does not exist.`);
22363
+ const vote = input2.vote.vote === "no" ? "no" : input2.vote.vote === "abstain" ? "abstain" : "yes";
22364
+ const next = castvote({ round, agentid: input2.vote.agentid?.trim() ?? "", vote, now });
22365
+ await memory.setconsensusround(next);
22366
+ const voterid = input2.vote.agentid?.trim() ?? "";
22367
+ await memory.addswarmaction({ id: randomid(), kind: "consensus", ...voterid !== "" ? { agentid: voterid } : {}, summary: `The agent ${input2.vote.agentid?.trim() ?? ""} voted ${vote} on ${round.subject}; the round is ${next.state}.`, at: now });
22368
+ await audit("swarm", `The agent ${input2.vote.agentid ?? ""} voted ${vote} on ${round.subject}; the round reads ${consensusstate(next).yes} yes, ${consensusstate(next).no} no and ${consensusstate(next).abstain} abstain against the quorum ${round.quorum}.`, {});
22369
+ return swarmstateof();
22370
+ }
22371
+ throw new Error("The swarm review request carries no request, ack, apply, sweep, verify, escalate, decide, consensus or vote action.");
22372
+ }
22373
+ case "swarmhandoff": {
22374
+ const input2 = message;
22375
+ const now = Date.now();
22376
+ if (input2.prepare !== void 0) {
22377
+ const agents = await memory.getagents();
22378
+ const record2 = preparehandoff({ agents, id: randomid(), fromagentid: input2.prepare.fromagentid?.trim() ?? "", toagentid: input2.prepare.toagentid?.trim() ?? "", taskstate: input2.prepare.taskstate ?? "", ...input2.prepare.tabid !== void 0 ? { tabid: input2.prepare.tabid } : {}, ...input2.prepare.reason !== void 0 && input2.prepare.reason.trim() !== "" ? { reason: input2.prepare.reason } : {}, now });
22379
+ await memory.addhandoff(record2);
22380
+ await memory.addswarmaction({ id: randomid(), kind: "handoff", agentid: record2.fromagentid, summary: `The tab ${record2.tabid} handoff from ${record2.fromagentid} to ${record2.toagentid} was prepared with the packaged task state.`, at: now });
22381
+ await audit("swarm", `The handoff of tab ${record2.tabid} from ${record2.fromagentid} to ${record2.toagentid} was prepared with the packaged task state; the transfer preserves the original session grants.`, {});
22382
+ return swarmstateof();
22383
+ }
22384
+ if (input2.transfer !== void 0) {
22385
+ const record2 = (await memory.gethandoffs()).find((entry) => entry.id === input2.transfer?.trim());
22386
+ if (!record2) throw new Error(`The handoff ${input2.transfer ?? ""} does not exist.`);
22387
+ const agents = await memory.getagents();
22388
+ const receiver = agents.find((agent) => agent.id === record2.toagentid);
22389
+ const session = await memory.getsession();
22390
+ const gate = handoffgrantgate({ record: record2, toscope: receiver?.scope, sessiongrants: session?.grants ?? [] });
22391
+ if (!gate.allowed) throw new Error(gate.reason);
22392
+ const outcome = transferhandoff({ agents, handoffs: await memory.gethandoffs(), id: record2.id, now });
22393
+ await memory.setagents(outcome.agents);
22394
+ await memory.updatehandoff(outcome.handoffs.find((entry) => entry.id === record2.id));
22395
+ await memory.addswarmaction({ id: randomid(), kind: "handoff", agentid: record2.toagentid, summary: `The tab ${record2.tabid} moved from ${record2.fromagentid} to ${record2.toagentid}; the task state stays packaged until the resume.`, at: now });
22396
+ await audit("swarm", `The tab ${record2.tabid} moved from ${record2.fromagentid} to ${record2.toagentid} under the one agent per tab rule; ${gate.reason ?? ""}`, {});
22397
+ return swarmstateof();
22398
+ }
22399
+ if (input2.resume !== void 0) {
22400
+ const record2 = (await memory.gethandoffs()).find((entry) => entry.id === input2.resume?.trim());
22401
+ if (!record2) throw new Error(`The handoff ${input2.resume ?? ""} does not exist.`);
22402
+ const resumed = resumehandoff({ handoffs: await memory.gethandoffs(), id: record2.id, now });
22403
+ await memory.updatehandoff(resumed);
22404
+ await memory.addswarmaction({ id: randomid(), kind: "handoff", agentid: resumed.toagentid, summary: `The agent ${resumed.toagentid} resumed the task from the packaged state of the handoff ${resumed.id}.`, at: now });
22405
+ await audit("swarm", `The agent ${resumed.toagentid} resumed the handed off task from its packaged state; the run continues behind the same session, plan and origin gates.`, {});
22406
+ return swarmstateof();
22407
+ }
22408
+ throw new Error("The swarm handoff request carries no prepare, transfer or resume action.");
22409
+ }
22410
+ case "swarmlocks": {
22411
+ const input2 = message;
22412
+ const now = Date.now();
22413
+ if (input2.acquire !== void 0) {
22414
+ const holder = input2.acquire.holder?.trim() ?? "";
22415
+ const origin = input2.acquire.origin?.trim() ?? "";
22416
+ const selector = input2.acquire.selector?.trim() ?? "";
22417
+ if (holder === "" || origin === "" || selector === "") throw new Error("The lock acquisition needs its holder, origin and selector.");
22418
+ const kind = input2.acquire.kind === "shared" ? "shared" : "exclusive";
22419
+ const locks = await memory.getlocks();
22420
+ const grade = lockscopevalid({ key: lockkey(origin, selector), holder, kind, origin, selector, acquiredat: now, ...input2.acquire.expiresat !== void 0 ? { expiresat: input2.acquire.expiresat } : {} });
22421
+ if (!grade.allowed) throw new Error(grade.reason);
22422
+ const outcome = acquirelock({ locks, holder, origin, selector, kind, ...input2.acquire.expiresat !== void 0 ? { expiresat: input2.acquire.expiresat } : {}, now });
22423
+ if (!outcome.acquired) throw new Error(outcome.reason);
22424
+ await memory.setlocks(outcome.locks);
22425
+ await memory.addswarmaction({ id: randomid(), kind: "lock", agentid: holder, summary: outcome.reason, at: now });
22426
+ await audit("swarm", `The ${kind} lock ${origin}|${selector} went to the agent ${holder}${input2.acquire.expiresat !== void 0 ? ` with the user configured expiry ${new Date(input2.acquire.expiresat).toISOString()}` : " with no expiry"}; the lock serializes work the same review already approved.`, {});
22427
+ return swarmstateof();
22428
+ }
22429
+ if (input2.release !== void 0) {
22430
+ const locks = await memory.getlocks();
22431
+ const outcome = releaselock({ locks, key: input2.release.key?.trim() ?? "", holder: input2.release.holder?.trim() ?? "", now });
22432
+ if (!outcome.released) throw new Error(`The agent ${input2.release.holder ?? ""} holds no lock ${input2.release.key ?? ""}.`);
22433
+ await memory.setlocks(outcome.locks);
22434
+ const releaserid = input2.release.holder?.trim() ?? "";
22435
+ await memory.addswarmaction({ id: randomid(), kind: "lock", ...releaserid !== "" ? { agentid: releaserid } : {}, summary: `The agent ${input2.release.holder?.trim() ?? ""} released the lock ${input2.release.key ?? ""}; the resource returned to the pool.`, at: now });
22436
+ await audit("swarm", `The agent ${input2.release.holder ?? ""} released the lock ${input2.release.key ?? ""}; the resource returned to the pool for the next agent.`, {});
22437
+ return swarmstateof();
22438
+ }
22439
+ if (input2.sweep === true) {
22440
+ const outcome = expirelocks({ locks: await memory.getlocks(), now });
22441
+ await memory.setlocks(outcome.locks);
22442
+ if (outcome.expired.length > 0) {
22443
+ await memory.addswarmaction({ id: randomid(), kind: "lock", summary: `The expiry sweep returned ${outcome.expired.length} abandoned lock${outcome.expired.length === 1 ? "" : "s"} to the pool.`, at: now });
22444
+ await audit("swarm", `The lock sweep expired ${outcome.expired.length} abandoned lock${outcome.expired.length === 1 ? "" : "s"} past their user configured expiry; the keys returned to the pool.`, {});
22445
+ }
22446
+ return swarmstateof();
22447
+ }
22448
+ if (input2.scan !== void 0) {
22449
+ const writers = (input2.scan.writers ?? []).map((writer) => ({ agentid: writer.agentid?.trim() ?? "", origin: writer.origin?.trim() ?? "", selector: writer.selector?.trim() ?? "", ...writer.taskid !== void 0 && writer.taskid.trim() !== "" ? { taskid: writer.taskid.trim() } : {} })).filter((writer) => writer.agentid !== "" && writer.origin !== "" && writer.selector !== "");
22450
+ const scan = scanconflicts({ id: randomid(), writers, now });
22451
+ await memory.addconflictscan(scan);
22452
+ await memory.addswarmaction({ id: randomid(), kind: "conflict", summary: scan.clean ? `The conflict scan found no overlapping write among ${writers.length} writers.` : `The conflict scan found ${scan.overlaps.length} overlapping target${scan.overlaps.length === 1 ? "" : "s"} among ${writers.length} writers with the suggested order ${scan.suggestedorder.join(" \u2192 ")}.`, at: now });
22453
+ await audit("swarm", scan.clean ? `The conflict scan of ${writers.length} parallel writers found no overlapping write; the runs stay safe side by side.` : `The conflict scan found ${scan.overlaps.length} overlapping target${scan.overlaps.length === 1 ? "" : "s"} (${scan.overlaps.map((overlap) => `${overlap.origin}|${overlap.selector} by ${overlap.writers.join(", ")}`).join("; ")}) and suggested the order ${scan.suggestedorder.join(" \u2192 ")}.`, {});
22454
+ return { ...await swarmstateof(), scan };
22455
+ }
22456
+ if (input2.arbitrate !== void 0) {
22457
+ const strategy = input2.arbitrate.strategy === "age" ? "age" : input2.arbitrate.strategy === "leader" ? "leader" : "priority";
22458
+ const rule = { id: randomid(), strategy, priorityorder: input2.arbitrate.priorityorder ?? [], configuredat: now };
22459
+ const topology = await memory.gettopology();
22460
+ const claims = (input2.arbitrate.claims ?? []).map((claim2) => ({ agentid: claim2.agentid?.trim() ?? "", claimedat: claim2.claimedat ?? now })).filter((claim2) => claim2.agentid !== "");
22461
+ const order = arbitrate({ rule, ...topology !== void 0 ? { leaderid: topology.leaderid } : {}, claims });
22462
+ await memory.addswarmaction({ id: randomid(), kind: "arbitrate", summary: `The ${strategy} arbitration rule ordered the competing claims ${order.join(" \u2192 ")}.`, at: now });
22463
+ await audit("swarm", `The user configured ${strategy} arbitration ordered the competing resource claims ${order.join(" \u2192 ")}; the ordering stays a user rule.`, {});
22464
+ return { ...await swarmstateof(), arbitration: order };
22465
+ }
22466
+ throw new Error("The swarm locks request carries no acquire, release, sweep, scan or arbitrate action.");
22467
+ }
22468
+ case "swarmmerge": {
22469
+ const input2 = message;
22470
+ const now = Date.now();
22471
+ const toentries = () => (input2.entries ?? []).map((entry, index) => ({ id: `${now}:${index}`, agentid: entry.agentid?.trim() ?? "", ...entry.taskid !== void 0 && entry.taskid.trim() !== "" ? { taskid: entry.taskid.trim() } : {}, key: entry.key?.trim() ?? "", value: entry.value ?? "", mergedat: now })).filter((entry) => entry.key !== "" && entry.agentid !== "");
22472
+ const ruleof = (value) => value === "last" ? "last" : value === "preferagent" ? "preferagent" : value === "fail" ? "fail" : "first";
22473
+ if (input2.merge !== void 0) {
22474
+ const fold = mergeresults({ entries: toentries(), rule: ruleof(input2.merge.rule), ...input2.merge.preferagent !== void 0 && input2.merge.preferagent.trim() !== "" ? { preferagent: input2.merge.preferagent.trim() } : {}, now });
22475
+ const grade = conflictresolutiongrade(ruleof(input2.merge.rule));
22476
+ if (!grade.allowed) throw new Error(grade.reason);
22477
+ await audit("swarm", `The merge folded ${input2.entries?.length ?? 0} parallel results under the ${ruleof(input2.merge.rule)} rule with ${fold.conflicts.length} conflict${fold.conflicts.length === 1 ? "" : "s"} resolved and every merged value keeping its provenance; ${grade.reason ?? ""}`, {});
22478
+ return { ...await swarmstateof(), merged: fold.entries, mergeconflicts: fold.conflicts, mergerefused: fold.refused };
22479
+ }
22480
+ if (input2.report !== void 0) {
22481
+ const rule = ruleof(input2.report.rule);
22482
+ const grade = conflictresolutiongrade(rule);
22483
+ if (!grade.allowed) throw new Error(grade.reason);
22484
+ const built = swarmreport({ id: randomid(), title: input2.report.title ?? "", outputs: toentries(), rule, ...input2.report.preferagent !== void 0 && input2.report.preferagent.trim() !== "" ? { preferagent: input2.report.preferagent.trim() } : {}, ...input2.report.confidence !== void 0 && input2.report.confidence.trim() !== "" ? { confidence: input2.report.confidence } : {}, now });
22485
+ if (built.refused) throw new Error(`The report refused the fold: ${built.conflicts.join("; ")}`);
22486
+ await memory.setreport(built.report);
22487
+ await memory.addswarmaction({ id: randomid(), kind: "merge", summary: `The report ${built.report.title} merged the outputs of ${built.report.sources.length} agent${built.report.sources.length === 1 ? "" : "s"} into ${built.report.sections.length} section${built.report.sections.length === 1 ? "" : "s"}.`, at: now });
22488
+ await audit("swarm", `The aggregate report ${built.report.title} folded the parallel outputs of ${built.report.sources.join(", ")} into ${built.report.sections.length} section${built.report.sections.length === 1 ? "" : "s"} under the ${rule} rule; every merged value keeps its provenance.`, {});
22489
+ return swarmstateof();
22490
+ }
22491
+ if (input2.export !== void 0) {
22492
+ const report = await memory.getreport();
22493
+ if (!report) throw new Error("No merged report is stored; the user builds the report before any export.");
22494
+ const grade = mergeegressgrade({ report, carriespagecontent: input2.export.carriespagecontent === true });
22495
+ if (!grade.allowed) throw new Error(grade.reason);
22496
+ await audit("swarm", `The merged report ${report.title} was exported; ${grade.reason ?? ""}`, {});
22497
+ return swarmstateof();
22498
+ }
22499
+ if (input2.compare !== void 0) {
22500
+ const outputs = (input2.compare.outputs ?? []).map((output) => ({ agentid: output.agentid?.trim() ?? "", value: output.value ?? "" })).filter((output) => output.agentid !== "");
22501
+ const comparison = compareoutputs({ id: randomid(), subject: input2.compare.subject ?? "", outputs, now });
22502
+ await audit("swarm", `The user compared ${outputs.length} competing agent outputs on ${comparison.subject}; the differences name where the agents disagree.`, {});
22503
+ return { ...await swarmstateof(), comparison };
22504
+ }
22505
+ if (input2.lesson !== void 0) {
22506
+ const board = await memory.getblackboard() ?? emptyboard();
22507
+ const next = sharelesson({ board, id: randomid(), agentid: input2.lesson.agentid?.trim() ?? "", statement: input2.lesson.statement ?? "", verifiedby: input2.lesson.verifiedby?.trim() ?? "", ...input2.lesson.section !== void 0 ? { section: ["goals", "facts", "findings", "scratch"].includes(input2.lesson.section) ? input2.lesson.section : "findings" } : {}, ...input2.lesson.consentclass !== void 0 ? { consentclass: input2.lesson.consentclass === "interaction" || input2.lesson.consentclass === "sensitive" ? input2.lesson.consentclass : "read" } : {}, now });
22508
+ await memory.setblackboard(next);
22509
+ const lessonauthor = input2.lesson.agentid?.trim() ?? "";
22510
+ await memory.addswarmaction({ id: randomid(), kind: "lesson", ...lessonauthor !== "" ? { agentid: lessonauthor } : {}, summary: `The verified lesson of ${input2.lesson.agentid?.trim() ?? "user"} landed on the findings section for every agent to read.`, at: now });
22511
+ await audit("swarm", `The verified lesson of the agent ${input2.lesson.agentid ?? ""} was shared to the blackboard findings section; only a verified lesson lands on the board.`, {});
22512
+ return swarmstateof();
22513
+ }
22514
+ if (input2.costs === true) {
22515
+ const usage = await memory.getagentusage();
22516
+ const cost = swarmcosts({ usage, now });
22517
+ await memory.addswarmcost(cost);
22518
+ await audit("swarm", `The shared cost accounting summed the usage of ${cost.agents} agent${cost.agents === 1 ? "" : "s"} into the swarm totals of ${cost.tokens} tokens, ${cost.cost} cost and ${cost.steps} steps.`, {});
22519
+ return swarmstateof();
22520
+ }
22521
+ if (input2.timeline !== void 0) {
22522
+ return { ...await swarmstateof(), timelinefiltered: await memory.getswarmtimeline({ ...input2.timeline.agentid !== void 0 && input2.timeline.agentid.trim() !== "" ? { agentid: input2.timeline.agentid.trim() } : {}, ...input2.timeline.kind !== void 0 && input2.timeline.kind.trim() !== "" ? { kind: input2.timeline.kind.trim() } : {} }) };
22523
+ }
22524
+ if (input2.replay !== void 0) {
22525
+ const agentid = input2.replay.trim();
22526
+ if (agentid === "") throw new Error("The replay needs its agent id.");
22527
+ const events = await memory.getagentevents();
22528
+ const actions = await memory.getswarmtimeline();
22529
+ const replay = replayagentrun({ events: [...events.map((event) => ({ id: event.id, kind: event.kind, summary: event.summary, at: event.at, ...event.agentid !== void 0 ? { agentid: event.agentid } : {} })), ...actions.map((action) => ({ id: action.id, kind: action.kind, summary: action.summary, at: action.at, ...action.agentid !== void 0 ? { agentid: action.agentid } : {} }))], agentid });
22530
+ await audit("swarm", `The replay rebuilt the run of the agent ${agentid} from the audit trail with ${replay.length} recorded action${replay.length === 1 ? "" : "s"}.`, {});
22531
+ return { ...await swarmstateof(), replay };
22532
+ }
22533
+ if (input2.snapshot === true) {
22534
+ const state = await swarmstateof();
22535
+ await memory.addboardsnapshot(state.board);
22536
+ await audit("swarm", `The progressboard snapshot was stored with ${state.board.lanes.length} lane${state.board.lanes.length === 1 ? "" : "s"} under the user configured retention.`, {});
22537
+ return swarmstateof();
22538
+ }
22539
+ throw new Error("The swarm merge request carries no merge, report, export, compare, lesson, costs, timeline, replay or snapshot action.");
22540
+ }
22541
+ case "environments": {
22542
+ const input2 = message;
22543
+ const now = Date.now();
22544
+ const session = await memory.getsession();
22545
+ const settings = await memory.getsettings();
22546
+ if (input2.requestcapability === true) {
22547
+ const granted = await requestoffscreengrant();
22548
+ await audit("environment", granted ? "The user granted the optional offscreen capability through the permissions api; the worker pool may spawn under the manifest declaration." : "The offscreen capability request stayed refused; the parse offload keeps its inline fallback inside the page.", { ...session ? { sessionid: session.id } : {} });
22549
+ return { ...await environmentviewof(), requested: granted };
22550
+ }
22551
+ if (input2.grants !== void 0) {
22552
+ if (!session) throw new Error("The environment grants need an active session to join.");
22553
+ const valid = /* @__PURE__ */ new Set(["pagecontext", "isolatedworld", "offscreenworker", "sandboxframe"]);
22554
+ const requested = input2.grants.filter((environment) => valid.has(environment));
22555
+ const unknown = input2.grants.filter((environment) => !valid.has(environment));
22556
+ if (unknown.length > 0) throw new Error(`The environments ${unknown.join(", ")} sit outside the four execution environments.`);
22557
+ await memory.setenvironmentgrants(requested);
22558
+ await audit("environment", `The user set the environment grants of the session to ${requested.length > 0 ? requested.join(", ") : "the documented default posture"}; no step ever widens them.`, { sessionid: session.id });
22559
+ return environmentviewof();
22560
+ }
22561
+ if (input2.pool !== void 0) {
22562
+ const sizegate = workerpoolsizevalid(input2.pool.size ?? settings?.workerpoolsize);
22563
+ if (!sizegate.allowed) throw new Error(sizegate.reason);
22564
+ const runid = (await memory.getplan())?.id ?? "pool";
22565
+ const current = workerpoolcounts.get(runid) ?? 0;
22566
+ const plan = poolplan({ pending: input2.pool.pending ?? 0, current, ...input2.pool.size !== void 0 ? { size: input2.pool.size } : {} });
22567
+ workerpoolcounts.set(runid, plan.workers);
22568
+ const provenance = { origin: session?.origin ?? "", stepid: "", environment: "offscreenworker" };
22569
+ await memory.addworkerevent({ id: randomid(), runid, kind: plan.added > 0 ? "spawn" : "teardown", workers: plan.workers, reason: plan.reason, provenance, at: now });
22570
+ await audit("worker", `The worker pool of the run ${runid} holds ${plan.workers} worker${plan.workers === 1 ? "" : "s"}: ${plan.reason}`, { ...session ? { sessionid: session.id } : {} });
22571
+ return { ...await environmentviewof(), pool: plan };
22572
+ }
22573
+ if (input2.offscreenclose === true) {
22574
+ const runid = (await memory.getplan())?.id ?? "pool";
22575
+ await closeoffscreendocument(runid);
22576
+ return environmentviewof();
22577
+ }
22578
+ if (input2.settings !== void 0) {
22579
+ const patch = { ...settings };
22580
+ if (input2.settings.parseoffload !== void 0) patch.parseoffload = input2.settings.parseoffload === true;
22581
+ if (input2.settings.workerpoolsize !== void 0) {
22582
+ const sizegate = workerpoolsizevalid(input2.settings.workerpoolsize);
22583
+ if (!sizegate.allowed) throw new Error(sizegate.reason);
22584
+ patch.workerpoolsize = input2.settings.workerpoolsize;
22585
+ }
22586
+ if (input2.settings.sandboxorigins !== void 0) patch.sandboxorigins = input2.settings.sandboxorigins.map((origin) => origin.trim()).filter((origin) => origin !== "");
22587
+ if (input2.settings.keepaliveinterval !== void 0) {
22588
+ const intervalgate = keepaliveintervalvalid(input2.settings.keepaliveinterval);
22589
+ if (!intervalgate.allowed) throw new Error(intervalgate.reason);
22590
+ patch.keepaliveinterval = input2.settings.keepaliveinterval;
22591
+ }
22592
+ if (input2.settings.zombieintervals !== void 0) patch.zombieintervals = input2.settings.zombieintervals;
22593
+ if (input2.settings.runstateretention !== void 0) patch.runstateretention = input2.settings.runstateretention;
22594
+ if (input2.settings.runstatebytes !== void 0) patch.runstatebytes = input2.settings.runstatebytes;
22595
+ await memory.setsettings(patch);
22596
+ await audit("environment", `The user updated the environment settings: parse offload ${patch.parseoffload === true ? "on" : "off"}, worker pool size ${patch.workerpoolsize ?? "queue driven"}, sandbox origins ${patch.sandboxorigins?.length ?? 0} configured, keepalive interval ${patch.keepaliveinterval ?? "the roadmap thirty seconds"}, zombie tolerance ${patch.zombieintervals ?? "the roadmap three intervals"}.`, {});
22597
+ return environmentviewof();
22598
+ }
22599
+ if (input2.render !== void 0) {
22600
+ const result = await executesandboxrender({ id: input2.render.stepid ?? randomid(), kind: "setattribute", summary: "Render untrusted markup inside the sandbox frame.", risk: "sensitive", ...input2.render.markup !== void 0 ? { value: input2.render.markup } : {}, options: JSON.stringify({ markup: input2.render.markup ?? "", ...input2.render.origin !== void 0 ? { sourceorigin: input2.render.origin } : {} }) }, session, await memory.getplan(), input2.render.origin ?? session?.origin ?? "");
22601
+ return { ...await environmentviewof(), render: result };
22602
+ }
22603
+ return environmentviewof();
22604
+ }
22605
+ case "runstate": {
22606
+ const input2 = message;
22607
+ const now = Date.now();
22608
+ const session = await memory.getsession();
22609
+ const plan = await memory.getplan();
22610
+ const settings = await memory.getsettings();
22611
+ const states = await memory.listrunstates();
22612
+ if (input2.start === true) {
22613
+ if (!session || !plan) throw new Error("The run state opens behind an active session with an approved plan.");
22614
+ const gate = keepalivegate({ session, plan, now });
22615
+ if (!gate.allowed) throw new Error(gate.reason);
22616
+ await openplanrun(session, plan);
22617
+ return { ...await environmentviewof(), opened: plan.id };
22618
+ }
22619
+ if (input2.stop === true) {
22620
+ const open = states.find((state) => state.keepalive.state === "active");
22621
+ if (open) await closeplanrun(open.runid, open.sessionid);
22622
+ return environmentviewof();
22623
+ }
22624
+ if (input2.beat === true) {
22625
+ await keepalivetick();
22626
+ return environmentviewof();
22627
+ }
22628
+ if (input2.recover === true) {
22629
+ const open = states.find((state) => state.keepalive.state === "active" || state.state === "recovered");
22630
+ if (!open) throw new Error("No interrupted run waits for its recovery.");
22631
+ const recovery = recoveryplan(open);
22632
+ await memory.setrunstate(open.profileid, reattachrun(open, now));
22633
+ await audit("environment", recovery.reason, { sessionid: open.sessionid });
22634
+ if (recovery.recoverable && recovery.pendingstepid !== void 0 && plan && plan.id === open.planid && plan.state === "approved") {
22635
+ const outcome = await executestep(recovery.pendingstepid);
22636
+ return { ...await environmentviewof(), recovery, resumed: outcome.summary };
22637
+ }
22638
+ return { ...await environmentviewof(), recovery };
22639
+ }
22640
+ if (input2.reap === true) {
22641
+ const swept = zombiesweep({ states, now, interval: settings?.keepaliveinterval ?? roadmapheartbeat, missedlimit: settings?.zombieintervals ?? roadmapzombieintervals });
22642
+ for (const state of swept.states) await memory.setrunstate(state.profileid, state);
22643
+ for (const runid of swept.reaped) await closeoffscreendocument(runid);
22644
+ if (swept.reaped.length > 0) await audit("environment", `The user reaped the zombie run${swept.reaped.length === 1 ? "" : "s"} ${swept.reaped.join(", ")} from the popup warning.`, {});
22645
+ return { ...await environmentviewof(), reaped: swept.reaped };
22646
+ }
22647
+ if (input2.lock !== void 0) {
22648
+ if (input2.lock.acquire !== void 0) {
22649
+ if (!session) throw new Error("The run lock needs its active session.");
22650
+ const runid = input2.lock.acquire.runid?.trim() ?? plan?.id ?? "";
22651
+ const outcome = acquirerunlock({ locks: await memory.getrunlocks(), sessionid: session.id, runid, holder: input2.lock.acquire.holder?.trim() ?? "user", ...input2.lock.acquire.expiresat !== void 0 ? { expiresat: input2.lock.acquire.expiresat } : {}, now });
22652
+ if (!outcome.acquired) throw new Error(outcome.reason);
22653
+ await memory.setrunlocks(outcome.locks);
22654
+ await audit("environment", outcome.reason, { sessionid: session.id });
22655
+ return { ...await environmentviewof(), lock: outcome.reason };
22656
+ }
22657
+ if (input2.lock.release !== void 0) {
22658
+ if (!session) throw new Error("The run lock release needs its active session.");
22659
+ const outcome = releaserunlock({ locks: await memory.getrunlocks(), sessionid: session.id, runid: input2.lock.release.runid?.trim() ?? plan?.id ?? "", now });
22660
+ if (!outcome.released) throw new Error(outcome.reason);
22661
+ await memory.setrunlocks(outcome.locks);
22662
+ await audit("environment", outcome.reason, { sessionid: session.id });
22663
+ return { ...await environmentviewof(), lock: outcome.reason };
22664
+ }
22665
+ }
22666
+ if (input2.serialize !== void 0) {
22667
+ const branches = (input2.serialize.branches ?? []).map((branch) => ({ branchid: branch.branchid?.trim() ?? "", steps: (branch.steps ?? []).filter((step) => step.stepid !== void 0 && step.tabid !== void 0).map((step) => ({ stepid: step.stepid, tabid: step.tabid })) })).filter((branch) => branch.branchid !== "");
22668
+ const order = serializesteps({ branches });
22669
+ await audit("environment", `The executor serialized ${order.length} step${order.length === 1 ? "" : "s"} across parallel branches so steps sharing one tab never run inside the same beat; every other step keeps its branch order.`, {});
22670
+ return { order };
22671
+ }
22672
+ if (input2.export === true) {
22673
+ const exported = await memory.exportrunstates();
22674
+ await audit("environment", `The run states were exported as one audit record: ${exported.runs} run${exported.runs === 1 ? "" : "s"}, ${exported.urls} url entr${exported.urls === 1 ? "y" : "ies"}, ${exported.environments} environment record${exported.environments === 1 ? "" : "s"} and ${exported.offloaded} worker turnaround${exported.offloaded === 1 ? "" : "s"}.`, {});
22675
+ return { export: exported, states: states.map((state) => ({ runid: state.runid, sessionid: state.sessionid, state: state.state, beats: state.keepalive.beats, lastbeatat: state.keepalive.lastbeatat, pendingstepid: state.pendingstepid, urlhistory: state.urlhistory, environments: state.environments, turnarounds: state.turnarounds })) };
22676
+ }
22677
+ if (input2.profileid !== void 0) {
22678
+ const state = await memory.getrunstate(input2.profileid);
22679
+ if (!state) throw new Error(`No run state exists for the profile ${input2.profileid}.`);
22680
+ return { state };
22681
+ }
22682
+ return environmentviewof();
22683
+ }
20933
22684
  default:
20934
22685
  throw new Error("Unknown Devthink request.");
20935
22686
  }
@@ -21111,20 +22862,42 @@ async function swarmstateof() {
21111
22862
  const killswitch = await memory.getkillswitch() ?? { engaged: false };
21112
22863
  const usage = await memory.getagentusage();
21113
22864
  const board = await memory.getblackboard() ?? emptyboard();
22865
+ const topology = await memory.gettopology();
22866
+ const reviewrequests = await memory.getreviewrequests();
22867
+ const reviews = await memory.getcriticreviews();
22868
+ const handoffs = await memory.gethandoffs();
22869
+ const report = await memory.getreport();
21114
22870
  return {
21115
22871
  overview: swarmoverview({ agents, queue, mailboxes }),
21116
22872
  agents: agents.map((agent) => {
21117
22873
  const claim2 = queue.claims.find((record2) => record2.agentid === agent.id);
21118
22874
  const task = claim2 !== void 0 ? queue.items.find((item) => item.id === claim2.taskid) : void 0;
21119
22875
  const agentusage = usage.find((entry) => entry.agentid === agent.id);
21120
- return { id: agent.id, name: agent.name, role: agent.role, state: agent.state, depth: agent.depth, ...agent.tabid !== void 0 ? { tabid: agent.tabid } : {}, ...agent.sessionid !== void 0 ? { sessionid: agent.sessionid } : {}, ...agent.parentid !== void 0 ? { parentid: agent.parentid } : {}, registeredat: agent.registeredat, ...agent.heartbeatat !== void 0 ? { heartbeatat: agent.heartbeatat } : {}, ...agent.budget !== void 0 ? { budget: agent.budget } : {}, ...agent.scope !== void 0 ? { scope: agent.scope } : {}, ...agentusage !== void 0 ? { usage: agentusage } : {}, ...task !== void 0 ? { currenttask: task.payload } : {}, unread: unreadcount(mailboxes, agent.id) };
22876
+ const openreview = reviewrequests.find((request) => request.fromagentid === agent.id && (request.state === "open" || request.state === "acked"));
22877
+ const receivedreview = reviews.find((review) => review.subjectagentid === agent.id);
22878
+ const arrows = handoffs.filter((record2) => record2.fromagentid === agent.id || record2.toagentid === agent.id).map((record2) => `${record2.fromagentid}\u2192${record2.toagentid}`);
22879
+ return { id: agent.id, name: agent.name, role: agent.role, state: agent.state, depth: agent.depth, ...agent.tabid !== void 0 ? { tabid: agent.tabid } : {}, ...agent.sessionid !== void 0 ? { sessionid: agent.sessionid } : {}, ...agent.parentid !== void 0 ? { parentid: agent.parentid } : {}, registeredat: agent.registeredat, ...agent.heartbeatat !== void 0 ? { heartbeatat: agent.heartbeatat } : {}, ...agent.budget !== void 0 ? { budget: agent.budget } : {}, ...agent.scope !== void 0 ? { scope: agent.scope } : {}, ...agentusage !== void 0 ? { usage: agentusage } : {}, ...task !== void 0 ? { currenttask: task.payload } : {}, unread: unreadcount(mailboxes, agent.id), ...openreview !== void 0 ? { reviewstatus: `awaiting the review of ${openreview.toagentid}` } : receivedreview !== void 0 ? { reviewstatus: `last critic verdict ${receivedreview.verdict}` } : {}, handoffarrows: [...new Set(arrows)] };
21121
22880
  }),
21122
22881
  queue: { lanes: queue.lanes, priorities: queue.priorities, completionpolicy: queue.completionpolicy, items: queue.items, claims: queue.claims, lanesreport: lanereport(queue), counts: taskcounts(queue), complete: queuecomplete(queue) },
21123
22882
  mailboxes: mailboxes.map((mailbox) => ({ agentid: mailbox.agentid, unread: mailbox.unread, inbox: mailbox.inbox, outbox: mailbox.outbox })),
21124
22883
  blackboard: { sections: boardsummary(board, now), entries: readentries({ board, now }) },
21125
22884
  spawns: await memory.getspawnrecords(),
21126
22885
  events: await memory.getagentevents(),
21127
- killswitch
22886
+ killswitch,
22887
+ ...topology !== void 0 ? { topology } : {},
22888
+ splits: await memory.getplannersplits(),
22889
+ reviews,
22890
+ verifications: await memory.getverifierchecks(),
22891
+ reviewrequests,
22892
+ handoffs,
22893
+ locks: await memory.getlocks(),
22894
+ conflicts: await memory.getconflictscans(),
22895
+ ...report !== void 0 ? { report } : {},
22896
+ board: boardstate({ agents, queue, ...topology !== void 0 ? { topology } : {}, now }),
22897
+ escalations: await memory.getescalations(),
22898
+ consensus: await memory.getconsensusrounds(),
22899
+ timeline: interleavetimeline(await memory.getswarmtimeline()),
22900
+ costs: await memory.getswarmcosts()
21128
22901
  };
21129
22902
  }
21130
22903
  async function llmstateof() {
@@ -21701,6 +23474,10 @@ setInterval(() => {
21701
23474
  void evaluatelistedtriggers().then(() => draintriggerqueue()).then(() => runwatchdog()).then(() => restorebackgroundruns()).catch(() => {
21702
23475
  });
21703
23476
  }, 3e4);
23477
+ setInterval(() => {
23478
+ void keepalivetick().catch(() => {
23479
+ });
23480
+ }, roadmapheartbeat);
21704
23481
  async function restoretriggers() {
21705
23482
  await registermenurules();
21706
23483
  await evaluatelistedtriggers();
@@ -21712,4 +23489,6 @@ async function restoretriggers() {
21712
23489
  }
21713
23490
  restoretriggers().catch(() => {
21714
23491
  });
23492
+ restorerunstates().catch(() => {
23493
+ });
21715
23494
  //# sourceMappingURL=background.js.map