@wenathlan/extension 1.1.62 → 1.1.64

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 (45) hide show
  1. package/README.md +5 -3
  2. package/dist/environments.d.ts +10 -0
  3. package/dist/environments.d.ts.map +1 -1
  4. package/dist/index.d.ts +4 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +956 -1
  7. package/dist/index.js.map +4 -4
  8. package/dist/memory.d.ts +126 -1
  9. package/dist/memory.d.ts.map +1 -1
  10. package/dist/planreview.d.ts +87 -0
  11. package/dist/planreview.d.ts.map +1 -0
  12. package/dist/policy.d.ts +101 -0
  13. package/dist/policy.d.ts.map +1 -1
  14. package/dist/protocol.d.ts +135 -0
  15. package/dist/protocol.d.ts.map +1 -1
  16. package/dist/sessioninterface.d.ts +213 -0
  17. package/dist/sessioninterface.d.ts.map +1 -0
  18. package/dist/surfaces.d.ts +59 -0
  19. package/dist/surfaces.d.ts.map +1 -0
  20. package/dist/types.d.ts +376 -3
  21. package/dist/types.d.ts.map +1 -1
  22. package/dist/version.d.ts +1 -1
  23. package/extension/dist/background.js +1394 -2
  24. package/extension/dist/background.js.map +4 -4
  25. package/extension/dist/dashboardpage.html +13 -0
  26. package/extension/dist/dashboardpage.js +129 -0
  27. package/extension/dist/dashboardpage.js.map +7 -0
  28. package/extension/dist/manifest.json +5 -2
  29. package/extension/dist/offscreen.js +5 -0
  30. package/extension/dist/offscreen.js.map +2 -2
  31. package/extension/dist/optionspage.html +14 -0
  32. package/extension/dist/optionspage.js +118 -0
  33. package/extension/dist/optionspage.js.map +7 -0
  34. package/extension/dist/pagebridge.js.map +1 -1
  35. package/extension/dist/popup.html +4 -1
  36. package/extension/dist/popup.js +193 -0
  37. package/extension/dist/popup.js.map +3 -3
  38. package/extension/dist/sidepanel.html +7 -2
  39. package/extension/dist/sidepanel.js +693 -219
  40. package/extension/dist/sidepanel.js.map +3 -3
  41. package/extension/dist/transparencypage.html +1 -0
  42. package/extension/dist/transparencypage.js +42 -0
  43. package/extension/dist/transparencypage.js.map +2 -2
  44. package/extension/manifest.json +5 -2
  45. package/package.json +1 -1
@@ -4900,6 +4900,265 @@ var sessionmemory = class {
4900
4900
  phishverdicts: await this.getphishverdicts()
4901
4901
  };
4902
4902
  }
4903
+ /**
4904
+ * Session interface persistence of the 1.1.63 family.
4905
+ * The five session stores live here, scoped per profile workspace: the sitenotes per origin with sensitive bodies sealed at rest, the append only scratchpad entries per task with their step provenance, the distilled runsummaries per run and origin, the correctionmemory entries per origin and kind captured from plan review, and the consentmemory entries per origin with every grant, denial, expiry and revocation carrying its boundary; beside them the semanticrecall index with fingerprint deduplication answers ranked queries inside the run scope, the incremental historysearch corpus indexes session metadata, notes and summaries as they are written, the errorsurface payloads of failed steps keep their retry hints with the policy verdict, the per tab session references isolate parallel tabs, and the export bundles notes, summaries and corrections as one audit bundle.
4906
+ * The recall seam stays documented: the local fingerprint index answers every query today while a future remote recall backend can take the same shapes behind the seam without touching the callers.
4907
+ */
4908
+ /** Replaces the stored site notes; a sensitive note carries its sealedbody only so the plain body never persists. */
4909
+ async setsitenotes(notes) {
4910
+ return this.adapter.set("sitenotes", notes);
4911
+ }
4912
+ /** Returns the stored site notes, oldest update first. */
4913
+ async getsitenotes() {
4914
+ return await this.adapter.get("sitenotes") ?? [];
4915
+ }
4916
+ /** Reads the site notes of one origin only; the read gate keeps the origin inside the session grants. */
4917
+ async readsitenotes(origin) {
4918
+ return (await this.getsitenotes()).filter((note) => note.origin === origin);
4919
+ }
4920
+ /** Writes one site note: a note of the same id keeps its latest edit while a new note joins the store. */
4921
+ async writesitenote(note) {
4922
+ const notes = await this.getsitenotes();
4923
+ await this.setsitenotes(notes.some((candidate) => candidate.id === note.id) ? notes.map((candidate) => candidate.id === note.id ? note : candidate) : [...notes, note]);
4924
+ }
4925
+ /** Removes one site note by its id. */
4926
+ async removesitenote(id) {
4927
+ await this.setsitenotes((await this.getsitenotes()).filter((note) => note.id !== id));
4928
+ }
4929
+ /** Expires the site notes past the user configured window; an absent window keeps every note. */
4930
+ async expiresitenotes(retention, now) {
4931
+ if (retention === void 0) return await this.getsitenotes();
4932
+ const kept = (await this.getsitenotes()).filter((note) => now - note.updatedat < retention);
4933
+ await this.setsitenotes(kept);
4934
+ return kept;
4935
+ }
4936
+ /** Replaces the stored scratchpad entries per task. */
4937
+ async setscratchpad(entries) {
4938
+ return this.adapter.set("scratchpad", entries);
4939
+ }
4940
+ /** Returns every stored scratchpad entry, newest first. */
4941
+ async getscratchpadall() {
4942
+ return await this.adapter.get("scratchpad") ?? [];
4943
+ }
4944
+ /** Appends one scratchpad entry: the pad stays append only so no later write rewrites an earlier entry. */
4945
+ async appendscratchentry(entry) {
4946
+ await this.setscratchpad([entry, ...await this.getscratchpadall()]);
4947
+ }
4948
+ /** Reads the scratchpad of one task session, newest first; entries of another task never cross the boundary. */
4949
+ async readscratchpad(taskid, sessionid) {
4950
+ return (await this.getscratchpadall()).filter((entry) => entry.taskid === taskid && entry.sessionid === sessionid);
4951
+ }
4952
+ /** Prunes the scratchpad entries past the user configured window; an absent window keeps every entry. */
4953
+ async prunescratchentries(window2, now) {
4954
+ if (window2 === void 0) return await this.getscratchpadall();
4955
+ const kept = (await this.getscratchpadall()).filter((entry) => now - entry.at < window2);
4956
+ await this.setscratchpad(kept);
4957
+ return kept;
4958
+ }
4959
+ /** Stores one distilled run summary of a completed run. */
4960
+ async setrunsummary(summary) {
4961
+ return this.adapter.set(`runsummary:${summary.runid}`, summary);
4962
+ }
4963
+ /** Returns the stored run summary of one run; an absent summary returns undefined. */
4964
+ async getrunsummary(runid) {
4965
+ return this.adapter.get(`runsummary:${runid}`);
4966
+ }
4967
+ /** Lists the stored run summaries, oldest distillation first, optionally filtered by origin. */
4968
+ async listrunsummaries(origin) {
4969
+ const index = await this.adapter.get("runsummaryindex") ?? [];
4970
+ const summaries = [];
4971
+ for (const runid of index) {
4972
+ const summary = await this.getrunsummary(runid);
4973
+ if (summary) summaries.push(summary);
4974
+ }
4975
+ const filtered = origin === void 0 ? summaries : summaries.filter((summary) => summary.origins.includes(origin));
4976
+ return filtered.sort((one, two) => one.distilledat - two.distilledat);
4977
+ }
4978
+ /** Tracks one run in the run summary index so the listing reads every stored summary. */
4979
+ async trackrunsummary(runid) {
4980
+ const index = await this.adapter.get("runsummaryindex") ?? [];
4981
+ if (!index.includes(runid)) await this.adapter.set("runsummaryindex", [...index, runid]);
4982
+ }
4983
+ /** Expires the run summaries past the user configured window; an absent window keeps every summary. */
4984
+ async expirerunsummaries(retention, now) {
4985
+ const summaries = await this.listrunsummaries();
4986
+ if (retention === void 0) return summaries;
4987
+ const kept = [];
4988
+ for (const summary of summaries) {
4989
+ if (now - summary.distilledat > retention) await this.adapter.set(`runsummary:${summary.runid}`, { ...summary, steps: [], kinds: [], origins: summary.origins });
4990
+ else kept.push(summary);
4991
+ }
4992
+ return kept;
4993
+ }
4994
+ /** Replaces the semantic recall index with its fingerprint deduplicated entries. */
4995
+ async setrecallindex(index) {
4996
+ return this.adapter.set("recallindex", index);
4997
+ }
4998
+ /** Returns the stored semantic recall index entries, newest first. */
4999
+ async getrecallindex() {
5000
+ return await this.adapter.get("recallindex") ?? [];
5001
+ }
5002
+ /** Adds one recall index entry with fingerprint deduplication: a repeated extraction keeps its first entry. */
5003
+ async addrecallentry(entry) {
5004
+ const index = await this.getrecallindex();
5005
+ if (index.some((candidate) => candidate.fingerprint === entry.fingerprint && candidate.origin === entry.origin)) return;
5006
+ await this.setrecallindex([entry, ...index]);
5007
+ }
5008
+ /** Answers one semantic recall query across the extraction stores: the local index ranks by text similarity inside the run scope and returns the provenance of every match. */
5009
+ async semanticrecall(query, scope, rank) {
5010
+ return rank(await this.getrecallindex(), query, scope);
5011
+ }
5012
+ /** Expires the recall index entries past the user configured window; the extraction records themselves stay for the audit trail. */
5013
+ async expirerecallentries(window2, now) {
5014
+ if (window2 === void 0) return await this.getrecallindex();
5015
+ const kept = (await this.getrecallindex()).filter((entry) => now - entry.at < window2);
5016
+ await this.setrecallindex(kept);
5017
+ return kept;
5018
+ }
5019
+ /** Replaces the stored correction memory entries per origin and kind. */
5020
+ async setcorrections(corrections) {
5021
+ return this.adapter.set("corrections", corrections);
5022
+ }
5023
+ /** Returns the stored correction memory entries, newest first, optionally filtered by origin and kind. */
5024
+ async getcorrections(filter) {
5025
+ const entries = await this.adapter.get("corrections") ?? [];
5026
+ return entries.filter((entry) => (filter?.origin === void 0 || entry.origin === filter.origin) && (filter?.kind === void 0 || entry.kind === filter.kind));
5027
+ }
5028
+ /** Records one correction memory entry captured from a plan review edit or rejection. */
5029
+ async addcorrection(entry) {
5030
+ await this.setcorrections([entry, ...await this.adapter.get("corrections") ?? []]);
5031
+ }
5032
+ /** Expires the correction memory entries past the user configured window; an absent window keeps every correction. */
5033
+ async expirecorrectionentries(window2, now) {
5034
+ if (window2 === void 0) return await this.getcorrections();
5035
+ const kept = (await this.getcorrections()).filter((entry) => now - entry.at < window2);
5036
+ await this.setcorrections(kept);
5037
+ return kept;
5038
+ }
5039
+ /** Replaces the stored consent memory entries per origin. */
5040
+ async setconsentmemory(entries) {
5041
+ return this.adapter.set("consentmemory", entries);
5042
+ }
5043
+ /** Returns the stored consent memory entries, newest first, optionally filtered by origin. */
5044
+ async getconsentmemory(origin) {
5045
+ const entries = await this.adapter.get("consentmemory") ?? [];
5046
+ return origin === void 0 ? entries : entries.filter((entry) => entry.origin === origin);
5047
+ }
5048
+ /** Records one consent memory entry per origin: every grant, denial, expiry and revocation lands with its boundary and kinds. */
5049
+ async addconsentmemoryentry(entry) {
5050
+ await this.setconsentmemory([entry, ...await this.adapter.get("consentmemory") ?? []]);
5051
+ }
5052
+ /** Replaces the stored error surface payloads of failed steps. */
5053
+ async seterrorsurfaces(surfaces) {
5054
+ return this.adapter.set("errorsurfaces", surfaces);
5055
+ }
5056
+ /** Returns the stored error surface payloads, newest first, optionally filtered by step. */
5057
+ async geterrorsurfaces(stepid) {
5058
+ const surfaces = await this.adapter.get("errorsurfaces") ?? [];
5059
+ return stepid === void 0 ? surfaces : surfaces.filter((surface) => surface.stepid === stepid);
5060
+ }
5061
+ /** Records one error surface payload of a failed step with its retry hint and the policy verdict. */
5062
+ async adderrorsurface(surface) {
5063
+ await this.seterrorsurfaces([surface, ...await this.adapter.get("errorsurfaces") ?? []].slice(0, 500));
5064
+ }
5065
+ /** Replaces the incremental history search corpus of session metadata, notes and run summaries. */
5066
+ async sethistoryindex(corpus) {
5067
+ return this.adapter.set("historyindex", corpus);
5068
+ }
5069
+ /** Returns the incremental history search corpus, newest entry first. */
5070
+ async gethistoryindex() {
5071
+ return await this.adapter.get("historyindex") ?? [];
5072
+ }
5073
+ /** Adds one corpus entry to the incremental history index on each store write. */
5074
+ async addhistoryentry(entry) {
5075
+ const corpus = await this.gethistoryindex();
5076
+ await this.sethistoryindex([entry, ...corpus.filter((candidate) => !(candidate.source === entry.source && candidate.id === entry.id))]);
5077
+ }
5078
+ /** Answers one history search query against the incremental corpus with the matched terms highlighted. */
5079
+ async historysearch(query, search) {
5080
+ return search(await this.gethistoryindex(), query);
5081
+ }
5082
+ /** Stores one per tab session reference so parallel tabs never collide inside the session stores. */
5083
+ async settabsession(ref) {
5084
+ return this.adapter.set(`tabsession:${ref.tabid}`, ref);
5085
+ }
5086
+ /** Returns the per tab session reference of one tab; an absent reference returns undefined. */
5087
+ async gettabsession(tabid2) {
5088
+ return this.adapter.get(`tabsession:${tabid2}`);
5089
+ }
5090
+ /** Lists every per tab session reference so the sessiongrid reads the per tab lock state of concurrent sessions. */
5091
+ async listtabsessions() {
5092
+ const tabs = await this.adapter.get("tabsessionindex") ?? [];
5093
+ const refs = [];
5094
+ for (const tabid2 of tabs) {
5095
+ const ref = await this.gettabsession(tabid2);
5096
+ if (ref) refs.push(ref);
5097
+ }
5098
+ return refs;
5099
+ }
5100
+ /** Tracks one tab in the per tab session index so the listing reads every isolated reference. */
5101
+ async tracktabsession(tabid2) {
5102
+ const tabs = await this.adapter.get("tabsessionindex") ?? [];
5103
+ if (!tabs.includes(tabid2)) await this.adapter.set("tabsessionindex", [...tabs, tabid2]);
5104
+ }
5105
+ /** Exports the site notes, the run summaries and the correction memory as one audit bundle: sensitive note bodies stay sealed in the export. */
5106
+ async exportsessionbundle(exportedat) {
5107
+ return { kind: "sessionbundle", notes: await this.getsitenotes(), summaries: await this.listrunsummaries(), corrections: await this.getcorrections(), exportedat };
5108
+ }
5109
+ /**
5110
+ * Interface surface stores of the 1.1.64 family live here, scoped per profile workspace: the commandpalette usage counts the recent first ranking reads, the taskinput history of natural language goals, the onboarding completion state, the per surface layout preferences, the logstream filter preferences and the stepapprove resolution history per origin.
5111
+ */
5112
+ /** Returns every commandpalette usage record so the ranking lifts the recent commands first. */
5113
+ async getpaletteusage() {
5114
+ return await this.adapter.get("paletteusage") ?? [];
5115
+ }
5116
+ /** Replaces the commandpalette usage records after one use: the count grows and the last use time moves so the ranking reads both. */
5117
+ async setpaletteusage(records) {
5118
+ return this.adapter.set("paletteusage", records);
5119
+ }
5120
+ /** Returns the stored taskinput history, newest first. */
5121
+ async gettaskinputs() {
5122
+ return await this.adapter.get("taskinputs") ?? [];
5123
+ }
5124
+ /** Adds one taskinput submission to the per profile history; the retention window stays a user setting. */
5125
+ async addtaskinput(entry) {
5126
+ const retention = (await this.getsettings())?.taskinputretention;
5127
+ const history2 = [entry, ...await this.gettaskinputs()];
5128
+ await this.adapter.set("taskinputs", retention === void 0 ? history2 : history2.filter((candidate) => entry.at - candidate.at < retention));
5129
+ }
5130
+ /** Returns the onboarding completion state; an absent state means the walkthrough never ran. */
5131
+ async getonboardingstate() {
5132
+ return this.adapter.get("onboarding");
5133
+ }
5134
+ /** Stores the onboarding completion state; a done walkthrough never runs again on its own. */
5135
+ async setonboardingstate(state) {
5136
+ return this.adapter.set("onboarding", state);
5137
+ }
5138
+ /** Returns the layout preferences of one surface; an absent preference set returns undefined. */
5139
+ async getsurfacelayout(surface) {
5140
+ return this.adapter.get(`surfacelayout:${surface}`);
5141
+ }
5142
+ /** Stores the layout preferences of one surface, scoped per profile workspace. */
5143
+ async setsurfacelayout(layout) {
5144
+ return this.adapter.set(`surfacelayout:${layout.surface}`, layout);
5145
+ }
5146
+ /** Returns the stored logstream filter preferences of the live view. */
5147
+ async getlogstreamfilters() {
5148
+ return this.adapter.get("logstreamfilters");
5149
+ }
5150
+ /** Stores the logstream filter preferences of the live view. */
5151
+ async setlogstreamfilters(filter) {
5152
+ return this.adapter.set("logstreamfilters", filter);
5153
+ }
5154
+ /** Returns every stored stepapprove resolution, newest first, with its human provenance. */
5155
+ async getstepapproveresolutions() {
5156
+ return await this.adapter.get("stepapproveresolutions") ?? [];
5157
+ }
5158
+ /** Records one stepapprove resolution in the per origin history. */
5159
+ async addstepapproveresolution(resolution) {
5160
+ await this.adapter.set("stepapproveresolutions", [resolution, ...await this.getstepapproveresolutions()]);
5161
+ }
4903
5162
  };
4904
5163
  function mediakindof(record2) {
4905
5164
  if ("pages" in record2) return "pdf";
@@ -5181,6 +5440,11 @@ function isolatedinjection(step) {
5181
5440
  }
5182
5441
  return { world: "ISOLATED", code: step.value, args };
5183
5442
  }
5443
+ var runsummarytask = "runsummary";
5444
+ function summaryrequestof(input) {
5445
+ if (input.payload.trim() === "") throw new Error("The runsummary request needs its payload reference.");
5446
+ return { id: input.id, runid: input.runid, stepid: input.sessionid, task: runsummarytask, payload: input.payload, transferables: [], sentat: input.sentat };
5447
+ }
5184
5448
 
5185
5449
  // toolcatalog.ts
5186
5450
  var toolcatalogversion = 1;
@@ -10367,6 +10631,87 @@ function untrustedrendergate(input) {
10367
10631
  if (input.environment === "sandboxframe") return { allowed: true, reason: "The extracted markup renders inside the sandboxframe under its nonce with scripts and handlers stripped; the untrusted content never reenters the page context." };
10368
10632
  return { allowed: false, reason: `The extracted markup grades untrusted and refuses to render inside the ${input.environment}; every untrusted render routes through the sandboxframe.` };
10369
10633
  }
10634
+ function sitenotesreadgate(input) {
10635
+ if (input.grants.includes(input.origin)) return { allowed: true, reason: `The session granted ${input.origin}, so the site notes of the origin read.` };
10636
+ return { allowed: false, reason: `The session never granted ${input.origin}; the site notes of the origin refuse the read.` };
10637
+ }
10638
+ function sitenoteswritegate(input) {
10639
+ if (!input.consent) return { allowed: false, reason: `The site note write for ${input.origin} needs the explicit consent of the user; no note lands without a reviewed write.` };
10640
+ return { allowed: true, reason: `The user consented to the site note write for ${input.origin}; the note keeps its author provenance and its timestamps.` };
10641
+ }
10642
+ function scratchpadscopegate(input) {
10643
+ if (input.taskid !== input.entrytaskid || input.sessionid !== input.entrysessionid) return { allowed: false, reason: `The scratchpad entry belongs to the task ${input.entrytaskid} of the session ${input.entrysessionid}; the task ${input.taskid} of the session ${input.sessionid} never crosses that boundary.` };
10644
+ return { allowed: true, reason: `The scratchpad entry belongs to the task ${input.taskid} of the session ${input.sessionid} that asks for it.` };
10645
+ }
10646
+ function memoryreadscopegate(input) {
10647
+ if (input.phase === "planning" || input.phase === "prompting") return { allowed: true, reason: `The ${input.phase} phase reads the correction and consent memory so the proposal and the prompt carry the prior decisions.` };
10648
+ return { allowed: false, reason: `The ${input.phase} phase reads no correction or consent memory; the history serves the planning and the prompting alone.` };
10649
+ }
10650
+ function semanticrecallscopegate(input) {
10651
+ if (input.origin === void 0) return { allowed: true, reason: `The recall query names no origin, so it ranks the ${input.scope.length} origin${input.scope.length === 1 ? "" : "s"} of the run scope only.` };
10652
+ if (!input.scope.includes(input.origin)) return { allowed: false, reason: `The recall query asks for ${input.origin} while the run scope holds ${input.scope.length > 0 ? input.scope.join(", ") : "no origin"}; a recall across origins outside the run scope refuses.` };
10653
+ return { allowed: true, reason: `The recall query asks for ${input.origin} inside the run scope; the ranking stays scoped.` };
10654
+ }
10655
+ function summarywindowvalid(window2) {
10656
+ if (window2 === void 0) return { allowed: true, reason: "No runsummary window is configured, so the distillation keeps every step with no fixed cap." };
10657
+ if (!Number.isInteger(window2) || window2 < 0) return { allowed: false, reason: "The runsummary window stays a whole number of steps the user chose; no engine cap exists." };
10658
+ return { allowed: true, reason: `The runsummary window of ${window2} step${window2 === 1 ? "" : "s"} stays the user configured choice; no engine cap exists.` };
10659
+ }
10660
+ function sessionretentionvalid(window2) {
10661
+ if (window2 === void 0) return { allowed: true, reason: "No retention window is configured, so the session store keeps every record forever." };
10662
+ if (!Number.isFinite(window2) || window2 <= 0) return { allowed: false, reason: "The retention window stays a positive user value in milliseconds; no engine boundary expires a record." };
10663
+ return { allowed: true, reason: `The retention window of ${window2} milliseconds stays the user configured choice.` };
10664
+ }
10665
+ function cancelrungate(input) {
10666
+ if (input.rollbackscope === "none") return { allowed: true, reason: `The cancelrun stops the run with no rollback; the ${input.queuedstepids.length} queued step${input.queuedstepids.length === 1 ? "" : "s"} stay as the run left them and the ${input.executedstepids.length} executed step${input.executedstepids.length === 1 ? "" : "s"} stay in the sealed log.` };
10667
+ return { allowed: true, reason: `The cancelrun rolls the ${input.queuedstepids.length} queued step${input.queuedstepids.length === 1 ? "" : "s"} back${input.queuedstepids.length > 0 ? ` (${input.queuedstepids.join(", ")})` : ""} while the ${input.executedstepids.length} executed step${input.executedstepids.length === 1 ? "" : "s"} stay untouched in the sealed log.` };
10668
+ }
10669
+ function retrydispatchgate(input) {
10670
+ if (!input.reviewed) return { allowed: false, reason: `The retry of the step ${input.stepid} passes only through a new reviewed dispatch; an automatic retry never bypasses the review.` };
10671
+ return { allowed: true, reason: `The retry of the step ${input.stepid} dispatches again through the full consent gate chain: the session, the plan and the origin gates all recheck the step.` };
10672
+ }
10673
+ function paletteactiongate(input) {
10674
+ if (input.action.permission !== void 0 && !input.granted.includes(input.action.permission)) return { allowed: false, reason: `The ${input.action.command} command needs the ${input.action.permission} capability granted before the palette lists it; the palette never offers an action the current capability set refuses.` };
10675
+ if (input.action.session === true && !input.sessionactive) return { allowed: false, reason: `The ${input.action.command} command needs an active browser session before the palette lists it; the palette never offers a run action without its session.` };
10676
+ return { allowed: true, reason: `The ${input.action.command} command rides its granted permissions and lists in the palette.` };
10677
+ }
10678
+ function taskinputproposalgate(input) {
10679
+ if (input.direct) return { allowed: false, reason: "The taskinput never executes a goal directly; every natural language goal routes through the same proposal flow as the api and becomes a reviewed plan first." };
10680
+ if (input.text.trim() === "") return { allowed: false, reason: "The taskinput submission needs its natural language goal; an empty goal never reaches the proposal flow." };
10681
+ if (input.origin.trim() === "") return { allowed: false, reason: "The taskinput submission needs its active origin scope; a goal without an origin never reaches the proposal flow." };
10682
+ return { allowed: true, reason: `The taskinput goal for ${input.origin} rides the same proposal flow as the api: the observation, the capabilities and the plan review all recheck it.` };
10683
+ }
10684
+ function planreviewgate(input) {
10685
+ if (input.state === "approved") return { allowed: true, reason: "The plan already passed its review: the approval is the review of record and the execution proceeds." };
10686
+ if (!input.reviewed) return { allowed: false, reason: "The pending plan has no plancard review yet; every step renders its card with the risk class, the environment and the options before any execution." };
10687
+ return { allowed: true, reason: "The plancard review of the pending plan is open; the resolution of each step stays a distinct human action." };
10688
+ }
10689
+ function stepapprovegate(input) {
10690
+ if (input.stepids.length === 0) return { allowed: false, reason: "A stepapprove resolution names its single step." };
10691
+ if (input.stepids.length > 1) return { allowed: false, reason: `One human action resolves exactly one step; the batch of ${input.stepids.length} steps refuses in full because no batch approval exists.` };
10692
+ if (input.surface === "background") return { allowed: false, reason: `The ${input.resolution} resolution of the step ${input.stepids[0]} needs its distinct human action from a surface; the background never resolves a review on its own.` };
10693
+ if (input.resolution === "edit") return { allowed: true, reason: `The user edits the step ${input.stepids[0]} from the ${input.surface} before approving; the corrected shape rides the plan and the resolution keeps its human provenance.` };
10694
+ return { allowed: true, reason: `The user ${input.resolution === "approve" ? "approved" : "rejected"} the step ${input.stepids[0]} from the ${input.surface}; one distinct human action resolved the step alone.` };
10695
+ }
10696
+ function diffpreviewgate(input) {
10697
+ if (input.risk !== "sensitive") return { allowed: false, reason: `The ${input.risk} step changes no page or browser state; the diffpreview compares the observed before state with the predicted after state of write class steps only.` };
10698
+ return { allowed: true, reason: "The write class step changes page or browser state, so the diffpreview compares its observed before state with its predicted after state." };
10699
+ }
10700
+ function onboardingconsentgate(input) {
10701
+ if (input.consentevents.length === 0) return { allowed: true, reason: "The onboarding completion writes its single consent scoped event; no consent event exists yet." };
10702
+ if (input.consentevents.length === 1) return { allowed: false, reason: `The onboarding already wrote its single consent scoped event ${input.consentevents[0]}; a walkthrough never writes a second one.` };
10703
+ return { allowed: false, reason: `The onboarding found ${input.consentevents.length} consent scoped events; a walkthrough writes exactly one and the extra events refuse.` };
10704
+ }
10705
+ function logbufferboundvalid(bound) {
10706
+ if (bound === void 0) return { allowed: true, reason: "No logstream buffer bound is configured, so the live window keeps every event while the full history stays in memory." };
10707
+ if (!Number.isInteger(bound) || bound <= 0) return { allowed: false, reason: "The logstream buffer bound stays a positive whole number of events the user chose; no engine cap exists." };
10708
+ return { allowed: true, reason: `The logstream buffer bound of ${bound} event${bound === 1 ? "" : "s"} stays the user configured choice; the full history stays in memory.` };
10709
+ }
10710
+ function logstreamegressgate(input) {
10711
+ if (input.entries === 0) return { allowed: false, reason: "The audit excerpt names no event of the logstream; an empty range never copies." };
10712
+ if (!input.verified) return { allowed: false, reason: "The logstream chain failed its live verification; the audit excerpt refuses the copy because only a verified range leaves the stream." };
10713
+ return { allowed: true, reason: `The logstream chain verifies across the ${input.entries} event${input.entries === 1 ? "" : "s"} of the range; the audit excerpt copies as one verified record.` };
10714
+ }
10370
10715
 
10371
10716
  // progress.ts
10372
10717
  function emptyprogress(planid, now) {
@@ -10617,7 +10962,7 @@ function maskexport(record2, shapes) {
10617
10962
  }
10618
10963
 
10619
10964
  // version.ts
10620
- var packageversion = "1.1.62";
10965
+ var packageversion = "1.1.64";
10621
10966
 
10622
10967
  // types.ts
10623
10968
  var protocolversion = packageversion;
@@ -11594,6 +11939,9 @@ function environmentreport(input) {
11594
11939
  function transparencyreport(input) {
11595
11940
  return { version: protocolversion, posture: "denydefault", grants: input.grants, windows: input.windows, connectallow: input.connectallow, permdiffs: input.permdiffs, safedefaults: input.safedefaults, vault: input.vault };
11596
11941
  }
11942
+ function surfacesnapshot(input) {
11943
+ return { version: protocolversion, surface: input.surface, palette: input.palette, timeline: input.timeline, logstream: input.logstream, plancards: input.plancards, ...input.onboarding !== void 0 ? { onboarding: input.onboarding } : {} };
11944
+ }
11597
11945
 
11598
11946
  // capture.ts
11599
11947
  var capturekinds = ["shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet"];
@@ -12736,6 +13084,460 @@ function endcall(input) {
12736
13084
  return { contexts: input.contexts.map((candidate) => candidate.callid === input.callid ? context : candidate), context };
12737
13085
  }
12738
13086
 
13087
+ // sessioninterface.ts
13088
+ function textfingerprint(text2) {
13089
+ let hash = 2166136261;
13090
+ for (let index = 0; index < text2.length; index += 1) {
13091
+ hash ^= text2.charCodeAt(index);
13092
+ hash = Math.imul(hash, 16777619) >>> 0;
13093
+ }
13094
+ return hash.toString(16).padStart(8, "0");
13095
+ }
13096
+ function keystreambyte(id, position) {
13097
+ let hash = 2166136261;
13098
+ const source = `${id}:${position}`;
13099
+ for (let index = 0; index < source.length; index += 1) {
13100
+ hash ^= source.charCodeAt(index);
13101
+ hash = Math.imul(hash, 16777619) >>> 0;
13102
+ }
13103
+ return hash & 255;
13104
+ }
13105
+ function sealnotebody(id, body) {
13106
+ const sealed = Array.from(body, (character, index) => String.fromCharCode(character.charCodeAt(0) ^ keystreambyte(id, index))).join("");
13107
+ return `sealed:${btoa(sealed)}`;
13108
+ }
13109
+ function opennotebody(id, sealedbody) {
13110
+ if (!sealedbody.startsWith("sealed:")) return "";
13111
+ try {
13112
+ const sealed = atob(sealedbody.slice("sealed:".length));
13113
+ return Array.from(sealed, (character, index) => String.fromCharCode(character.charCodeAt(0) ^ keystreambyte(id, index))).join("");
13114
+ } catch {
13115
+ return "";
13116
+ }
13117
+ }
13118
+ function sitenoteof(input) {
13119
+ if (input.origin.trim() === "") throw new Error("The site note needs its origin.");
13120
+ if (input.title.trim() === "") throw new Error("The site note needs its title.");
13121
+ if (input.body.trim() === "") throw new Error("The site note needs its body.");
13122
+ const id = input.id ?? randomid();
13123
+ if (input.sensitive === true) return { id, origin: input.origin, title: input.title.trim(), sealedbody: sealnotebody(id, input.body), author: input.author, sensitive: true, createdat: input.now, updatedat: input.now };
13124
+ return { id, origin: input.origin, title: input.title.trim(), body: input.body, author: input.author, sensitive: false, createdat: input.now, updatedat: input.now };
13125
+ }
13126
+ function notebodyof(note) {
13127
+ if (note.sensitive) return note.sealedbody !== void 0 ? opennotebody(note.id, note.sealedbody) : "";
13128
+ return note.body ?? "";
13129
+ }
13130
+ function editnote(note, input) {
13131
+ if (input.title.trim() === "") throw new Error("The site note keeps a non empty title.");
13132
+ if (input.body.trim() === "") throw new Error("The site note keeps a non empty body.");
13133
+ if (note.sensitive) return { ...note, title: input.title.trim(), sealedbody: sealnotebody(note.id, input.body), updatedat: input.now, author: input.author };
13134
+ return { ...note, title: input.title.trim(), body: input.body, updatedat: input.now, author: input.author };
13135
+ }
13136
+ function scratchentryof(input) {
13137
+ if (input.taskid.trim() === "") throw new Error("The scratchpad entry needs its task.");
13138
+ if (input.text.trim() === "") throw new Error("The scratchpad entry needs its text.");
13139
+ return { id: input.id ?? randomid(), taskid: input.taskid, sessionid: input.sessionid, text: input.text, ...input.stepid !== void 0 && input.stepid.trim() !== "" ? { stepid: input.stepid } : {}, author: input.author, at: input.now };
13140
+ }
13141
+ function distillrunsummary(input) {
13142
+ const steps = input.outcomes.map((outcome) => {
13143
+ const step = input.plan.steps.find((candidate) => candidate.id === outcome.stepid);
13144
+ return { stepid: outcome.stepid, kind: step?.kind ?? "unknown", ok: outcome.ok, summary: outcome.summary };
13145
+ });
13146
+ const windowed = input.window !== void 0 && Number.isInteger(input.window) && input.window >= 0 ? steps.slice(Math.max(0, steps.length - input.window)) : steps;
13147
+ const kinds = [...new Set(windowed.map((step) => step.kind))];
13148
+ return { runid: input.plan.id, sessionid: input.sessionid, origins: [...new Set(input.origins)], kinds, steps: windowed, ...input.window !== void 0 && Number.isInteger(input.window) && input.window >= 0 ? { window: input.window } : {}, task: "runsummary", provenance: input.provenance, distilledat: input.now };
13149
+ }
13150
+ function summaryhistoryentry(summary) {
13151
+ return { source: "summary", id: summary.runid, title: `Run summary of ${summary.runid}`, text: `${summary.origins.join(" ")} ${summary.kinds.join(" ")} ${summary.steps.map((step) => step.summary).join(" ")}`, outcome: summary.steps.every((step) => step.ok) ? "completed" : "failed", at: summary.distilledat };
13152
+ }
13153
+ function notehistoryentry(note) {
13154
+ return { source: "note", id: note.id, ...note.origin !== "" ? { origin: note.origin } : {}, title: note.title, text: note.sensitive ? note.title : `${note.title} ${note.body ?? ""}`, at: note.updatedat };
13155
+ }
13156
+ function recallentryof(input) {
13157
+ if (input.text.trim() === "") throw new Error("The recall index entry needs its text.");
13158
+ if (input.stepid.trim() === "" || input.runid.trim() === "") throw new Error("The recall index entry needs its run and step provenance.");
13159
+ const normalized = input.text.trim().replace(/\s+/g, " ");
13160
+ return { fingerprint: textfingerprint(normalized), origin: input.origin, runid: input.runid, stepid: input.stepid, text: normalized, at: input.at };
13161
+ }
13162
+ function termsof(text2) {
13163
+ return new Set(text2.toLowerCase().split(/[^a-z0-9]+/).filter((term) => term.length > 1));
13164
+ }
13165
+ function rankrecall(index, query, scope) {
13166
+ if (query.text.trim() === "") return [];
13167
+ const terms = termsof(query.text);
13168
+ const scoped = query.origin !== void 0 && query.origin.trim() !== "" ? [query.origin] : scope.origins;
13169
+ const matches = [];
13170
+ for (const entry of index) {
13171
+ if (!scoped.includes(entry.origin)) continue;
13172
+ const entryterms = termsof(entry.text);
13173
+ let shared = 0;
13174
+ for (const term of terms) if (entryterms.has(term)) shared += 1;
13175
+ const union = (/* @__PURE__ */ new Set([...terms, ...entryterms])).size;
13176
+ const score = union === 0 ? 0 : shared / union;
13177
+ if (score <= 0) continue;
13178
+ matches.push({ entry, score, reason: `The extraction of ${entry.origin} shares ${shared} term${shared === 1 ? "" : "s"} with the query at the score ${score.toFixed(3)}; the match carries the run ${entry.runid} and the step ${entry.stepid}.` });
13179
+ }
13180
+ const ranked = matches.sort((one, two) => two.score - one.score);
13181
+ return query.limit !== void 0 && Number.isInteger(query.limit) && query.limit >= 0 ? ranked.slice(0, query.limit) : ranked;
13182
+ }
13183
+ function editedcorrectionof(input) {
13184
+ if (input.stepid.trim() === "" || input.kind.trim() === "") throw new Error("The correction needs its step and kind.");
13185
+ if (input.original === input.corrected) throw new Error("The correction needs a changed step shape.");
13186
+ return { id: input.id ?? randomid(), origin: input.origin, kind: input.kind, stepid: input.stepid, source: "edited", original: input.original, corrected: input.corrected, reason: input.reason, at: input.now };
13187
+ }
13188
+ function rejectedcorrectionof(input) {
13189
+ if (input.stepid.trim() === "" || input.reason.trim() === "") throw new Error("The rejected correction needs its step and its rejection reason.");
13190
+ return { id: input.id ?? randomid(), origin: input.origin, kind: input.kind, stepid: input.stepid, source: "rejected", original: input.original, reason: input.reason, at: input.now };
13191
+ }
13192
+ function matchingcorrections(corrections, proposal) {
13193
+ return corrections.filter((entry) => entry.origin === proposal.origin && entry.kind === proposal.kind);
13194
+ }
13195
+ function consentmemoryof(input) {
13196
+ if (input.origin.trim() === "") throw new Error("The consent memory entry needs its origin.");
13197
+ if (input.boundary.trim() === "") throw new Error("The consent memory entry needs the boundary the prompt named.");
13198
+ return { id: input.id ?? randomid(), origin: input.origin, decision: input.decision, boundary: input.boundary, kinds: [...new Set(input.kinds)], at: input.now, ...input.expiresat !== void 0 ? { expiresat: input.expiresat } : {} };
13199
+ }
13200
+ function consentadvisoryverdict(entries, origin, kind) {
13201
+ const matching = entries.filter((entry) => entry.origin === origin && entry.kinds.includes(kind));
13202
+ const latest = matching[matching.length - 1];
13203
+ if (latest === void 0) return { advisory: false, reason: `No prior decision exists for the ${kind} kind on ${origin}; the prompt opens fresh.` };
13204
+ if (latest.decision === "deny") return { advisory: true, reason: `The consent memory holds a prior denial of the ${kind} kind on ${origin} from ${new Date(latest.at).toISOString()}; the denial carries the same weight as a grant and the record stays advisory only.` };
13205
+ return { advisory: true, reason: `The consent memory holds a prior ${latest.decision} of the ${kind} kind on ${origin} with the boundary ${latest.boundary}; the record advises the new prompt and never auto grants.` };
13206
+ }
13207
+ function rollbacksplit(plan, progress) {
13208
+ const executed = progress && progress.planid === plan?.id ? progress.completedsteps : [];
13209
+ const executedset = new Set(executed);
13210
+ const queued = (plan?.steps ?? []).map((step) => step.id).filter((id) => !executedset.has(id));
13211
+ return { executedstepids: executed, queuedstepids: queued };
13212
+ }
13213
+ function rollbackof(plan, progress, preference) {
13214
+ const split = rollbacksplit(plan, progress);
13215
+ if (preference === "none") return { scope: "none", label: `Stop the run ${plan?.id ?? ""} without a rollback; the ${split.queuedstepids.length} queued step${split.queuedstepids.length === 1 ? "" : "s"} stay as the run left them.`, queuedstepids: split.queuedstepids };
13216
+ return { scope: "queued", label: `Cancel the run ${plan?.id ?? ""} and roll its ${split.queuedstepids.length} queued step${split.queuedstepids.length === 1 ? "" : "s"} back${split.queuedstepids.length > 0 ? ` (${split.queuedstepids.join(", ")})` : ""} while the ${split.executedstepids.length} executed step${split.executedstepids.length === 1 ? "" : "s"} stay untouched in the sealed log.`, queuedstepids: split.queuedstepids };
13217
+ }
13218
+ function cancelrunactionof(input) {
13219
+ return { runid: input.runid, sessionid: input.sessionid, rollback: rollbackof(input.plan, input.progress, input.preference) };
13220
+ }
13221
+ function errorsurfaceof(input) {
13222
+ if (input.message.trim() === "") throw new Error("The error surface needs its message in plain language.");
13223
+ return { stepid: input.stepid, runid: input.runid, cause: input.cause, message: input.message, retry: { allowed: input.retryallowed, reason: input.retryreason }, context: input.context, at: input.now };
13224
+ }
13225
+ function classifyfailure(input) {
13226
+ if (input.gatewait) return "gate";
13227
+ if (input.policyrefused) return "policy";
13228
+ if (/\b(network|offline|timeout|timed out|fetch failed|socket|dns|connection)\b/i.test(input.message)) return "network";
13229
+ return "page";
13230
+ }
13231
+ function sessiongridrows(input) {
13232
+ const rows = [];
13233
+ if (input.session && input.plan && ["pending", "approved"].includes(input.plan.state)) {
13234
+ const split = rollbacksplit(input.plan, input.progress);
13235
+ const held = input.locks.some((lock) => lock.runid === input.plan?.id);
13236
+ const origins = [.../* @__PURE__ */ new Set([input.session.origin, ...input.session.grants ?? []])];
13237
+ const actions = ["cancelrun"];
13238
+ if (input.session.pausedat !== void 0) actions.push("resume");
13239
+ rows.push({ sessionid: input.session.id, runid: input.plan.id, origins, state: "live", outcome: `${split.executedstepids.length} of ${input.plan.steps.length} reviewed steps executed`, steps: input.plan.steps.length, completed: split.executedstepids.length, lock: held ? "held" : "free", tabid: input.session.tabid, updatedat: input.plan.createdat, actions });
13240
+ }
13241
+ for (const log of input.logs) {
13242
+ const summary = input.summaries.find((candidate) => candidate.runid === log.runid);
13243
+ const tabsession = input.tabsessions.find((candidate) => candidate.runid === log.runid);
13244
+ const held = input.locks.some((lock) => lock.runid === log.runid);
13245
+ rows.push({ sessionid: log.sessionid, runid: log.runid, origins: [...new Set(log.entries.map((entry) => entry.origin).filter((origin) => origin !== ""))], state: "saved", outcome: summary !== void 0 ? summary.steps.every((step) => step.ok) ? "completed" : "failed" : log.seal !== void 0 ? "sealed" : "open", steps: summary?.steps.length ?? log.entries.filter((entry) => entry.kind === "step").length, completed: summary?.steps.filter((step) => step.ok).length ?? log.entries.filter((entry) => entry.kind === "step").length, lock: held ? "held" : "free", ...log.seal !== void 0 ? { sealhash: log.seal.sealhash.current } : {}, ...tabsession !== void 0 ? { tabid: tabsession.tabid } : {}, updatedat: log.updatedat, actions: ["reopen"] });
13246
+ }
13247
+ return rows.sort((one, two) => two.updatedat - one.updatedat);
13248
+ }
13249
+ function historyqueryof(value) {
13250
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
13251
+ const candidate = value;
13252
+ if (typeof candidate.text !== "string" || candidate.text.trim() === "") return void 0;
13253
+ const origin = typeof candidate.origin === "string" && candidate.origin.trim() !== "" ? candidate.origin.trim() : void 0;
13254
+ const from = typeof candidate.from === "number" && Number.isFinite(candidate.from) ? candidate.from : void 0;
13255
+ const to = typeof candidate.to === "number" && Number.isFinite(candidate.to) ? candidate.to : void 0;
13256
+ if (from !== void 0 && to !== void 0 && from > to) return void 0;
13257
+ const outcome = typeof candidate.outcome === "string" && candidate.outcome.trim() !== "" ? candidate.outcome.trim() : void 0;
13258
+ return { text: candidate.text.trim(), ...origin !== void 0 ? { origin } : {}, ...from !== void 0 ? { from } : {}, ...to !== void 0 ? { to } : {}, ...outcome !== void 0 ? { outcome } : {} };
13259
+ }
13260
+ function historysearch(corpus, query) {
13261
+ const terms = query.text.toLowerCase().split(/[^a-z0-9]+/).filter((term) => term.length > 1);
13262
+ const hits = [];
13263
+ for (const entry of corpus) {
13264
+ if (query.origin !== void 0 && entry.origin !== query.origin) continue;
13265
+ if (query.from !== void 0 && entry.at < query.from) continue;
13266
+ if (query.to !== void 0 && entry.at > query.to) continue;
13267
+ if (query.outcome !== void 0 && entry.outcome !== query.outcome) continue;
13268
+ const haystack = `${entry.title} ${entry.text}`.toLowerCase();
13269
+ const matched = terms.filter((term) => haystack.includes(term));
13270
+ if (matched.length === 0) continue;
13271
+ const position = haystack.indexOf(matched[0] ?? "");
13272
+ const start = Math.max(0, position - 40);
13273
+ const excerpt = `${start > 0 ? "\u2026" : ""}${`${entry.title} ${entry.text}`.slice(start, start + 160)}${start + 160 < `${entry.title} ${entry.text}`.length ? "\u2026" : ""}`;
13274
+ hits.push({ source: entry.source, id: entry.id, title: entry.title, excerpt, highlights: [...new Set(matched)], ...entry.origin !== void 0 ? { origin: entry.origin } : {}, ...entry.outcome !== void 0 ? { outcome: entry.outcome } : {}, at: entry.at });
13275
+ }
13276
+ return hits.sort((one, two) => two.at - one.at);
13277
+ }
13278
+ function tabsessionrefof(input) {
13279
+ if (!Number.isInteger(input.tabid) || input.tabid < 0) throw new Error("The per tab session reference needs its tab.");
13280
+ if (input.sessionid.trim() === "") throw new Error("The per tab session reference needs its session.");
13281
+ return { tabid: input.tabid, sessionid: input.sessionid, ...input.runid !== void 0 && input.runid.trim() !== "" ? { runid: input.runid } : {}, origin: input.origin, updatedat: input.now };
13282
+ }
13283
+
13284
+ // surfaces.ts
13285
+ function surfacepalette() {
13286
+ return [
13287
+ { id: "starttask", label: "Start task", keywords: ["task", "objective", "run", "goal", "plan"], action: { command: "starttask", surface: "popup" } },
13288
+ { id: "pauserun", label: "Pause run", keywords: ["pause", "hold", "stop", "run"], action: { command: "pauserun", surface: "popup", session: true } },
13289
+ { id: "resumerun", label: "Resume run", keywords: ["resume", "continue", "unpause", "run"], action: { command: "resumerun", surface: "popup", session: true } },
13290
+ { id: "cancelrun", label: "Cancel run", keywords: ["cancel", "stop", "rollback", "queued"], action: { command: "cancelrun", surface: "popup", session: true } },
13291
+ { id: "resumesession", label: "Resume session", keywords: ["session", "resume", "grid", "reopen"], action: { command: "resumesession", surface: "sidepanel" } },
13292
+ { id: "stepapprove", label: "Review step", keywords: ["approve", "reject", "edit", "step", "review", "plancard"], action: { command: "stepapprove", surface: "sidepanel", session: true } },
13293
+ { id: "diffpreview", label: "Preview step diff", keywords: ["diff", "preview", "before", "after", "write"], action: { command: "diffpreview", surface: "sidepanel", session: true } },
13294
+ { id: "historysearch", label: "Search history", keywords: ["history", "search", "notes", "summaries", "corpus"], action: { command: "historysearch", surface: "dashboardpage" } },
13295
+ { id: "revokeconsent", label: "Revoke consent", keywords: ["revoke", "consent", "allowlist", "origin", "grant"], action: { command: "revokeconsent", surface: "dashboardpage", session: true } },
13296
+ { id: "opentransparencypage", label: "Open transparency page", keywords: ["transparency", "grants", "permissions", "diff"], action: { command: "opentransparencypage", surface: "optionspage" } },
13297
+ { id: "opendashboardpage", label: "Open dashboard", keywords: ["dashboard", "sessions", "runs", "notes", "full"], action: { command: "opendashboardpage", surface: "dashboardpage" } },
13298
+ { id: "openoptionspage", label: "Open options", keywords: ["options", "settings", "preferences", "configure"], action: { command: "openoptionspage", surface: "optionspage" } },
13299
+ { id: "copyauditexcerpt", label: "Copy audit excerpt", keywords: ["audit", "excerpt", "copy", "verified", "range"], action: { command: "copyauditexcerpt", surface: "dashboardpage" } },
13300
+ { id: "replayonboarding", label: "Replay onboarding", keywords: ["onboarding", "tour", "walkthrough", "replay", "first"], action: { command: "replayonboarding", surface: "onboarding" } }
13301
+ ];
13302
+ }
13303
+ function palettecommandsof(entries, input) {
13304
+ return entries.filter((entry) => paletteactiongate({ action: entry.action, granted: input.granted, sessionactive: input.sessionactive }).allowed);
13305
+ }
13306
+ function fuzzyentryscore(entry, query) {
13307
+ const text2 = query.trim().toLowerCase();
13308
+ if (text2 === "") return 1;
13309
+ const id = entry.id.toLowerCase();
13310
+ const label = entry.label.toLowerCase();
13311
+ if (id === text2 || label === text2) return 100;
13312
+ let score = 0;
13313
+ if (id.includes(text2)) score += 40;
13314
+ if (label.includes(text2)) score += 30;
13315
+ for (const keyword of entry.keywords) {
13316
+ const lower = keyword.toLowerCase();
13317
+ if (lower === text2) score += 20;
13318
+ else if (lower.includes(text2)) score += 10;
13319
+ }
13320
+ if (score === 0 && text2.length > 1) {
13321
+ for (const haystack of [label, id]) {
13322
+ let cursor = 0;
13323
+ let matched = true;
13324
+ for (const letter of text2) {
13325
+ const found = haystack.indexOf(letter, cursor);
13326
+ if (found === -1) {
13327
+ matched = false;
13328
+ break;
13329
+ }
13330
+ cursor = found + 1;
13331
+ }
13332
+ if (matched) {
13333
+ score += 15;
13334
+ break;
13335
+ }
13336
+ }
13337
+ }
13338
+ return score;
13339
+ }
13340
+ function palettequery(entries, input) {
13341
+ const text2 = input.text.trim();
13342
+ const matches = entries.map((entry) => ({ entry, score: fuzzyentryscore(entry, text2) })).filter((match) => match.score > 0);
13343
+ const lastusedof = (command) => input.usage.find((record2) => record2.command === command)?.lastusedat ?? 0;
13344
+ const countof = (command) => input.usage.find((record2) => record2.command === command)?.count ?? 0;
13345
+ const recentwindow = input.recentwindow;
13346
+ const ranked = matches.sort((left, right) => {
13347
+ if (right.score !== left.score) return right.score - left.score;
13348
+ const leftrecent = recentwindow === void 0 ? 0 : countof(left.entry.action.command) > 0 && lastusedof(left.entry.action.command) >= lastusedof(right.entry.action.command) ? 1 : 0;
13349
+ const rightrecent = recentwindow === void 0 ? 0 : countof(right.entry.action.command) > 0 && lastusedof(right.entry.action.command) >= lastusedof(left.entry.action.command) ? 1 : 0;
13350
+ if (rightrecent !== leftrecent) return rightrecent - leftrecent;
13351
+ return lastusedof(right.entry.action.command) - lastusedof(left.entry.action.command);
13352
+ });
13353
+ return ranked.map((match) => ({ entry: match.entry, score: match.score, reason: match.score >= 100 ? `The query matches the ${match.entry.id} command exactly.` : `The query matches the label or the keywords of the ${match.entry.id} command${countof(match.entry.action.command) > 0 ? ` and its ${countof(match.entry.action.command)} recorded use${countof(match.entry.action.command) === 1 ? "" : "s"} rank it first among equals` : ""}.` }));
13354
+ }
13355
+ function paletteuseafter(usage, command, now) {
13356
+ const existing = usage.find((record2) => record2.command === command);
13357
+ if (existing === void 0) return [{ command, count: 1, lastusedat: now }, ...usage];
13358
+ return usage.map((record2) => record2.command === command ? { ...record2, count: record2.count + 1, lastusedat: now } : record2);
13359
+ }
13360
+ function taskinputof(input) {
13361
+ if (input.text.trim() === "") throw new Error("The taskinput needs its natural language goal.");
13362
+ if (input.origin.trim() === "") throw new Error("The taskinput needs its active origin scope.");
13363
+ return { id: randomid(), text: input.text.trim(), context: input.context ?? "", origin: input.origin.trim(), surface: input.surface, at: input.at };
13364
+ }
13365
+ function onboardingsteps() {
13366
+ return [
13367
+ { id: "origingrants", surface: "popup", title: "Origin grants", body: "Devthink denies automation by default; grant one exact origin at a time from the popup and every run stays inside the granted origins.", completion: "origingrantscompleted" },
13368
+ { id: "planreview", surface: "sidepanel", title: "Plan review", body: "Every task becomes a plan of reviewed steps; read the plancards of each risk class and approve, reject or edit one step at a time.", completion: "planreviewcompleted" },
13369
+ { id: "runcontrol", surface: "sidepanel", title: "Run control", body: "Runs start, pause, resume and cancel under your hand; a cancelled run rolls only its queued steps back while the executed steps stay sealed.", completion: "runcontrolcompleted" },
13370
+ { id: "logaudit", surface: "dashboardpage", title: "Log audit", body: "The immutable log chains every step transition with masked values; open the dashboard, verify the chain and copy a verified range as an audit excerpt.", completion: "logauditcompleted" }
13371
+ ];
13372
+ }
13373
+ function onboardingstart(previous, now) {
13374
+ return { stepscompleted: [], done: false, startedat: now };
13375
+ }
13376
+ function onboardingcomplete(state, stepid, now) {
13377
+ const steps = onboardingsteps();
13378
+ const step = steps.find((candidate) => candidate.id === stepid);
13379
+ if (step === void 0) throw new Error(`The onboarding knows no ${stepid} step.`);
13380
+ const completed = state.stepscompleted.includes(stepid) ? state.stepscompleted : [...state.stepscompleted, stepid];
13381
+ const done = steps.every((candidate) => completed.includes(candidate.id));
13382
+ if (!done) return { state: { ...state, stepscompleted: completed, done: false } };
13383
+ const consentevent = "onboardingconsentgranted";
13384
+ return { state: { stepscompleted: completed, done: true, ...state.startedat !== void 0 ? { startedat: state.startedat } : {}, consentevent, completedat: now }, consentevent };
13385
+ }
13386
+ function broadcastframeof(input) {
13387
+ if (input.summary.trim() === "") throw new Error("The broadcast frame needs its summary.");
13388
+ return { channel: input.channel, surface: input.surface, summary: input.summary, at: input.at };
13389
+ }
13390
+ function broadcastchannelof(kind) {
13391
+ if (["session", "proposal", "approval", "action", "stop", "pause", "resume", "complete", "cancel", "error", "capability"].includes(kind)) return "runstate";
13392
+ if (["notes", "scratchpad", "summary", "recall", "correction", "consentmemory", "search", "vault", "gate", "grant", "revoke", "expiry", "deny"].includes(kind)) return "sessions";
13393
+ if (["configure", "transparency"].includes(kind)) return "settings";
13394
+ return "logstream";
13395
+ }
13396
+ function busrouteaction(action, input) {
13397
+ const entry = surfacepalette().find((candidate) => candidate.action.command === action.command);
13398
+ if (entry === void 0) return { dispatched: false, gate: "commandbus", reason: `The ${action.surface} asked for the unknown ${action.command} command; the bus routes only catalog commands.` };
13399
+ const permission = paletteactiongate({ action: entry.action, granted: input.granted, sessionactive: input.sessionactive });
13400
+ if (!permission.allowed) return { dispatched: false, gate: "paletteactiongate", reason: permission.reason ?? "The command misses its granted permission." };
13401
+ if (action.command === "starttask") {
13402
+ const proposal = taskinputproposalgate({ text: input.text ?? "", origin: input.origin ?? "", direct: false });
13403
+ if (!proposal.allowed) return { dispatched: false, gate: "taskinputproposalgate", reason: proposal.reason ?? "The task submission refuses." };
13404
+ }
13405
+ if (action.command === "stepapprove" || action.command === "diffpreview") {
13406
+ const review = planreviewgate({ reviewed: input.planreviewed, state: input.planstate });
13407
+ if (!review.allowed) return { dispatched: false, gate: "planreviewgate", reason: review.reason ?? "The plan review stays open." };
13408
+ }
13409
+ return { dispatched: true, gate: "commandbus", reason: `The ${action.command} action of the ${action.surface} routed through its policy gates and dispatches.` };
13410
+ }
13411
+
13412
+ // planreview.ts
13413
+ function plancardsof(input) {
13414
+ return input.plan.steps.map((step) => ({
13415
+ stepid: step.id,
13416
+ kind: step.kind,
13417
+ risk: step.risk,
13418
+ environment: step.environment ?? defaultenvironment(step),
13419
+ options: step.options ?? "",
13420
+ summary: step.summary,
13421
+ corrections: matchingcorrections(input.corrections, { origin: input.plan.origin, kind: step.kind }).map((entry) => ({ id: entry.id, source: entry.source, reason: entry.reason })),
13422
+ editable: input.plan.state === "pending"
13423
+ }));
13424
+ }
13425
+ function plancardgroups(cards) {
13426
+ const order = ["sensitive", "interaction", "read"];
13427
+ return order.map((risk) => ({ risk, cards: cards.filter((card) => card.risk === risk), expanded: risk === "sensitive" })).filter((group) => group.cards.length > 0);
13428
+ }
13429
+ function stepresolutionof(input) {
13430
+ if (input.stepid.trim() === "") throw new Error("The stepapprove resolution needs its step.");
13431
+ if (input.resolution === "edit" && (input.edited ?? "").trim() === "") throw new Error("The edited resolution needs its corrected step shape.");
13432
+ return { stepid: input.stepid, planid: input.planid, origin: input.origin, resolution: input.resolution, surface: input.surface, ...input.edited !== void 0 && input.edited.trim() !== "" ? { edited: input.edited } : {}, at: input.at };
13433
+ }
13434
+ function resolutionlogeventof(resolution) {
13435
+ return {
13436
+ kind: "review",
13437
+ stepid: resolution.stepid,
13438
+ summary: resolution.resolution === "edit" ? `The user edited the step ${resolution.stepid} of the plan ${resolution.planid} from the ${resolution.surface} before approving; the corrected shape rides the plan.` : `The user ${resolution.resolution === "approve" ? "approved" : "rejected"} the step ${resolution.stepid} of the plan ${resolution.planid} from the ${resolution.surface}; one distinct human action resolved the step alone.`
13439
+ };
13440
+ }
13441
+ function maskverdictsof(state, sensitivefields) {
13442
+ const verdicts = {};
13443
+ for (const [field, value] of Object.entries(state)) {
13444
+ if (sensitivefields.includes(field)) verdicts[field] = `The ${field} value stays masked (${value.length} characters) and never renders in the clear.`;
13445
+ }
13446
+ return verdicts;
13447
+ }
13448
+ function diffpreviewof(input) {
13449
+ const changes = [];
13450
+ const fields = [.../* @__PURE__ */ new Set([...Object.keys(input.before), ...Object.keys(input.after)])];
13451
+ for (const field of fields) {
13452
+ const hasbefore = Object.prototype.hasOwnProperty.call(input.before, field);
13453
+ const hasafter = Object.prototype.hasOwnProperty.call(input.after, field);
13454
+ const beforevalue = input.before[field];
13455
+ const aftervalue = input.after[field];
13456
+ if (hasbefore && !hasafter && beforevalue !== void 0) changes.push({ field, kind: "removed", before: beforevalue });
13457
+ else if (!hasbefore && hasafter && aftervalue !== void 0) changes.push({ field, kind: "added", after: aftervalue });
13458
+ else if (hasbefore && hasafter && beforevalue !== void 0 && aftervalue !== void 0 && beforevalue !== aftervalue) changes.push({ field, kind: "changed", before: beforevalue, after: aftervalue });
13459
+ }
13460
+ return { stepid: input.stepid, before: input.before, after: input.after, changes, maskverdicts: input.maskverdicts ?? {}, provenance: input.provenance };
13461
+ }
13462
+ function stepstimelinenodes(input) {
13463
+ const completed = input.progress?.completedsteps ?? [];
13464
+ const outcomes = input.progress?.outcomes ?? [];
13465
+ const environments = input.progress?.environments;
13466
+ const turnarounds = input.progress?.turnarounds;
13467
+ const gatewaits = input.progress?.gatewaits;
13468
+ let activeset = false;
13469
+ let blocked = false;
13470
+ return input.plan.steps.map((step) => {
13471
+ const outcome = [...outcomes].reverse().find((candidate) => candidate.stepid === step.id);
13472
+ const gatewait = gatewaits?.[step.id];
13473
+ let status;
13474
+ if (outcome !== void 0) status = outcome.ok ? "done" : "failed";
13475
+ else if (gatewait !== void 0) status = "waiting";
13476
+ else if (completed.includes(step.id)) status = "done";
13477
+ else if (input.plan.state === "cancelled" || input.plan.state === "expired") status = "halted";
13478
+ else if (input.plan.state === "rejected") status = "halted";
13479
+ else if (input.plan.state === "approved" && !activeset && !blocked) {
13480
+ status = "running";
13481
+ activeset = true;
13482
+ } else status = "pending";
13483
+ if (status === "waiting") blocked = true;
13484
+ const active = status === "running";
13485
+ return {
13486
+ stepid: step.id,
13487
+ kind: step.kind,
13488
+ status,
13489
+ ...turnarounds?.[step.id] !== void 0 ? { durationms: turnarounds[step.id] } : {},
13490
+ ...environments?.[step.id] !== void 0 ? { environment: environments[step.id] } : step.environment !== void 0 ? { environment: step.environment } : {},
13491
+ active,
13492
+ anchor: `#step-${step.id}`,
13493
+ ...outcome !== void 0 ? { resultsummary: outcome.summary } : {}
13494
+ };
13495
+ });
13496
+ }
13497
+ var logstreamgenesis = "0".repeat(64);
13498
+ async function logstreameventof(input) {
13499
+ if (input.summary.trim() === "") throw new Error("The logstream event needs its summary.");
13500
+ const id = randomid();
13501
+ const hash = await entryhashof({ previous: input.previous, entry: { id, runid: "surfaces", kind: "step", summary: input.summary, origin: input.origin, ...input.stepid !== void 0 ? { stepid: input.stepid } : {}, at: input.at } });
13502
+ return { id, level: input.level, source: input.source, origin: input.origin, summary: input.summary, ...input.stepid !== void 0 ? { stepid: input.stepid } : {}, masked: input.masked, maskverdict: input.maskverdict, hash, at: input.at };
13503
+ }
13504
+ function appendlogstreamevent(events, event) {
13505
+ return [...events, event];
13506
+ }
13507
+ function filterlogstream(events, filter) {
13508
+ return events.filter((event) => (filter.level === void 0 || event.level === filter.level) && (filter.origin === void 0 || filter.origin === "" || event.origin === filter.origin) && (filter.stepid === void 0 || filter.stepid === "" || event.stepid === filter.stepid));
13509
+ }
13510
+ function livebufferof(events, bound) {
13511
+ if (bound === void 0) return events;
13512
+ if (!Number.isInteger(bound) || bound <= 0) return events;
13513
+ return events.slice(-bound);
13514
+ }
13515
+ async function verifylogstream(events) {
13516
+ for (let index = 0; index < events.length; index += 1) {
13517
+ const event = events[index];
13518
+ if (event === void 0) continue;
13519
+ const predecessor = events[index - 1];
13520
+ const expectedprevious = index === 0 || predecessor === void 0 ? logstreamgenesis : predecessor.hash.current;
13521
+ if (event.hash.previous !== expectedprevious) return { valid: false, brokenat: index, reason: `The logstream chain breaks at the event ${event.id}: its previous hash does not link to its predecessor.` };
13522
+ const recomputed = await entryhashof({ previous: event.hash.previous, entry: { id: event.id, runid: "surfaces", kind: "step", summary: event.summary, origin: event.origin, ...event.stepid !== void 0 ? { stepid: event.stepid } : {}, at: event.at } });
13523
+ if (recomputed.current !== event.hash.current) return { valid: false, brokenat: index, reason: `The logstream chain breaks at the event ${event.id}: its own hash does not reproduce.` };
13524
+ }
13525
+ return { valid: true, reason: `The logstream chain of ${events.length} event${events.length === 1 ? "" : "s"} verifies link by link.` };
13526
+ }
13527
+ async function auditexcerptof(events, input) {
13528
+ if (input.from < 0 || input.to <= input.from || input.to > events.length) return { ok: false, text: "", reason: `The excerpt range ${input.from} to ${input.to} names no contiguous slice of the ${events.length} event${events.length === 1 ? "" : "s"}.` };
13529
+ const range = events.slice(input.from, input.to);
13530
+ const verification = await verifylogstream(range);
13531
+ if (!verification.valid) return { ok: false, text: "", reason: `The excerpt refuses the copy: ${verification.reason}` };
13532
+ const text2 = range.map((event) => `[${event.at}] ${event.level} ${event.source}${event.stepid !== void 0 ? ` step ${event.stepid}` : ""} ${event.origin} \u2014 ${event.summary}${event.masked ? ` (${event.maskverdict})` : ""}`).join("\n");
13533
+ return { ok: true, text: text2, reason: `The excerpt copied the verified range ${input.from} to ${input.to} of the logstream.` };
13534
+ }
13535
+ function loglevelof(kind) {
13536
+ if (kind === "error") return "error";
13537
+ if (["deny", "revoke", "stop", "quarantine", "phish", "defer", "schema", "expiry"].includes(kind)) return "warn";
13538
+ return "info";
13539
+ }
13540
+
12739
13541
  // llm.ts
12740
13542
  var defaultrefusalmarkers = ["i cannot", "i can't", "i'm unable", "refusal:", "cannot comply"];
12741
13543
  function buildrequest(input) {
@@ -14934,6 +15736,31 @@ function extensionpage(sender) {
14934
15736
  }
14935
15737
  async function audit(kind, summary, extra = {}) {
14936
15738
  await memory.addaudi({ id: randomid(), kind, at: Date.now(), summary, ...extra });
15739
+ await recordsurfaceevent(kind, summary, extra);
15740
+ }
15741
+ var surfacechannel = typeof BroadcastChannel === "function" ? new BroadcastChannel("devthinksurfaces") : void 0;
15742
+ var logstreamhistory = [];
15743
+ var recentframes = [];
15744
+ async function recordsurfaceevent(kind, summary, extra = {}) {
15745
+ try {
15746
+ const session = await memory.getsession();
15747
+ const origin = session?.origin ?? "";
15748
+ if (origin === "") return;
15749
+ const at = Date.now();
15750
+ const lastevent = logstreamhistory[logstreamhistory.length - 1];
15751
+ const previous = lastevent !== void 0 ? lastevent.hash.current : logstreamgenesis;
15752
+ const event = await logstreameventof({ level: loglevelof(kind), source: "background", origin, summary, ...extra.stepid !== void 0 ? { stepid: extra.stepid } : {}, masked: extra.masked === true, maskverdict: extra.masked === true ? extra.maskverdict ?? "The source payload stayed masked before it streamed." : "The source payload carries no masked value.", previous, at });
15753
+ logstreamhistory = appendlogstreamevent(logstreamhistory, event);
15754
+ const frame = broadcastframeof({ channel: broadcastchannelof(kind), surface: "background", summary, at });
15755
+ recentframes = [...recentframes, frame];
15756
+ surfacechannel?.postMessage(frame);
15757
+ } catch {
15758
+ }
15759
+ }
15760
+ async function broadcastsurfaceframe(input) {
15761
+ const frame = broadcastframeof({ ...input, at: Date.now() });
15762
+ recentframes = [...recentframes, frame];
15763
+ surfacechannel?.postMessage(frame);
14937
15764
  }
14938
15765
  function stepoptions6(step) {
14939
15766
  try {
@@ -15471,6 +16298,10 @@ async function startsession() {
15471
16298
  const { tab, origin } = await activecontext();
15472
16299
  const session = { id: randomid(), tabid: tab.id, origin, startedat: Date.now(), expiresat: Date.now() + sessionduration, grants: [origin] };
15473
16300
  await memory.setsession(session);
16301
+ await memory.settabsession(tabsessionrefof({ tabid: session.tabid, sessionid: session.id, origin, now: session.startedat }));
16302
+ await memory.tracktabsession(session.tabid);
16303
+ await memory.addconsentmemoryentry(consentmemoryof({ origin, decision: "grant", boundary: `the session expiry at ${new Date(session.expiresat).toISOString()}`, kinds: ["observe"], now: session.startedat }));
16304
+ if ((await memory.getsettings())?.historyindex !== false) await memory.addhistoryentry({ source: "session", id: session.id, origin, title: `Session of ${origin}`, text: `session ${origin} started ${new Date(session.startedat).toISOString()}`, at: session.startedat });
15474
16305
  await memory.addallowlistorigin({ origin, profileid: runstateprofile, grantedat: session.startedat });
15475
16306
  const scope = scopegrantof({ origin, kinds: ["observe"], boundary: `the session expiry at ${new Date(session.expiresat).toISOString()}`, now: session.startedat });
15476
16307
  let runlog = openrunlog({ runid: session.id, sessionid: session.id, now: session.startedat });
@@ -20420,6 +21251,8 @@ async function executeaction(step, session, plan, tabid2, origin, settings, verd
20420
21251
  }
20421
21252
  await audit("deny", `The ${step.kind} step ${step.id} on ${origin} was denied without navigation: ${securityverdict.reason}`, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {}, stepid: step.id });
20422
21253
  if (securityverdict.suspended && session) {
21254
+ await memory.addconsentmemoryentry(consentmemoryof({ origin, decision: "expire", boundary: "the consent window boundary that expired mid step", kinds: [step.kind], now: Date.now() })).catch(() => {
21255
+ });
20423
21256
  await appendrunevent("expiry", `The consent window of ${origin} expired mid step and the run suspended at the step ${step.id}; a new explicit prompt renews it.`, session, origin, step.id).catch(() => {
20424
21257
  });
20425
21258
  await audit("expiry", `The consent window of ${origin} expired mid step and the run suspended at the step ${step.id}; the executor refuses to resume without a new explicit prompt.`, { sessionid: session.id, ...plan ? { planid: plan.id } : {}, stepid: step.id });
@@ -20559,6 +21392,17 @@ async function executeaction(step, session, plan, tabid2, origin, settings, verd
20559
21392
  const auditkind = stepauditkind(step, Boolean(output?.ok));
20560
21393
  await audit(auditkind, summary, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {}, stepid: step.id });
20561
21394
  await memory.addoutcome(outcome);
21395
+ if (session && plan) await memory.appendscratchentry(scratchentryof({ taskid: plan.id, sessionid: session.id, text: `${step.kind} ${step.id} ${outcome.ok ? "completed" : "failed"}: ${summary}`, stepid: step.id, author: "agent", now: Date.now() })).catch(() => {
21396
+ });
21397
+ if (plan) await memory.addrecallentry(recallentryof({ origin, runid: plan.id, stepid: step.id, text: summary, at: Date.now() })).catch(() => {
21398
+ });
21399
+ let stepretry;
21400
+ if (!outcome.ok && plan) {
21401
+ const surface = errorsurfaceof({ stepid: step.id, runid: plan.id, message: summary, cause: classifyfailure({ message: summary, policyrefused: false, gatewait: false }), retryallowed: true, retryreason: "The failed step may dispatch again through the full consent gate chain.", context: { origin, kind: step.kind, environment: routing.environment }, now: Date.now() });
21402
+ await memory.adderrorsurface(surface).catch(() => {
21403
+ });
21404
+ stepretry = { allowed: true, reason: `The ${surface.cause} failure of the step ${step.id} may retry through a new reviewed dispatch; the retry rides the full consent gate chain and never bypasses the review.` };
21405
+ }
20562
21406
  if (session) await appendrunevent("step", `The ${step.kind} step ${step.id} ${outcome.ok ? "completed" : "failed"} on ${origin}: ${summary}${securityverdict.classification.sensitive ? ` The step grades ${securityverdict.classification.reason}` : ""}`, session, origin, step.id).catch(() => {
20563
21407
  });
20564
21408
  if (output?.ok && plan && mode === "plan") {
@@ -20590,11 +21434,36 @@ async function executeaction(step, session, plan, tabid2, origin, settings, verd
20590
21434
  const done = { ...plan, state: "completed", completedat: Date.now() };
20591
21435
  await memory.setplan(done);
20592
21436
  await closeplanrun(done.id, session?.id ?? "");
21437
+ await distillcompletedrun(done, tracked, session, settings).catch(() => {
21438
+ });
20593
21439
  await audit("complete", "Every reviewed step of the approved plan has executed.", { ...session ? { sessionid: session.id } : {}, planid: done.id });
20594
21440
  }
20595
21441
  }
21442
+ if (stepretry !== void 0 && output !== void 0) return { ...output, retry: stepretry };
21443
+ if (stepretry !== void 0) return { ok: false, summary, retry: stepretry };
20596
21444
  return output ?? { ok: false, summary };
20597
21445
  }
21446
+ async function distillcompletedrun(plan, progress, session, settings) {
21447
+ const log = await memory.getimmutablelog(plan.id);
21448
+ const origins = [.../* @__PURE__ */ new Set([session?.origin ?? "", ...(log?.entries ?? []).map((entry) => entry.origin).filter((entryorigin) => entryorigin !== "")])].filter((entryorigin) => entryorigin !== "");
21449
+ let provenance = "inline";
21450
+ const inline = distillrunsummary({ plan, outcomes: progress.outcomes ?? [], origins, sessionid: session?.id ?? "", ...settings?.summarywindow !== void 0 ? { window: settings.summarywindow } : {}, provenance: "inline", now: Date.now() });
21451
+ const ready = await ensureoffscreendocument(plan.id).catch(() => false);
21452
+ if (ready) {
21453
+ const request = summaryrequestof({ id: randomid(), runid: plan.id, sessionid: session?.id ?? "", payload: JSON.stringify({ runid: plan.id, steps: inline.steps.length, window: settings?.summarywindow }), sentat: Date.now() });
21454
+ try {
21455
+ const answer = await chrome.runtime.sendMessage({ kind: "offscreen", action: "summary", request: { id: request.id, runid: request.runid, stepid: request.stepid, task: runsummarytask, payload: request.payload, transferables: [] } });
21456
+ if (answer && answer.ok !== false) provenance = "offscreenworker";
21457
+ } catch {
21458
+ }
21459
+ }
21460
+ const summary = { ...inline, provenance };
21461
+ await memory.setrunsummary(summary);
21462
+ await memory.trackrunsummary(plan.id);
21463
+ if (settings?.historyindex !== false) await memory.addhistoryentry(summaryhistoryentry(summary));
21464
+ await audit("summary", `The run summary of ${plan.id} distilled ${summary.steps.length} step outcome${summary.steps.length === 1 ? "" : "s"} across ${summary.origins.length} origin${summary.origins.length === 1 ? "" : "s"} as one ${provenance === "offscreenworker" ? "offscreen worker task" : "inline distillation"}${summary.window !== void 0 ? ` inside the user configured window of ${summary.window} steps` : " with no window cap"}.`, { ...session ? { sessionid: session.id } : {}, planid: plan.id });
21465
+ return summary;
21466
+ }
20598
21467
  async function previewstep(stepid) {
20599
21468
  const session = await memory.getsession();
20600
21469
  const plan = await memory.getplan();
@@ -20656,8 +21525,10 @@ async function provenancereportValue() {
20656
21525
  }
20657
21526
  var commandschemas = {
20658
21527
  security: { allowlist: "object", profile: "object", consent: "object", revoke: "object", mask: "object", read: "object", export: "object", settings: "object", gate: "object", vault: "object", connectallow: "object", ratelimit: "object", redact: "object" },
21528
+ sessions: { note: "object", scratch: "object", summary: "object", recall: "object", correction: "object", consent: "object", grid: "object", search: "object", cancel: "object", retry: "object", error: "object", settings: "object", bundle: "object" },
20659
21529
  environments: { action: "string", grants: "array", requestcapability: "boolean", pool: "object", offscreenclose: "boolean", sandbox: "object", settings: "object", render: "object", sandboxhost: "object" },
20660
21530
  transparency: {},
21531
+ surface: { palette: "object", task: "object", onboarding: "object", bus: "object", broadcast: "object", layout: "object", logstream: "object", approve: "object", diff: "object", review: "object", timeline: "object", dashboard: "object", settings: "object" },
20661
21532
  execute: { stepid: "string" },
20662
21533
  configure: { endpoint: "string" }
20663
21534
  };
@@ -20669,6 +21540,494 @@ function schemavalidation(message) {
20669
21540
  if (schema === void 0) return [];
20670
21541
  return schemacheck({ command, schema }).errors;
20671
21542
  }
21543
+ async function sessionviewof() {
21544
+ const session = await memory.getsession();
21545
+ const plan = await memory.getplan();
21546
+ const settings = await memory.getsettings();
21547
+ const progress = await memory.getprogress();
21548
+ const notes = await memory.getsitenotes();
21549
+ const scratch = plan && session ? await memory.readscratchpad(plan.id, session.id) : [];
21550
+ const summaries = await memory.listrunsummaries();
21551
+ const corrections = await memory.getcorrections();
21552
+ const consentmemory = await memory.getconsentmemory();
21553
+ const index = await memory.getrecallindex();
21554
+ const scope = session ? [.../* @__PURE__ */ new Set([session.origin, ...session.grants ?? []])] : [];
21555
+ const recall = rankrecall(index, { text: plan?.objective ?? session?.origin ?? "" }, { origins: scope }).slice(0, 5);
21556
+ const errors = (await memory.geterrorsurfaces()).slice(0, 20);
21557
+ const rows = sessiongridrows({ ...session !== void 0 ? { session } : {}, ...plan !== void 0 ? { plan } : {}, ...progress !== void 0 ? { progress } : {}, logs: await memory.listimmutablelogs(), summaries, locks: await memory.getrunlocks(), tabsessions: await memory.listtabsessions() });
21558
+ return {
21559
+ grid: rows.map((row) => ({ sessionid: row.sessionid, runid: row.runid, origins: row.origins, state: row.state, outcome: row.outcome, steps: row.steps, completed: row.completed, lock: row.lock, ...row.tabid !== void 0 ? { tabid: row.tabid } : {}, ...row.sealhash !== void 0 ? { sealhash: row.sealhash } : {}, updatedat: row.updatedat, actions: row.actions })),
21560
+ notes: notes.map((note) => ({ id: note.id, origin: note.origin, title: note.title, body: notebodyof(note), author: note.author, sensitive: note.sensitive, updatedat: note.updatedat })),
21561
+ scratchpad: scratch.map((entry) => ({ id: entry.id, taskid: entry.taskid, text: entry.text, ...entry.stepid !== void 0 ? { stepid: entry.stepid } : {}, author: entry.author, at: entry.at })),
21562
+ summaries: summaries.map((summary) => ({ runid: summary.runid, origins: summary.origins, kinds: summary.kinds, steps: summary.steps.length, provenance: summary.provenance, distilledat: summary.distilledat })),
21563
+ corrections: corrections.slice(0, 20).map((entry) => ({ id: entry.id, origin: entry.origin, kind: entry.kind, stepid: entry.stepid, source: entry.source, reason: entry.reason, at: entry.at })),
21564
+ consentmemory: consentmemory.slice(0, 20).map((entry) => ({ id: entry.id, origin: entry.origin, decision: entry.decision, boundary: entry.boundary, kinds: entry.kinds, at: entry.at, ...entry.expiresat !== void 0 ? { expiresat: entry.expiresat } : {} })),
21565
+ recall: recall.map((match) => ({ origin: match.entry.origin, runid: match.entry.runid, stepid: match.entry.stepid, score: match.score, reason: match.reason })),
21566
+ errors: errors.map((surface) => ({ stepid: surface.stepid, runid: surface.runid, cause: surface.cause, message: surface.message, retry: surface.retry, at: surface.at })),
21567
+ emptystates: [
21568
+ { surface: "sessiongrid", message: rows.length === 0 ? "No session exists yet; start the first run by describing an objective and reviewing the plan the agent proposes." : "" },
21569
+ { surface: "historysearch", message: "No history matches yet; start with a first query such as an origin, a note title or a kind the runs executed." },
21570
+ { surface: "sitenotes", message: notes.length === 0 ? `No site note exists${session ? ` for ${session.origin}` : ""} yet; write the first note with a title and a body and the note flow keeps it per origin with its author provenance.` : "" },
21571
+ { surface: "scratchpad", message: scratch.length === 0 ? "The scratchpad holds no entry yet; the agent appends its per task notes here while the reviewed steps run, and every entry carries its step provenance." : "" }
21572
+ ].filter((state) => state.message !== ""),
21573
+ ...settings?.cancelrollback !== void 0 ? { cancelrollback: settings.cancelrollback } : {},
21574
+ historyindex: settings?.historyindex !== false
21575
+ };
21576
+ }
21577
+ async function handlesessionscommand(message) {
21578
+ const input = message;
21579
+ const now = Date.now();
21580
+ const session = await memory.getsession();
21581
+ const plan = await memory.getplan();
21582
+ const settings = await memory.getsettings();
21583
+ if (input.note !== void 0) {
21584
+ if (input.note.add !== void 0) {
21585
+ const origin = input.note.add.origin?.trim() || session?.origin || "";
21586
+ if (origin === "") throw new Error("The site note needs its origin.");
21587
+ const writegate = sitenoteswritegate({ consent: input.note.add.consent === true, origin });
21588
+ if (!writegate.allowed) throw new Error(writegate.reason);
21589
+ const note = sitenoteof({ origin, title: input.note.add.title ?? "", body: input.note.add.body ?? "", author: "user", ...input.note.add.sensitive === true ? { sensitive: true } : {}, now });
21590
+ await memory.writesitenote(note);
21591
+ if (settings?.historyindex !== false) await memory.addhistoryentry(notehistoryentry(note));
21592
+ await audit("notes", `The user wrote the site note ${note.id} for ${origin}${note.sensitive ? " with its body sealed at rest" : ""}; the note keeps its author provenance and its timestamps.`, { ...session ? { sessionid: session.id } : {} });
21593
+ return { ...await sessionviewof(), note };
21594
+ }
21595
+ if (input.note.edit !== void 0) {
21596
+ const id = input.note.edit.id?.trim() ?? "";
21597
+ const note = (await memory.getsitenotes()).find((candidate) => candidate.id === id);
21598
+ if (!note) throw new Error(`No site note ${id} exists to edit.`);
21599
+ const writegate = sitenoteswritegate({ consent: true, origin: note.origin });
21600
+ if (!writegate.allowed) throw new Error(writegate.reason);
21601
+ const edited = editnote(note, { title: input.note.edit.title ?? note.title, body: input.note.edit.body ?? notebodyof(note), author: "user", now });
21602
+ await memory.writesitenote(edited);
21603
+ if (settings?.historyindex !== false) await memory.addhistoryentry(notehistoryentry(edited));
21604
+ await audit("notes", `The user edited the site note ${edited.id} of ${edited.origin}; the edit keeps the created timestamp and names its author.`, { ...session ? { sessionid: session.id } : {} });
21605
+ return { ...await sessionviewof(), note: edited };
21606
+ }
21607
+ if (input.note.remove !== void 0) {
21608
+ const id = input.note.remove.id?.trim() ?? "";
21609
+ await memory.removesitenote(id);
21610
+ await audit("notes", `The user removed the site note ${id}.`, { ...session ? { sessionid: session.id } : {} });
21611
+ return { ...await sessionviewof(), removed: id };
21612
+ }
21613
+ if (input.note.list !== void 0) {
21614
+ const origin = input.note.list.origin?.trim() || session?.origin || "";
21615
+ if (origin === "") throw new Error("The site note read needs its origin.");
21616
+ const readgate = sitenotesreadgate({ origin, grants: session ? [.../* @__PURE__ */ new Set([session.origin, ...session.grants ?? []])] : [] });
21617
+ if (!readgate.allowed) throw new Error(readgate.reason);
21618
+ return { notes: (await memory.readsitenotes(origin)).map((note) => ({ id: note.id, origin: note.origin, title: note.title, body: notebodyof(note), author: note.author, sensitive: note.sensitive, updatedat: note.updatedat })) };
21619
+ }
21620
+ }
21621
+ if (input.scratch !== void 0) {
21622
+ if (!session || !plan) throw new Error("The scratchpad serves the running task of an active session.");
21623
+ const taskid = input.scratch.append?.taskid?.trim() || input.scratch.read?.taskid?.trim() || plan.id;
21624
+ const scopegate2 = scratchpadscopegate({ taskid, sessionid: session.id, entrytaskid: taskid, entrysessionid: session.id });
21625
+ if (!scopegate2.allowed) throw new Error(scopegate2.reason);
21626
+ if (input.scratch.append !== void 0) {
21627
+ const entry = scratchentryof({ taskid, sessionid: session.id, text: input.scratch.append.text ?? "", ...input.scratch.append.stepid !== void 0 && input.scratch.append.stepid.trim() !== "" ? { stepid: input.scratch.append.stepid } : {}, author: "user", now });
21628
+ await memory.appendscratchentry(entry);
21629
+ await audit("scratchpad", `The user appended one scratchpad entry to the task ${taskid}${entry.stepid !== void 0 ? ` beside the step ${entry.stepid}` : ""}; the pad stays append only.`, { sessionid: session.id, planid: taskid });
21630
+ return { ...await sessionviewof(), entry };
21631
+ }
21632
+ if (input.scratch.read !== void 0) return { scratchpad: await memory.readscratchpad(taskid, session.id) };
21633
+ }
21634
+ if (input.summary !== void 0) {
21635
+ if (input.summary.read !== void 0) {
21636
+ const runid = input.summary.read.runid?.trim() || plan?.id || "";
21637
+ const summary = await memory.getrunsummary(runid);
21638
+ if (!summary) throw new Error(`No run summary exists for the run ${runid}.`);
21639
+ return { summary };
21640
+ }
21641
+ if (input.summary.list !== void 0) return { summaries: await memory.listrunsummaries(input.summary.list.origin?.trim() || void 0) };
21642
+ }
21643
+ if (input.recall !== void 0 && input.recall.query !== void 0) {
21644
+ const text2 = input.recall.query.text?.trim() ?? "";
21645
+ if (text2 === "") throw new Error("The semantic recall query needs its text.");
21646
+ const scope = session ? [.../* @__PURE__ */ new Set([session.origin, ...session.grants ?? []])] : [];
21647
+ const scopegate2 = semanticrecallscopegate({ origin: input.recall.query.origin?.trim() || void 0, scope });
21648
+ if (!scopegate2.allowed) throw new Error(scopegate2.reason);
21649
+ const matches = await memory.semanticrecall({ text: text2, ...input.recall.query.origin !== void 0 && input.recall.query.origin.trim() !== "" ? { origin: input.recall.query.origin.trim() } : {}, ...input.recall.query.limit !== void 0 ? { limit: input.recall.query.limit } : {} }, { origins: scope }, rankrecall);
21650
+ await audit("recall", `The semantic recall ranked ${matches.length} past extraction${matches.length === 1 ? "" : "s"} by text similarity inside the ${scope.length} origin scope${scope.length === 1 ? "" : "s"} of the run; every match carries its run and step provenance.`, { ...session ? { sessionid: session.id } : {} });
21651
+ return { matches };
21652
+ }
21653
+ if (input.correction !== void 0 && input.correction.list !== void 0) {
21654
+ const readgate = memoryreadscopegate({ phase: plan && plan.state === "pending" ? "planning" : "prompting" });
21655
+ if (!readgate.allowed) throw new Error(readgate.reason);
21656
+ return { corrections: await memory.getcorrections({ ...input.correction.list.origin !== void 0 ? { origin: input.correction.list.origin } : {}, ...input.correction.list.kind !== void 0 ? { kind: input.correction.list.kind } : {} }) };
21657
+ }
21658
+ if (input.consent !== void 0 && input.consent.list !== void 0) {
21659
+ const readgate = memoryreadscopegate({ phase: "prompting" });
21660
+ if (!readgate.allowed) throw new Error(readgate.reason);
21661
+ const origin = input.consent.list.origin?.trim() || session?.origin || "";
21662
+ const entries = await memory.getconsentmemory(origin === "" ? void 0 : origin);
21663
+ const advisory = consentadvisoryverdict(entries, origin, "observe");
21664
+ return { entries, advisory: advisory.reason };
21665
+ }
21666
+ if (input.grid !== void 0) {
21667
+ if (input.grid.rows !== void 0) return { grid: (await sessionviewof()).grid };
21668
+ if (input.grid.open !== void 0) {
21669
+ const runid = input.grid.open.runid?.trim() ?? "";
21670
+ const view = await sessionviewof();
21671
+ const row = view.grid.find((candidate) => candidate.runid === runid);
21672
+ if (!row) throw new Error(`No session grid row exists for the run ${runid}.`);
21673
+ if (row.tabid !== void 0) await chrome.sidePanel.open({ tabId: row.tabid }).catch(() => {
21674
+ });
21675
+ await audit("session", `The user opened the session grid row of the run ${runid} from the interface deep link.`, { ...session ? { sessionid: session.id } : {}, planid: runid });
21676
+ return { opened: runid };
21677
+ }
21678
+ if (input.grid.resume !== void 0) {
21679
+ const runid = input.grid.resume.runid?.trim() ?? "";
21680
+ if (!session) throw new Error("The resume needs its active session.");
21681
+ if (session.pausedat === void 0) throw new Error("The session of the run stays active; nothing to resume.");
21682
+ const { pausedat, ...resumedsession } = session;
21683
+ void pausedat;
21684
+ await memory.setsession(resumedsession);
21685
+ await audit("resume", `The user resumed the paused session of the run ${runid} from the session grid.`, { sessionid: session.id, planid: runid });
21686
+ return { ...await sessionviewof(), resumed: runid };
21687
+ }
21688
+ if (input.grid.reopen !== void 0) {
21689
+ const runid = input.grid.reopen.runid?.trim() ?? "";
21690
+ const log = await memory.getimmutablelog(runid);
21691
+ if (!log) throw new Error(`No sealed run exists for the run ${runid}.`);
21692
+ const read = await readverifiedlog(log);
21693
+ if (!read.ok) throw new Error(read.reason);
21694
+ await audit("session", `The user reopened the sealed run ${runid} from the session grid; the chain verification passed and the log link stays intact.`, { ...session ? { sessionid: session.id } : {}, planid: runid });
21695
+ return { reopened: runid, entries: read.entries.length, ...log.seal !== void 0 ? { sealhash: log.seal.sealhash.current } : {} };
21696
+ }
21697
+ }
21698
+ if (input.search !== void 0 && input.search.query !== void 0) {
21699
+ if (settings?.historyindex === false) throw new Error("The historysearch index building stays off in the user preferences; the search box needs the index on.");
21700
+ const query = historyqueryof(input.search.query);
21701
+ if (!query) throw new Error("The history search needs its text with a coherent time range.");
21702
+ const hits = await memory.historysearch(query, historysearch);
21703
+ await audit("search", `The history search matched ${hits.length} entr${hits.length === 1 ? "y" : "ies"} of the corpus${query.origin !== void 0 ? ` for ${query.origin}` : ""}${query.outcome !== void 0 ? ` with the outcome ${query.outcome}` : ""}; every hit highlights its matched terms.`, { ...session ? { sessionid: session.id } : {} });
21704
+ return { hits };
21705
+ }
21706
+ if (input.cancel !== void 0) {
21707
+ const runid = input.cancel.runid?.trim() || plan?.id || "";
21708
+ if (runid === "") throw new Error("The cancelrun needs its run.");
21709
+ const progress = await memory.getprogress();
21710
+ const preference = input.cancel.rollback === "none" ? "none" : input.cancel.rollback === "queued" ? "queued" : settings?.cancelrollback;
21711
+ const action = cancelrunactionof({ runid, sessionid: session?.id ?? "", plan: plan && plan.id === runid ? plan : void 0, progress, preference });
21712
+ const split = rollbacksplit(plan && plan.id === runid ? plan : void 0, progress);
21713
+ const cancelgate = cancelrungate({ queuedstepids: action.rollback.queuedstepids, executedstepids: split.executedstepids, rollbackscope: action.rollback.scope });
21714
+ if (!cancelgate.allowed) throw new Error(cancelgate.reason);
21715
+ if (plan && plan.id === runid && ["pending", "approved"].includes(plan.state)) await memory.setplan({ ...plan, state: "cancelled" });
21716
+ if (session) await appendrunevent("cancel", `The user cancelled the run ${runid}: ${action.rollback.label}`, session, session.origin).catch(() => {
21717
+ });
21718
+ await audit("cancel", `The user cancelled the run ${runid}: ${action.rollback.label}`, { ...session ? { sessionid: session.id } : {}, planid: runid });
21719
+ return { ...await sessionviewof(), cancelled: action };
21720
+ }
21721
+ if (input.retry !== void 0) {
21722
+ const stepid = input.retry.stepid?.trim() ?? "";
21723
+ if (stepid === "") throw new Error("The retry needs its step.");
21724
+ const retrygate = retrydispatchgate({ reviewed: true, stepid });
21725
+ if (!retrygate.allowed) throw new Error(retrygate.reason);
21726
+ const output = await executestep(stepid);
21727
+ await audit("retry", `The user retried the step ${stepid} through a new reviewed dispatch: ${output.summary}`, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {}, stepid });
21728
+ return output;
21729
+ }
21730
+ if (input.error !== void 0) return { errors: await memory.geterrorsurfaces(input.error.stepid?.trim() || void 0) };
21731
+ if (input.settings !== void 0) {
21732
+ const patch = { ...settings };
21733
+ for (const field of ["noteretention", "scratchpadretention", "summaryretention", "correctionretention", "recallwindow"]) {
21734
+ const value = input.settings[field];
21735
+ if (value === void 0) continue;
21736
+ const gate = sessionretentionvalid(value);
21737
+ if (!gate.allowed) throw new Error(gate.reason);
21738
+ patch[field] = value;
21739
+ }
21740
+ if (input.settings.summarywindow !== void 0) {
21741
+ const windowgate = summarywindowvalid(input.settings.summarywindow);
21742
+ if (!windowgate.allowed) throw new Error(windowgate.reason);
21743
+ patch.summarywindow = input.settings.summarywindow;
21744
+ }
21745
+ if (input.settings.historyindex !== void 0) patch.historyindex = input.settings.historyindex === true;
21746
+ if (input.settings.cancelrollback !== void 0) patch.cancelrollback = input.settings.cancelrollback === "none" ? "none" : "queued";
21747
+ await memory.setsettings(patch);
21748
+ await audit("configure", `The user updated the session interface preferences: notes retention ${patch.noteretention !== void 0 ? `${patch.noteretention} milliseconds` : "every note stays"}, scratchpad retention ${patch.scratchpadretention !== void 0 ? `${patch.scratchpadretention} milliseconds` : "every entry stays"}, summaries retention ${patch.summaryretention !== void 0 ? `${patch.summaryretention} milliseconds` : "every summary stays"}, corrections retention ${patch.correctionretention !== void 0 ? `${patch.correctionretention} milliseconds` : "every correction stays"}, recall window ${patch.recallwindow !== void 0 ? `${patch.recallwindow} milliseconds` : "the whole index"}, summary window ${patch.summarywindow !== void 0 ? `${patch.summarywindow} steps` : "no cap"}, historysearch index ${patch.historyindex !== false ? "on" : "off"} and cancelrun rollback ${patch.cancelrollback ?? "queued"}.`, {});
21749
+ return { ...await sessionviewof(), configured: true };
21750
+ }
21751
+ if (input.bundle !== void 0 && input.bundle.export === true) {
21752
+ const bundle = await memory.exportsessionbundle(now);
21753
+ await audit("export", `The user exported the session audit bundle: ${bundle.notes.length} note${bundle.notes.length === 1 ? "" : "s"}, ${bundle.summaries.length} run summar${bundle.summaries.length === 1 ? "y" : "ies"} and ${bundle.corrections.length} correction${bundle.corrections.length === 1 ? "" : "s"}; sensitive note bodies stay sealed in the export.`, { ...session ? { sessionid: session.id } : {} });
21754
+ return { bundle };
21755
+ }
21756
+ throw new Error("The sessions command carries no note, scratch, summary, recall, correction, consent, grid, search, cancel, retry, error, settings or bundle action.");
21757
+ }
21758
+ async function grantedcapabilities() {
21759
+ const report = await memory.getcapabilities();
21760
+ const granted = ["activeTab", "storage", "scripting", "sidePanel"];
21761
+ if (report?.tabs) granted.push("tabs");
21762
+ if (report?.downloads) granted.push("downloads");
21763
+ if (report?.clipboardread) granted.push("clipboardRead");
21764
+ if (report?.clipboardwrite) granted.push("clipboardWrite");
21765
+ if (await offscreengranted()) granted.push("offscreen");
21766
+ return granted;
21767
+ }
21768
+ async function surfacesnapshotof(surface) {
21769
+ const settings = await memory.getsettings();
21770
+ const session = await memory.getsession();
21771
+ const sessionactive = Boolean(session && !session.stoppedat && session.expiresat > Date.now());
21772
+ const granted = await grantedcapabilities();
21773
+ const plan = await memory.getplan();
21774
+ const progress = await memory.getprogress();
21775
+ const onboarding = await memory.getonboardingstate();
21776
+ const palette = palettequery(palettecommandsof(surfacepalette(), { granted, sessionactive }), { text: "", usage: await memory.getpaletteusage(), ...settings?.paletterecents !== void 0 ? { recentwindow: settings.paletterecents } : {} });
21777
+ const verification = await verifylogstream(logstreamhistory);
21778
+ return surfacesnapshot({
21779
+ surface,
21780
+ palette,
21781
+ timeline: plan ? stepstimelinenodes({ plan, ...progress !== void 0 && progress.planid === plan.id ? { progress } : {}, now: Date.now() }) : [],
21782
+ logstream: { events: livebufferof(logstreamhistory, settings?.logstreambuffer).map((event) => ({ id: event.id, level: event.level, source: event.source, origin: event.origin, summary: event.summary, ...event.stepid !== void 0 ? { stepid: event.stepid } : {}, masked: event.masked, maskverdict: event.maskverdict, at: event.at })), chainvalid: verification.valid, reason: verification.reason },
21783
+ plancards: plan !== void 0 ? plancardgroups(plancardsof({ plan, corrections: await memory.getcorrections() })) : [],
21784
+ ...onboarding !== void 0 ? { onboarding: { stepscompleted: onboarding.stepscompleted, done: onboarding.done } } : {}
21785
+ });
21786
+ }
21787
+ async function handlesurfacecommand(message) {
21788
+ const input = message;
21789
+ const now = Date.now();
21790
+ const session = await memory.getsession();
21791
+ const sessionactive = Boolean(session && !session.stoppedat && session.expiresat > now);
21792
+ const settings = await memory.getsettings();
21793
+ const plan = await memory.getplan();
21794
+ const surfaceof = (value, fallback) => ["popup", "sidepanel", "dashboardpage", "optionspage", "onboarding"].includes(value ?? "") ? value : fallback;
21795
+ if (input.palette !== void 0) {
21796
+ const granted = await grantedcapabilities();
21797
+ const entries = surfacepalette();
21798
+ if (input.palette.used !== void 0) {
21799
+ const command = input.palette.used.command?.trim() ?? "";
21800
+ if (command === "") throw new Error("The palette use record needs its command.");
21801
+ const usage = paletteuseafter(await memory.getpaletteusage(), command, now);
21802
+ const record2 = usage[0];
21803
+ if (record2 === void 0) throw new Error("The palette use record never landed.");
21804
+ await memory.setpaletteusage(usage);
21805
+ await audit("palette", `The user ran the ${command} command from the commandpalette; its ${record2.count} recorded use${record2.count === 1 ? "" : "s"} rank it first among equal matches.`, { ...session ? { sessionid: session.id } : {} });
21806
+ return { usage: record2 };
21807
+ }
21808
+ if (input.palette.query !== void 0) {
21809
+ const matches = palettequery(palettecommandsof(entries, { granted, sessionactive }), { text: input.palette.query.text ?? "", usage: await memory.getpaletteusage(), ...settings?.paletterecents !== void 0 ? { recentwindow: settings.paletterecents } : {} });
21810
+ return { matches };
21811
+ }
21812
+ return { entries: palettecommandsof(entries, { granted, sessionactive }) };
21813
+ }
21814
+ if (input.task !== void 0) {
21815
+ if (input.task.submit !== void 0) {
21816
+ if (!session || session.stoppedat || session.expiresat <= now) throw new Error("Start a current browser session before submitting a task goal.");
21817
+ const { tab, origin } = await activecontext();
21818
+ if (session.tabid !== tab.id || session.origin !== origin) throw new Error("The selected tab or origin no longer matches the approved session.");
21819
+ const version = await memory.getobservationversion();
21820
+ const storedobservation = version !== void 0 ? await memory.getobservation(version) : void 0;
21821
+ const context = storedobservation ? `${storedobservation.observation.title}: ${storedobservation.observation.textpreview}` : tab.title ?? "";
21822
+ const submission = taskinputof({ text: input.task.submit.text ?? "", context, origin, surface: surfaceof(input.task.submit.surface, "popup"), at: now });
21823
+ await memory.addtaskinput(submission);
21824
+ const proposed = await propose(submission.text, false);
21825
+ await audit("surface", `The ${submission.surface} taskinput submitted a natural language goal for ${origin} with the active origin and the page outline attached; the goal routed through the same proposal flow as the api and the plan ${proposed.id} now awaits its plancard review.`, { sessionid: session.id, planid: proposed.id });
21826
+ await broadcastsurfaceframe({ channel: "runstate", surface: "background", summary: `A taskinput goal became the plan ${proposed.id} and awaits review.` });
21827
+ return { submission, plan: proposed, status: "ready" };
21828
+ }
21829
+ if (input.task.history === true) return { history: await memory.gettaskinputs() };
21830
+ if (input.task.status === true) return { status: plan === void 0 ? "idle" : plan.state === "pending" ? "ready" : plan.state === "approved" ? "ready" : "failed" };
21831
+ }
21832
+ if (input.onboarding !== void 0) {
21833
+ const state = await memory.getonboardingstate();
21834
+ if (input.onboarding.complete !== void 0) {
21835
+ const stepid = input.onboarding.complete.stepid?.trim() ?? "";
21836
+ const current = state ?? onboardingstart(void 0, now);
21837
+ const completion = onboardingcomplete(current, stepid, now);
21838
+ if (completion.consentevent !== void 0) {
21839
+ const consentgate = onboardingconsentgate({ consentevents: current.consentevent !== void 0 ? [current.consentevent] : [] });
21840
+ if (!consentgate.allowed) throw new Error(consentgate.reason);
21841
+ const origin = session?.origin ?? "onboarding";
21842
+ await memory.addconsentmemoryentry(consentmemoryof({ origin, decision: "grant", boundary: "onboarding", kinds: [], now }));
21843
+ }
21844
+ await memory.setonboardingstate(completion.state);
21845
+ await audit("onboarding", completion.consentevent !== void 0 ? `The user finished the onboarding walkthrough and its completion wrote the single consent scoped event ${completion.consentevent}; the walkthrough never writes a second one.` : `The user completed the ${stepid} step of the onboarding walkthrough; ${completion.state.stepscompleted.length} of ${onboardingsteps().length} steps stand done.`, { ...session ? { sessionid: session.id } : {} });
21846
+ await broadcastsurfaceframe({ channel: "settings", surface: "background", summary: completion.consentevent !== void 0 ? "The onboarding walkthrough completed." : `The onboarding step ${stepid} completed.` });
21847
+ return { steps: onboardingsteps(), state: completion.state, ...completion.consentevent !== void 0 ? { consentevent: completion.consentevent } : {} };
21848
+ }
21849
+ if (input.onboarding.replay === true) {
21850
+ const replayed = onboardingstart(state, now);
21851
+ await memory.setonboardingstate(replayed);
21852
+ await audit("onboarding", "The user replayed the onboarding walkthrough on demand from the optionspage; the replay restarts the steps and writes no second consent event.", {});
21853
+ return { steps: onboardingsteps(), state: replayed };
21854
+ }
21855
+ return { steps: onboardingsteps(), ...state ? { state } : {} };
21856
+ }
21857
+ if (input.bus !== void 0 && input.bus.action !== void 0) {
21858
+ const surface = surfaceof(input.bus.action.surface, "popup");
21859
+ const command = input.bus.action.command?.trim() ?? "";
21860
+ const action = { surface, command, ...input.bus.action.stepid !== void 0 && input.bus.action.stepid.trim() !== "" ? { stepid: input.bus.action.stepid } : {}, ...input.bus.action.payload !== void 0 && input.bus.action.payload.trim() !== "" ? { payload: input.bus.action.payload } : {} };
21861
+ const granted = await grantedcapabilities();
21862
+ const route = busrouteaction(action, { sessionactive, granted, planreviewed: Boolean(plan && plan.state === "approved"), planstate: plan?.state === "pending" ? "pending" : "approved", text: input.bus.action.payload ?? "", origin: session?.origin ?? "" });
21863
+ if (!route.dispatched) throw new Error(route.reason);
21864
+ await audit("surface", `The ${surface} routed the ${command} action through the command bus: ${route.reason}`, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {} });
21865
+ if (command === "starttask") {
21866
+ if (!sessionactive) await handlerequest({ kind: "startsession" }, {});
21867
+ const goal = (input.bus.action.payload ?? "").trim();
21868
+ if (goal !== "") await propose(goal, false);
21869
+ } else if (command === "pauserun") await handlerequest({ kind: "pausesession" }, {});
21870
+ else if (command === "resumerun") await handlerequest({ kind: "resumesession" }, {});
21871
+ else if (command === "cancelrun") {
21872
+ if (plan) await handlerequest({ kind: "sessions", cancel: { runid: plan.id } }, {}).catch(() => {
21873
+ });
21874
+ } else if (command === "revokeconsent") {
21875
+ if (session) await handlerequest({ kind: "security", allowlist: { remove: { origin: session.origin } } }, {}).catch(() => {
21876
+ });
21877
+ }
21878
+ await broadcastsurfaceframe({ channel: "runstate", surface, summary: `The ${command} action of the ${surface} dispatched through the command bus.` });
21879
+ return { route, palette: palettecommandsof(surfacepalette(), { granted, sessionactive }) };
21880
+ }
21881
+ if (input.broadcast !== void 0 && input.broadcast.frames === true) {
21882
+ const bound = settings?.logstreambuffer;
21883
+ const boundgate = logbufferboundvalid(bound);
21884
+ if (!boundgate.allowed) throw new Error(boundgate.reason);
21885
+ return { frames: bound === void 0 ? recentframes : recentframes.slice(-bound), logstream: livebufferof(logstreamhistory, bound), total: logstreamhistory.length };
21886
+ }
21887
+ if (input.layout !== void 0) {
21888
+ if (input.layout.set !== void 0) {
21889
+ const surface = surfaceof(input.layout.set.surface, "popup");
21890
+ const preferences = input.layout.set.preferences ?? {};
21891
+ await memory.setsurfacelayout({ surface, preferences, updatedat: now });
21892
+ await audit("surface", `The user saved the layout preferences of the ${surface} with ${Object.keys(preferences).length} key${Object.keys(preferences).length === 1 ? "" : "s"}; the preferences scope per profile workspace and take effect without a reload.`, { ...session ? { sessionid: session.id } : {} });
21893
+ return { layout: { surface, preferences, updatedat: now } };
21894
+ }
21895
+ if (input.layout.get !== void 0) return { layout: await memory.getsurfacelayout(surfaceof(input.layout.get.surface, "popup")) };
21896
+ }
21897
+ if (input.logstream !== void 0) {
21898
+ if (input.logstream.read !== void 0) {
21899
+ const stored = await memory.getlogstreamfilters();
21900
+ const requested = input.logstream.read.filters;
21901
+ const level = requested?.level !== void 0 && requested.level !== "" ? requested.level : stored?.level;
21902
+ const origin = requested?.origin !== void 0 && requested.origin !== "" ? requested.origin : stored?.origin;
21903
+ const stepid = requested?.stepid !== void 0 && requested.stepid !== "" ? requested.stepid : stored?.stepid;
21904
+ const filter = {};
21905
+ if (level !== void 0 && level !== "") filter.level = level;
21906
+ if (origin !== void 0 && origin !== "") filter.origin = origin;
21907
+ if (stepid !== void 0 && stepid !== "") filter.stepid = stepid;
21908
+ const verification = await verifylogstream(logstreamhistory);
21909
+ return { events: filterlogstream(livebufferof(logstreamhistory, settings?.logstreambuffer), filter), chain: verification, total: logstreamhistory.length };
21910
+ }
21911
+ if (input.logstream.excerpt !== void 0) {
21912
+ const from = input.logstream.excerpt.from ?? 0;
21913
+ const to = input.logstream.excerpt.to ?? logstreamhistory.length;
21914
+ const excerpt = await auditexcerptof(logstreamhistory, { from, to });
21915
+ const gate = logstreamegressgate({ verified: excerpt.ok, entries: Math.max(0, Math.min(to, logstreamhistory.length) - Math.max(0, from)) });
21916
+ if (!gate.allowed) throw new Error(gate.reason);
21917
+ await audit("logstream", `The user copied the verified range ${from} to ${to} of the live logstream as an audit excerpt; the copy left the stream only after the chain verified.`, { ...session ? { sessionid: session.id } : {} });
21918
+ return { excerpt: excerpt.text, reason: excerpt.reason };
21919
+ }
21920
+ if (input.logstream.filters?.set !== void 0) {
21921
+ const requested = input.logstream.filters.set;
21922
+ const filter = {};
21923
+ if (requested?.level !== void 0 && requested.level !== "") filter.level = requested.level;
21924
+ if (requested?.origin !== void 0 && requested.origin !== "") filter.origin = requested.origin;
21925
+ if (requested?.stepid !== void 0 && requested.stepid !== "") filter.stepid = requested.stepid;
21926
+ await memory.setlogstreamfilters(filter);
21927
+ await audit("surface", `The user saved the logstream filter preferences${Object.keys(filter).length > 0 ? ` for ${Object.entries(filter).map(([key, value]) => `${key} ${value}`).join(" and ")}` : ""}; the live view reopens with them.`, {});
21928
+ return { filters: filter };
21929
+ }
21930
+ }
21931
+ if (input.approve !== void 0 && input.approve.resolve !== void 0) {
21932
+ const stepid = input.approve.resolve.stepid?.trim() ?? "";
21933
+ const resolution = input.approve.resolve.resolution === "approve" || input.approve.resolve.resolution === "edit" ? input.approve.resolve.resolution : input.approve.resolve.resolution === "reject" ? "reject" : void 0;
21934
+ if (resolution === void 0) throw new Error("The stepapprove resolution needs its approve, reject or edit decision.");
21935
+ const surface = surfaceof(input.approve.resolve.surface, "sidepanel");
21936
+ if (!plan || !["pending", "approved"].includes(plan.state)) throw new Error("The stepapprove resolution serves the pending or approved plan under review.");
21937
+ const step = plan.steps.find((candidate) => candidate.id === stepid);
21938
+ if (!step) throw new Error(`No step ${stepid} of the plan ${plan.id} exists to resolve.`);
21939
+ const gate = stepapprovegate({ stepids: [stepid], resolution, surface });
21940
+ if (!gate.allowed) throw new Error(gate.reason);
21941
+ const record2 = stepresolutionof({ stepid, planid: plan.id, origin: plan.origin, resolution, surface, ...resolution === "edit" && input.approve.resolve.edited !== void 0 ? { edited: input.approve.resolve.edited } : {}, at: now });
21942
+ await memory.addstepapproveresolution(record2);
21943
+ const event = resolutionlogeventof(record2);
21944
+ if (resolution === "edit") {
21945
+ const edited = input.approve.resolve.edited ?? "";
21946
+ try {
21947
+ const shape = JSON.parse(edited);
21948
+ const editedkind = shape.kind;
21949
+ const editedsummary = shape.summary;
21950
+ if (typeof editedkind !== "string" || editedkind.trim() === "" || typeof editedsummary !== "string" || editedsummary.trim() === "") throw new Error("The edited step shape needs its kind and summary.");
21951
+ await memory.setplan({ ...plan, steps: plan.steps.map((candidate) => candidate.id === stepid ? { ...candidate, kind: editedkind, summary: editedsummary, ...shape.target !== void 0 ? { target: shape.target } : {}, ...shape.value !== void 0 ? { value: shape.value } : {}, ...shape.options !== void 0 ? { options: shape.options } : {} } : candidate) });
21952
+ await memory.addcorrection(editedcorrectionof({ origin: plan.origin, kind: step.kind, stepid, original: JSON.stringify({ kind: step.kind, summary: step.summary, ...step.target !== void 0 ? { target: step.target } : {}, ...step.value !== void 0 ? { value: step.value } : {}, ...step.options !== void 0 ? { options: step.options } : {} }), corrected: edited, reason: `The user edited the step ${stepid} from the ${surface} before approving.`, now }));
21953
+ } catch (error) {
21954
+ throw new Error(error instanceof Error ? error.message : "The edited step shape failed to parse.");
21955
+ }
21956
+ }
21957
+ if (resolution === "reject") await memory.addcorrection(rejectedcorrectionof({ origin: plan.origin, kind: step.kind, stepid, original: JSON.stringify({ kind: step.kind, summary: step.summary }), reason: `The user rejected the step ${stepid} from the ${surface}.`, now }));
21958
+ await appendrunevent("review", event.summary, session, plan.origin, stepid);
21959
+ await audit("approval", event.summary, { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid });
21960
+ await broadcastsurfaceframe({ channel: "runstate", surface, summary: `The step ${stepid} resolved with a ${resolution} from the ${surface}.` });
21961
+ return { resolution: record2, cards: plancardsof({ plan: await memory.getplan() ?? plan, corrections: await memory.getcorrections() }) };
21962
+ }
21963
+ if (input.diff !== void 0 && input.diff.preview !== void 0) {
21964
+ const stepid = input.diff.preview.stepid?.trim() ?? "";
21965
+ if (!plan) throw new Error("The diffpreview serves the plan under review.");
21966
+ const step = plan.steps.find((candidate) => candidate.id === stepid);
21967
+ if (!step) throw new Error(`No step ${stepid} of the plan ${plan.id} exists to preview.`);
21968
+ const gate = diffpreviewgate({ risk: step.risk });
21969
+ if (!gate.allowed) throw new Error(gate.reason);
21970
+ if (!session) throw new Error("The diffpreview needs its session to observe the before state.");
21971
+ const before = input.diff.preview.before ?? {};
21972
+ const options = stepoptions6(step);
21973
+ const predicted = { ...Object.fromEntries(Object.entries(options).filter(([, value]) => typeof value === "string" || typeof value === "number" || typeof value === "boolean").map(([key, value]) => [key, String(value)])), ...step.value !== void 0 ? { value: step.value } : {}, ...step.target !== void 0 ? { target: step.target } : {} };
21974
+ const after = Object.keys(input.diff.preview.after ?? {}).length > 0 ? input.diff.preview.after : predicted;
21975
+ const payload = JSON.stringify({ before, after });
21976
+ let provenance = "inline";
21977
+ if (settings?.diffpreviewbytes !== void 0 && payload.length >= settings.diffpreviewbytes && await offscreengranted()) {
21978
+ try {
21979
+ const answer = await chrome.runtime.sendMessage({ kind: "offscreen", action: "parse", request: { id: randomid(), runid: plan.id, stepid, task: "diffpreview", payload, transferables: [] } });
21980
+ if (answer?.ok === true) provenance = "offscreenworker";
21981
+ } catch {
21982
+ }
21983
+ }
21984
+ const sensitivefields = [.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])].filter((field) => defaultmaskshapes.some((shape) => field.toLowerCase().includes(shape)));
21985
+ const preview = diffpreviewof({ stepid, before, after, maskverdicts: maskverdictsof({ ...before, ...after }, sensitivefields), provenance });
21986
+ await audit("diff", `The user opened the diffpreview of the write class step ${stepid} of the plan ${plan.id}: ${preview.changes.filter((change) => change.kind === "added").length} added, ${preview.changes.filter((change) => change.kind === "changed").length} changed and ${preview.changes.filter((change) => change.kind === "removed").length} removed field${preview.changes.length === 1 ? "" : "s"}${preview.maskverdicts && Object.keys(preview.maskverdicts).length > 0 ? ` with ${Object.keys(preview.maskverdicts).length} masked value${Object.keys(preview.maskverdicts).length === 1 ? "" : "s"} carrying their mask verdicts` : ""}${provenance === "offscreenworker" ? "; the generation offloaded to the offscreen worker pool" : ""}.`, { ...session ? { sessionid: session.id } : {}, planid: plan.id, stepid });
21987
+ return { preview };
21988
+ }
21989
+ if (input.review !== void 0) {
21990
+ const corrections = await memory.getcorrections();
21991
+ const cards = plan ? plancardsof({ plan, corrections }) : [];
21992
+ if (input.review.groups !== void 0 || input.review.cards !== void 0) return { cards, groups: plancardgroups(cards) };
21993
+ return { cards };
21994
+ }
21995
+ if (input.timeline !== void 0 && input.timeline.nodes === true) {
21996
+ if (!plan) return { nodes: [] };
21997
+ const progress = await memory.getprogress();
21998
+ return { nodes: stepstimelinenodes({ plan, ...progress !== void 0 && progress.planid === plan.id ? { progress } : {}, now }) };
21999
+ }
22000
+ if (input.dashboard !== void 0 && input.dashboard.view === true) {
22001
+ const view = await memory.gettransparencyview();
22002
+ const report = transparencyreport({ grants: transparencygrants({ allowlist: view.allowlist, profiles: view.profiles }), windows: windowhistory(view.windows), connectallow: connectallowlist(view.connectallow), permdiffs: view.permdiffs, safedefaults: view.safedefaults, vault: vaultview(view.vault) });
22003
+ const onboarding = await memory.getonboardingstate();
22004
+ return { sessionview: await sessionviewof(), transparency: report, onboarding: onboarding ?? { stepscompleted: [], done: false }, environments: await environmentviewof(), security: await securityviewof() };
22005
+ }
22006
+ if (input.snapshot !== void 0) return await surfacesnapshotof(surfaceof(input.snapshot.surface, "popup"));
22007
+ if (input.settings !== void 0) {
22008
+ const current = settings ?? {};
22009
+ if (input.settings.logstreambuffer !== void 0) {
22010
+ const boundgate = logbufferboundvalid(input.settings.logstreambuffer);
22011
+ if (!boundgate.allowed) throw new Error(boundgate.reason);
22012
+ }
22013
+ if (input.settings.paletterecents !== void 0 && (!Number.isInteger(input.settings.paletterecents) || input.settings.paletterecents < 0)) throw new Error("The palette recent window stays a whole number of commands the user chose.");
22014
+ if (input.settings.taskinputretention !== void 0 && input.settings.taskinputretention <= 0) throw new Error("The taskinput history retention stays a positive user value in milliseconds.");
22015
+ if (input.settings.diffpreviewbytes !== void 0 && input.settings.diffpreviewbytes <= 0) throw new Error("The diffpreview byte ceiling stays a positive user value.");
22016
+ const next = {
22017
+ ...current,
22018
+ ...input.settings.paletterecents !== void 0 ? { paletterecents: input.settings.paletterecents } : {},
22019
+ ...input.settings.logstreambuffer !== void 0 ? { logstreambuffer: input.settings.logstreambuffer } : {},
22020
+ ...input.settings.taskinputretention !== void 0 ? { taskinputretention: input.settings.taskinputretention } : {},
22021
+ ...input.settings.paletteshortcut !== void 0 ? { paletteshortcut: input.settings.paletteshortcut } : {},
22022
+ ...input.settings.diffpreviewbytes !== void 0 ? { diffpreviewbytes: input.settings.diffpreviewbytes } : {}
22023
+ };
22024
+ await memory.setsettings(next);
22025
+ await audit("configure", `The user set the interface surface options${input.settings.paletterecents !== void 0 ? ` with the palette recent window of ${input.settings.paletterecents}` : ""}${input.settings.logstreambuffer !== void 0 ? ` and the logstream live buffer bound of ${input.settings.logstreambuffer}` : ""}${input.settings.paletteshortcut !== void 0 ? ` and the palette shortcut ${input.settings.paletteshortcut}` : ""}${input.settings.diffpreviewbytes !== void 0 ? ` and the diffpreview byte ceiling of ${input.settings.diffpreviewbytes}` : ""}; every write takes effect without reloading the extension.`, { ...session ? { sessionid: session.id } : {} });
22026
+ await broadcastsurfaceframe({ channel: "settings", surface: "optionspage", summary: "The interface surface options changed and take effect without a reload." });
22027
+ return { settings: next };
22028
+ }
22029
+ throw new Error("The surface command carries no palette, task, onboarding, bus, broadcast, layout, logstream, approve, diff, review, timeline, dashboard, snapshot or settings action.");
22030
+ }
20672
22031
  async function handlerequest(message, sender) {
20673
22032
  const originverdict = origincheckof({ ...sender.id !== void 0 ? { senderid: sender.id } : {}, ...sender.origin !== void 0 ? { senderorigin: sender.origin } : {}, extensionid: chrome.runtime.id, connectallow: await memory.getconnectallow() });
20674
22033
  const inboundgate = origincheckgate({ verdict: originverdict });
@@ -20804,7 +22163,7 @@ async function handlerequest(message, sender) {
20804
22163
  const livetab = session ? await chrome.tabs.get(session.tabid).catch(() => void 0) : void 0;
20805
22164
  const waitprofile = session ? waitprofiles.find((record2) => record2.origin === session.origin) : void 0;
20806
22165
  const livestate = { phase: livetab?.status === "loading" ? "loading" : "complete", ...navrecords[0] ? { finalurl: navrecords[0].finalurl, redirects: navrecords[0].chain } : {} };
20807
- return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), cdpsessions: await memory.getcdpsessions(), cdpcommands: await memory.getcdpcommands(), cdpeventrules: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watchexpressions: await memory.getwatchexpressions(), scriptoverrides: await memory.getscriptoverrides(), debuggergrants: await memory.getdebuggergrants(), pauseretention: runsettings?.pauseretention, breakpointceiling: runsettings?.breakpointceiling, cdpattached: [...activecdpsessions.values()].filter((active) => active.session.detachedat === void 0).length, profileretention: runsettings?.profileretention, traceceiling: runsettings?.traceceiling, profile: profilereport({ flows: await memory.getflowmetrics(), heaps: await memory.getheaprecords(), samples: await memory.getgrowsamples(), trends: await memory.gettrends(), profiles: await memory.getcpuprofiles(), shifts: await memory.getshiftentries(), traces: await memory.gettracerecords(), sourcemaps: await memory.getsourcemaps(), consents: await memory.getsourcemapconsents() }), profileactive: activememorytrackers.size + activeprofiletargets.size, profiletargets: [...activeprofiletargets.values()].flatMap((entry) => entry.targets), socketsactive: activesockets.size, emulation: emulationreport({ ...plan && await loademulationstate(plan.id) !== void 0 ? { state: await loademulationstate(plan.id) } : {}, devices: await memory.getdevicepresets(), networks: await memory.getnetworkpresets(), locations: await memory.getlocationpresets(), agents: await memory.getagentpresets(), blackbox: await memory.getblackboxrules(), permissions: await memory.getpermissionoverrides(), consents: await memory.getlocationconsents() }), emulatedlayers: plan ? layernames(await loademulationstate(plan.id)) : [], emulationretention: runsettings?.emulationretention, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), sessionmemory: sessionreport({ records: sessionrecords, events: await memory.getsessionevents(), folders: await memory.getsessionfolders(), diffs: await memory.getsessiondiffs(), ...autosnapshot !== void 0 ? { auto: autosnapshot.interval } : {}, ...crashed ? { crashed: true } : {} }), autosnapshotstate: autosnapshot, sessionretention: runsettings?.sessionretention, trigger: triggerlist({ rules: await memory.gettriggerules(), workflows: await memory.listworkflows(), queue: await memory.gettriggerqueue() }), triggerretention: runsettings?.triggerretention, workflow: workflowreport({ workflows: await memory.listworkflows(), runs: await memory.listworkflowruns(), templates: await memory.getsteptemplates(), ...newestworkflowrun !== void 0 ? { log: await memory.getrunlog(newestworkflowrun.id), scopes: await memory.getrunscopes(newestworkflowrun.id), provenance: await memory.getworkflowprovenance(newestworkflowrun.id), control: await memory.listcontroldecisions(newestworkflowrun.id) } : {} }), runlogretention: runsettings?.runlogretention, runhistoryretention: runsettings?.runhistoryretention, editor: editorstate({ versions: await memory.listworkflowversions(), diffs: await memory.listversiondiffs(), history: await memory.gethistory(), overrides: await memory.listsiteoverrides(), imports: (await memory.listworkflowimports()).map((entry) => ({ id: entry.id, workflowid: entry.record.id, name: entry.record.name, version: entry.record.version, steps: entry.record.steps.length, risk: entry.record.risk, importedat: entry.importedat, ...entry.filename !== void 0 ? { filename: entry.filename } : {} })), backgroundruns: await memory.getbackgroundruns(), watchdog: { ...runsettings?.watchdog !== void 0 ? { config: runsettings.watchdog } : {}, events: await memory.listwatchdogevents() } }), ...taskstate !== void 0 ? { taskstate } : {}, ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {}, mcp: await mcpstateof(), llm: await llmstateof(), swarm: await swarmstateof(), environments: await environmentviewof(), security: await securityviewof() };
22166
+ return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), cdpsessions: await memory.getcdpsessions(), cdpcommands: await memory.getcdpcommands(), cdpeventrules: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watchexpressions: await memory.getwatchexpressions(), scriptoverrides: await memory.getscriptoverrides(), debuggergrants: await memory.getdebuggergrants(), pauseretention: runsettings?.pauseretention, breakpointceiling: runsettings?.breakpointceiling, cdpattached: [...activecdpsessions.values()].filter((active) => active.session.detachedat === void 0).length, profileretention: runsettings?.profileretention, traceceiling: runsettings?.traceceiling, profile: profilereport({ flows: await memory.getflowmetrics(), heaps: await memory.getheaprecords(), samples: await memory.getgrowsamples(), trends: await memory.gettrends(), profiles: await memory.getcpuprofiles(), shifts: await memory.getshiftentries(), traces: await memory.gettracerecords(), sourcemaps: await memory.getsourcemaps(), consents: await memory.getsourcemapconsents() }), profileactive: activememorytrackers.size + activeprofiletargets.size, profiletargets: [...activeprofiletargets.values()].flatMap((entry) => entry.targets), socketsactive: activesockets.size, emulation: emulationreport({ ...plan && await loademulationstate(plan.id) !== void 0 ? { state: await loademulationstate(plan.id) } : {}, devices: await memory.getdevicepresets(), networks: await memory.getnetworkpresets(), locations: await memory.getlocationpresets(), agents: await memory.getagentpresets(), blackbox: await memory.getblackboxrules(), permissions: await memory.getpermissionoverrides(), consents: await memory.getlocationconsents() }), emulatedlayers: plan ? layernames(await loademulationstate(plan.id)) : [], emulationretention: runsettings?.emulationretention, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), sessionmemory: sessionreport({ records: sessionrecords, events: await memory.getsessionevents(), folders: await memory.getsessionfolders(), diffs: await memory.getsessiondiffs(), ...autosnapshot !== void 0 ? { auto: autosnapshot.interval } : {}, ...crashed ? { crashed: true } : {} }), autosnapshotstate: autosnapshot, sessionretention: runsettings?.sessionretention, trigger: triggerlist({ rules: await memory.gettriggerules(), workflows: await memory.listworkflows(), queue: await memory.gettriggerqueue() }), triggerretention: runsettings?.triggerretention, workflow: workflowreport({ workflows: await memory.listworkflows(), runs: await memory.listworkflowruns(), templates: await memory.getsteptemplates(), ...newestworkflowrun !== void 0 ? { log: await memory.getrunlog(newestworkflowrun.id), scopes: await memory.getrunscopes(newestworkflowrun.id), provenance: await memory.getworkflowprovenance(newestworkflowrun.id), control: await memory.listcontroldecisions(newestworkflowrun.id) } : {} }), runlogretention: runsettings?.runlogretention, runhistoryretention: runsettings?.runhistoryretention, editor: editorstate({ versions: await memory.listworkflowversions(), diffs: await memory.listversiondiffs(), history: await memory.gethistory(), overrides: await memory.listsiteoverrides(), imports: (await memory.listworkflowimports()).map((entry) => ({ id: entry.id, workflowid: entry.record.id, name: entry.record.name, version: entry.record.version, steps: entry.record.steps.length, risk: entry.record.risk, importedat: entry.importedat, ...entry.filename !== void 0 ? { filename: entry.filename } : {} })), backgroundruns: await memory.getbackgroundruns(), watchdog: { ...runsettings?.watchdog !== void 0 ? { config: runsettings.watchdog } : {}, events: await memory.listwatchdogevents() } }), ...taskstate !== void 0 ? { taskstate } : {}, ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {}, mcp: await mcpstateof(), llm: await llmstateof(), swarm: await swarmstateof(), environments: await environmentviewof(), security: await securityviewof(), sessionview: await sessionviewof(), surfacepreferences: { ...runsettings?.paletterecents !== void 0 ? { paletterecents: runsettings.paletterecents } : {}, ...runsettings?.paletteshortcut !== void 0 ? { paletteshortcut: runsettings.paletteshortcut } : {}, ...runsettings?.logstreambuffer !== void 0 ? { logstreambuffer: runsettings.logstreambuffer } : {}, ...runsettings?.taskinputretention !== void 0 ? { taskinputretention: runsettings.taskinputretention } : {}, ...runsettings?.diffpreviewbytes !== void 0 ? { diffpreviewbytes: runsettings.diffpreviewbytes } : {} }, sessionpreferences: { ...runsettings?.recallwindow !== void 0 ? { recallwindow: runsettings.recallwindow } : {}, ...runsettings?.noteretention !== void 0 ? { noteretention: runsettings.noteretention } : {}, ...runsettings?.scratchpadretention !== void 0 ? { scratchpadretention: runsettings.scratchpadretention } : {}, ...runsettings?.summaryretention !== void 0 ? { summaryretention: runsettings.summaryretention } : {}, ...runsettings?.correctionretention !== void 0 ? { correctionretention: runsettings.correctionretention } : {}, ...runsettings?.summarywindow !== void 0 ? { summarywindow: runsettings.summarywindow } : {}, ...runsettings?.historyindex !== void 0 ? { historyindex: runsettings.historyindex } : {}, ...runsettings?.cancelrollback !== void 0 ? { cancelrollback: runsettings.cancelrollback } : {} } };
20808
22167
  }
20809
22168
  case "capabilities":
20810
22169
  return refreshcapabilities();
@@ -20833,7 +22192,12 @@ async function handlerequest(message, sender) {
20833
22192
  const rejected = { ...plan, state: "rejected" };
20834
22193
  await memory.setplan(rejected);
20835
22194
  const current = await memory.getsession();
22195
+ for (const step of plan.steps) {
22196
+ await memory.addcorrection(rejectedcorrectionof({ origin: current?.origin ?? "", kind: step.kind, stepid: step.id, original: JSON.stringify({ kind: step.kind, target: step.target, value: step.value, summary: step.summary }), reason: "The user rejected the reviewed plan during plan review.", now: Date.now() })).catch(() => {
22197
+ });
22198
+ }
20836
22199
  await audit("approval", "The user rejected the plan.", { ...current ? { sessionid: current.id } : {}, planid: rejected.id });
22200
+ await audit("correction", `The rejection captured ${plan.steps.length} correction entr${plan.steps.length === 1 ? "y" : "ies"} in the correction memory, one per rejected step with its rejection reason.`, { ...current ? { sessionid: current.id } : {}, planid: rejected.id });
20837
22201
  return rejected;
20838
22202
  }
20839
22203
  case "preview":
@@ -23190,6 +24554,14 @@ async function handlerequest(message, sender) {
23190
24554
  await memory.setplan(plan);
23191
24555
  await memory.setprogress(resetforplan(await memory.getprogress(), plan, Date.now()));
23192
24556
  await memory.setreplans(replans.map((candidate) => candidate.id === replan.id ? { ...candidate, state: "approved" } : candidate));
24557
+ const originaltail = draft.steps.filter((step) => !replan.completedstepids.includes(step.id));
24558
+ for (let index = 0; index < originaltail.length && index < replan.tail.length; index += 1) {
24559
+ const before = originaltail[index];
24560
+ const after = replan.tail[index];
24561
+ if (before === void 0 || after === void 0) continue;
24562
+ await memory.addcorrection(editedcorrectionof({ origin: session?.origin ?? "", kind: after.kind, stepid: after.id, original: JSON.stringify({ kind: before.kind, target: before.target, value: before.value, summary: before.summary }), corrected: JSON.stringify({ kind: after.kind, target: after.target, value: after.value, summary: after.summary }), reason: `The plan review replaced the failed tail step ${before.id} with the revised step ${after.id} of the replan ${replan.id}.`, now: Date.now() })).catch(() => {
24563
+ });
24564
+ }
23193
24565
  await audit("model", `The user approved the fresh review of the replan ${replan.id}: ${completed.length} completed step${completed.length === 1 ? "" : "s"} stay and the ${replan.tail.length} revised step${replan.tail.length === 1 ? "" : "s"} became the changed tail of a pending plan that still passes the same plan review.`, { planid: plan.id, ...session ? { sessionid: session.id } : {} });
23194
24566
  return llmstateof();
23195
24567
  }
@@ -23861,6 +25233,10 @@ async function handlerequest(message, sender) {
23861
25233
  await audit("transparency", `The transparencypage read its transparency report in one memory read: ${report.grants.length} grant row${report.grants.length === 1 ? "" : "s"} with revoke actions, ${report.windows.length} consent window${report.windows.length === 1 ? "" : "s"}, ${report.connectallow.length} connectallow entr${report.connectallow.length === 1 ? "y" : "ies"}, ${report.permdiffs.length} permdiff record${report.permdiffs.length === 1 ? "" : "s"} and ${report.vault.length} vault label${report.vault.length === 1 ? "" : "s"}.`, {});
23862
25234
  return report;
23863
25235
  }
25236
+ case "sessions":
25237
+ return handlesessionscommand(message);
25238
+ case "surface":
25239
+ return handlesurfacecommand(message);
23864
25240
  case "security": {
23865
25241
  const input2 = message;
23866
25242
  const now = Date.now();
@@ -23891,6 +25267,8 @@ async function handlerequest(message, sender) {
23891
25267
  const existing = profiles.find((candidate) => candidate.origin === origin);
23892
25268
  const updated = profilekind({ profile: existing ?? originprofileof({ origin, now }), kind, decision, now });
23893
25269
  await memory.saveoriginprofile(updated);
25270
+ if (decision === "deny") await memory.addconsentmemoryentry(consentmemoryof({ origin, decision: "deny", boundary: "the origin profile the user edits", kinds: [kind], now })).catch(() => {
25271
+ });
23894
25272
  await audit("grant", `The origin profile of ${origin} now ${decision === "grant" ? "grants" : "denies"} the ${kind} kind the user reviewed; ${updated.grants.length} grant${updated.grants.length === 1 ? "" : "s"} and ${updated.denials.length} denial${updated.denials.length === 1 ? "" : "s"} on the origin.`, { ...session ? { sessionid: session.id } : {} });
23895
25273
  return { ...await securityviewof(), profile: updated };
23896
25274
  }
@@ -23905,6 +25283,8 @@ async function handlerequest(message, sender) {
23905
25283
  const kinds = (input2.consent.open.kinds ?? []).map((kind) => kind.trim()).filter((kind) => kind !== "");
23906
25284
  const window2 = openconsentwindow({ sessionid: session.id, origin, duration, kinds: kinds.length > 0 ? kinds : ["observe"], now });
23907
25285
  await memory.setconsentwindows([window2, ...(await memory.getconsentwindows()).map((candidate) => candidate.sessionid === session.id && candidate.origin === origin && candidate.state === "active" ? { ...candidate, state: "closed", closedat: now } : candidate)]);
25286
+ await memory.addconsentmemoryentry(consentmemoryof({ origin, decision: "grant", boundary: window2.boundary, kinds: window2.kinds, expiresat: window2.expiresat, now })).catch(() => {
25287
+ });
23908
25288
  await appendrunevent("grant", `The consent prompt opened the window ${window2.id} for ${origin} with the boundary ${window2.boundary}.`, session, origin).catch(() => {
23909
25289
  });
23910
25290
  await audit("grant", `The user answered the consent prompt for ${origin} with the window ${window2.id} and the boundary ${window2.boundary}; no grant ever outlives its named boundary.`, { sessionid: session.id });
@@ -23921,6 +25301,8 @@ async function handlerequest(message, sender) {
23921
25301
  if (!durationgate.allowed) throw new Error(durationgate.reason);
23922
25302
  const { renewed, closed } = renewconsentwindow({ window: current, duration, kinds: current.kinds, now });
23923
25303
  await memory.setconsentwindows([renewed, closed, ...(await memory.getconsentwindows()).filter((candidate) => candidate.id !== windowid)]);
25304
+ await memory.addconsentmemoryentry(consentmemoryof({ origin: current.origin, decision: "grant", boundary: renewed.boundary, kinds: renewed.kinds, expiresat: renewed.expiresat, now })).catch(() => {
25305
+ });
23924
25306
  await appendrunevent("grant", `The consent window of ${current.origin} renewed through a new explicit prompt with the boundary ${renewed.boundary}; the old window stays closed in the history.`, session, current.origin).catch(() => {
23925
25307
  });
23926
25308
  await audit("grant", `The user renewed the consent window of ${current.origin} through a new explicit prompt with the boundary ${renewed.boundary}.`, { sessionid: session.id });
@@ -23949,6 +25331,8 @@ async function handlerequest(message, sender) {
23949
25331
  const queued = (plan2?.steps ?? []).map((step) => step.id).filter((id) => !completed.has(id));
23950
25332
  const revocation = revokerun({ sessionid: session.id, runid, ...pendingstepid !== void 0 ? { pendingstepid } : {}, ...queued.length > 0 ? { queuedstepids: queued } : {}, actor: "user", ...input2.revoke.reason !== void 0 ? { reason: input2.revoke.reason } : {}, now });
23951
25333
  await memory.addrevocation(revocation);
25334
+ await memory.addconsentmemoryentry(consentmemoryof({ origin: session.origin, decision: "revoke", boundary: "the mid run revocation of the user", kinds: ["observe"], now })).catch(() => {
25335
+ });
23952
25336
  if (plan2 && plan2.id === runid && ["pending", "approved"].includes(plan2.state)) await memory.setplan({ ...plan2, state: "cancelled" });
23953
25337
  if (plan2 && plan2.id === runid && pendingstepid !== void 0) await memory.setprogress(recordrevocation(progress, runid, pendingstepid, { haltedstepids: revocation.haltedstepids, revokedstepid: pendingstepid, reason: revocation.reason }, now));
23954
25338
  await appendrunevent("revoke", `The user revoked the run ${runid}: the pending step ${pendingstepid ?? "none"} and every queued step halted without executing (${revocation.haltedstepids.join(", ")}).`, session, session.origin, pendingstepid).catch(() => {
@@ -24904,6 +26288,14 @@ chrome.runtime.onStartup.addListener(() => {
24904
26288
  void runwatchdog().catch(() => {
24905
26289
  });
24906
26290
  });
26291
+ chrome.runtime.onInstalled.addListener((details) => {
26292
+ void (async () => {
26293
+ if (details.reason !== "install") return;
26294
+ await memory.setonboardingstate(onboardingstart(await memory.getonboardingstate(), Date.now()));
26295
+ await audit("onboarding", "Devthink installed for the first time and the onboarding walkthrough started; it walks the origin grants, the plan review, the run control and the log audit once.", {});
26296
+ })().catch(() => {
26297
+ });
26298
+ });
24907
26299
  async function pauseinterruptedworkflowruns() {
24908
26300
  for (const run of await memory.listworkflowruns()) {
24909
26301
  if (run.state !== "running") continue;