@wenathlan/extension 1.1.64 → 1.1.66
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.
- package/README.md +5 -3
- package/dist/attentionfeed.d.ts +74 -0
- package/dist/attentionfeed.d.ts.map +1 -0
- package/dist/backgroundruns.d.ts +71 -0
- package/dist/backgroundruns.d.ts.map +1 -0
- package/dist/datagrid.d.ts +46 -0
- package/dist/datagrid.d.ts.map +1 -0
- package/dist/evidenceviews.d.ts +45 -0
- package/dist/evidenceviews.d.ts.map +1 -0
- package/dist/flowlibrary.d.ts +147 -0
- package/dist/flowlibrary.d.ts.map +1 -0
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1437 -3
- package/dist/index.js.map +4 -4
- package/dist/memory.d.ts +104 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/outputcompare.d.ts +68 -0
- package/dist/outputcompare.d.ts.map +1 -0
- package/dist/pickerviews.d.ts +72 -0
- package/dist/pickerviews.d.ts.map +1 -0
- package/dist/policy.d.ts +122 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/portability.d.ts +35 -0
- package/dist/portability.d.ts.map +1 -0
- package/dist/protocol.d.ts +281 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/quickactions.d.ts +46 -0
- package/dist/quickactions.d.ts.map +1 -0
- package/dist/runreplay.d.ts +53 -0
- package/dist/runreplay.d.ts.map +1 -0
- package/dist/siteprefs.d.ts +45 -0
- package/dist/siteprefs.d.ts.map +1 -0
- package/dist/statusviews.d.ts +65 -0
- package/dist/statusviews.d.ts.map +1 -0
- package/dist/surfaces.d.ts +2 -2
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/syncbridge.d.ts +80 -0
- package/dist/syncbridge.d.ts.map +1 -0
- package/dist/tourviews.d.ts +28 -0
- package/dist/tourviews.d.ts.map +1 -0
- package/dist/types.d.ts +444 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +1841 -5
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/dashboardpage.html +3 -0
- package/extension/dist/dashboardpage.js +108 -0
- package/extension/dist/dashboardpage.js.map +2 -2
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/optionspage.html +5 -0
- package/extension/dist/optionspage.js +222 -0
- package/extension/dist/optionspage.js.map +2 -2
- package/extension/dist/pagebridge.js.map +1 -1
- package/extension/dist/popup.html +2 -2
- package/extension/dist/popup.js +102 -0
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.html +2 -2
- package/extension/dist/sidepanel.js +219 -3
- package/extension/dist/sidepanel.js.map +2 -2
- package/extension/dist/style.css +3 -1
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
|
@@ -5159,6 +5159,173 @@ var sessionmemory = class {
|
|
|
5159
5159
|
async addstepapproveresolution(resolution) {
|
|
5160
5160
|
await this.adapter.set("stepapproveresolutions", [resolution, ...await this.getstepapproveresolutions()]);
|
|
5161
5161
|
}
|
|
5162
|
+
/**
|
|
5163
|
+
* Interface surface stores of the 1.1.65 family live here, scoped per profile workspace: the siteprofiles with the per site interface preferences, the shortcutkeys bindings and the theme preference per profile, the recenttray entries with their configurable depth and the notification consent and preference per profile.
|
|
5164
|
+
*/
|
|
5165
|
+
/** Returns the siteprofile of one origin; an absent profile keeps the global interface preferences. */
|
|
5166
|
+
async getsiteprofile(origin) {
|
|
5167
|
+
return this.adapter.get(`siteprofile:${origin}`);
|
|
5168
|
+
}
|
|
5169
|
+
/** Stores the siteprofile of one origin with its theme, shortcutkeys and default view; the profile never adjusts a policy gate. */
|
|
5170
|
+
async setsiteprofile(profile) {
|
|
5171
|
+
return this.adapter.set(`siteprofile:${profile.origin}`, profile);
|
|
5172
|
+
}
|
|
5173
|
+
/** Returns every stored siteprofile keyed by origin. */
|
|
5174
|
+
async listsiteprofiles() {
|
|
5175
|
+
const entries = Object.entries(await this.adapter.get("siteprofiles") ?? {});
|
|
5176
|
+
return entries.map(([, profile]) => profile);
|
|
5177
|
+
}
|
|
5178
|
+
/** Stores every siteprofile keyed by origin so the list view reads them in one call. */
|
|
5179
|
+
async setsiteprofiles(profiles) {
|
|
5180
|
+
await this.adapter.set("siteprofiles", Object.fromEntries(profiles.map((profile) => [profile.origin, profile])));
|
|
5181
|
+
}
|
|
5182
|
+
/** Returns the stored shortcutkeys bindings of the profile; an absent set keeps the shipped editable defaults. */
|
|
5183
|
+
async getshortcutbindings() {
|
|
5184
|
+
return await this.adapter.get("shortcutbindings") ?? [];
|
|
5185
|
+
}
|
|
5186
|
+
/** Stores the shortcutkeys bindings the user edited in the optionspage. */
|
|
5187
|
+
async setshortcutbindings(bindings) {
|
|
5188
|
+
return this.adapter.set("shortcutbindings", bindings);
|
|
5189
|
+
}
|
|
5190
|
+
/** Returns the stored darklight theme preference of the profile; an absent preference follows the os preference alone. */
|
|
5191
|
+
async getthemepreference() {
|
|
5192
|
+
return this.adapter.get("themepreference");
|
|
5193
|
+
}
|
|
5194
|
+
/** Stores the darklight theme preference of the profile with its manual override. */
|
|
5195
|
+
async setthemepreference(preference) {
|
|
5196
|
+
return this.adapter.set("themepreference", preference);
|
|
5197
|
+
}
|
|
5198
|
+
/** Returns the recenttray entries, newest first, with their resume and reopen offers. */
|
|
5199
|
+
async getrecenttray() {
|
|
5200
|
+
return await this.adapter.get("recenttray") ?? [];
|
|
5201
|
+
}
|
|
5202
|
+
/** Adds one recenttray entry with the user configured depth; an absent depth keeps every run. */
|
|
5203
|
+
async addrecenttrayentry(entry) {
|
|
5204
|
+
const depth = (await this.getsettings())?.recenttraydepth;
|
|
5205
|
+
const appended = [entry, ...(await this.getrecenttray()).filter((candidate) => candidate.runid !== entry.runid)];
|
|
5206
|
+
await this.adapter.set("recenttray", depth !== void 0 && Number.isInteger(depth) && depth > 0 ? appended.slice(0, depth) : appended);
|
|
5207
|
+
}
|
|
5208
|
+
/** Returns the notification consent and preference of the profile; an absent record keeps the notifications content free and on. */
|
|
5209
|
+
async getnotificationprefs() {
|
|
5210
|
+
return this.adapter.get("notificationprefs");
|
|
5211
|
+
}
|
|
5212
|
+
/** Stores the notification consent and preference of the profile; the content consent gates every page content bearing body. */
|
|
5213
|
+
async setnotificationprefs(prefs) {
|
|
5214
|
+
return this.adapter.set("notificationprefs", prefs);
|
|
5215
|
+
}
|
|
5216
|
+
/** Returns the notification payloads the surface history keeps for the user to open after a do not disturb quiet. */
|
|
5217
|
+
async getnotificationhistory() {
|
|
5218
|
+
return await this.adapter.get("notificationhistory") ?? [];
|
|
5219
|
+
}
|
|
5220
|
+
/** Records one notification payload in the history so its deep link stays reachable while the notifications permission stays outside the manifest. */
|
|
5221
|
+
async addnotificationhistory(payload) {
|
|
5222
|
+
await this.adapter.set("notificationhistory", [payload, ...await this.getnotificationhistory()]);
|
|
5223
|
+
}
|
|
5224
|
+
/**
|
|
5225
|
+
* Ecosystem stores of the 1.1.66 family live here, scoped per profile workspace: the flowlibrary entries with their manifest digests and provenance, the library install and removal events, the syncbridge hooks with their conflict records, the attentionfeed entries with their configurable retention, the runreplay cursors per viewed run, the outputcompare sessions with their metric results and the background run queue state for restart recovery.
|
|
5226
|
+
* The flowlibrary store deduplicates entries by manifest digest, every entry carries its publisher provenance, and the manifest list exports for audit; the memory adapter seam stays the documented marketplace backend boundary because a future remote registry replaces the adapter only.
|
|
5227
|
+
*/
|
|
5228
|
+
/** Returns every flowlibrary entry of the profile workspace, newest first. */
|
|
5229
|
+
async getflowlibrary() {
|
|
5230
|
+
return await this.adapter.get("flowlibrary") ?? [];
|
|
5231
|
+
}
|
|
5232
|
+
/** Replaces the flowlibrary entries of the profile workspace. */
|
|
5233
|
+
async setflowlibrary(entries) {
|
|
5234
|
+
return this.adapter.set("flowlibrary", entries);
|
|
5235
|
+
}
|
|
5236
|
+
/** Adds one flowlibrary entry deduplicated by manifest digest: an entry whose digest already exists replaces its predecessor while its provenance keeps both records. */
|
|
5237
|
+
async addlibraryentry(entry) {
|
|
5238
|
+
const entries = await this.getflowlibrary();
|
|
5239
|
+
const deduped = entries.filter((candidate) => candidate.digest !== entry.digest);
|
|
5240
|
+
await this.setflowlibrary([entry, ...deduped]);
|
|
5241
|
+
return [entry, ...deduped];
|
|
5242
|
+
}
|
|
5243
|
+
/** Removes one flowlibrary entry by its id while the library events keep their record for the audit trail. */
|
|
5244
|
+
async removelibraryentry(entryid) {
|
|
5245
|
+
await this.setflowlibrary((await this.getflowlibrary()).filter((candidate) => candidate.id !== entryid));
|
|
5246
|
+
}
|
|
5247
|
+
/** Returns every library install, update and removal event, newest first. */
|
|
5248
|
+
async getlibraryevents() {
|
|
5249
|
+
return await this.adapter.get("libraryevents") ?? [];
|
|
5250
|
+
}
|
|
5251
|
+
/** Records one library lifecycle event beside the flowlibrary store. */
|
|
5252
|
+
async addlibraryevent(event) {
|
|
5253
|
+
await this.adapter.set("libraryevents", [event, ...await this.getlibraryevents()]);
|
|
5254
|
+
}
|
|
5255
|
+
/** Exports the manifest list of the flowlibrary for audit: one row per entry with its digest, publisher, version, state and provenance and no step payload. */
|
|
5256
|
+
async exportlibrarymanifests() {
|
|
5257
|
+
return (await this.getflowlibrary()).map((entry) => ({ id: entry.id, title: entry.manifest.title, publisher: entry.manifest.publisher, version: entry.manifest.version, digest: entry.digest, state: entry.state, provenance: entry.provenance, addedat: entry.addedat }));
|
|
5258
|
+
}
|
|
5259
|
+
/** Returns every syncbridge hook of the profile workspace; every hook keeps its explicit opt in with no default on. */
|
|
5260
|
+
async getsyncbridgehooks() {
|
|
5261
|
+
return await this.adapter.get("syncbridgehooks") ?? [];
|
|
5262
|
+
}
|
|
5263
|
+
/** Replaces the syncbridge hooks of the profile workspace. */
|
|
5264
|
+
async setsyncbridgehooks(hooks) {
|
|
5265
|
+
return this.adapter.set("syncbridgehooks", hooks);
|
|
5266
|
+
}
|
|
5267
|
+
/** Returns every syncbridge conflict record, newest first, with both versions instead of a silent overwrite. */
|
|
5268
|
+
async getsyncbridgeconflicts() {
|
|
5269
|
+
return await this.adapter.get("syncbridgeconflicts") ?? [];
|
|
5270
|
+
}
|
|
5271
|
+
/** Records one syncbridge conflict with both manifest versions. */
|
|
5272
|
+
async addsyncbridgeconflict(conflict) {
|
|
5273
|
+
await this.adapter.set("syncbridgeconflicts", [conflict, ...await this.getsyncbridgeconflicts()]);
|
|
5274
|
+
}
|
|
5275
|
+
/** Resolves one syncbridge conflict by its id with the resolution the user picked; one conflict resolves exactly once. */
|
|
5276
|
+
async resolvesyncbridgeconflict(id, resolution, now) {
|
|
5277
|
+
const conflicts = await this.getsyncbridgeconflicts();
|
|
5278
|
+
await this.adapter.set("syncbridgeconflicts", conflicts.map((conflict) => conflict.id === id && conflict.resolution === void 0 ? { ...conflict, resolution, resolvedat: now } : conflict));
|
|
5279
|
+
return this.getsyncbridgeconflicts();
|
|
5280
|
+
}
|
|
5281
|
+
/** Returns every attentionfeed entry, newest first, with its cause, refs and deep link. */
|
|
5282
|
+
async getattentionentries() {
|
|
5283
|
+
return await this.adapter.get("attentionfeed") ?? [];
|
|
5284
|
+
}
|
|
5285
|
+
/** Records one attentionfeed entry deduplicated by its cause, run and gate refs while the retention window stays a user setting. */
|
|
5286
|
+
async addattentionentry(entry) {
|
|
5287
|
+
const existing = (await this.getattentionentries()).filter((candidate) => candidate.id !== entry.id);
|
|
5288
|
+
await this.adapter.set("attentionfeed", [entry, ...existing]);
|
|
5289
|
+
}
|
|
5290
|
+
/** Dismisses one attentionfeed entry by its id: the dismissal removes the feed row only while the waiting cause keeps its own resolution path. */
|
|
5291
|
+
async dismissattentionentry(id) {
|
|
5292
|
+
const entries = (await this.getattentionentries()).filter((candidate) => candidate.id !== id);
|
|
5293
|
+
await this.adapter.set("attentionfeed", entries);
|
|
5294
|
+
return entries;
|
|
5295
|
+
}
|
|
5296
|
+
/** Prunes the attentionfeed entries past their retention window; an absent window keeps every entry while the pruned ids return for the audit note. */
|
|
5297
|
+
async pruneattentionentries(now) {
|
|
5298
|
+
const retention = (await this.getsettings())?.attentionretention;
|
|
5299
|
+
const entries = await this.getattentionentries();
|
|
5300
|
+
if (retention === void 0) return { kept: entries, pruned: [] };
|
|
5301
|
+
const kept = entries.filter((entry) => now - entry.at < retention);
|
|
5302
|
+
await this.adapter.set("attentionfeed", kept);
|
|
5303
|
+
return { kept, pruned: entries.filter((entry) => now - entry.at >= retention).map((entry) => entry.id) };
|
|
5304
|
+
}
|
|
5305
|
+
/** Returns the runreplay cursors per viewed run so a reopened replay stands where the viewer left it. */
|
|
5306
|
+
async getreplaycursors() {
|
|
5307
|
+
return await this.adapter.get("replaycursors") ?? {};
|
|
5308
|
+
}
|
|
5309
|
+
/** Stores one runreplay cursor for its viewed run. */
|
|
5310
|
+
async setreplaycursor(runid, cursor) {
|
|
5311
|
+
await this.adapter.set("replaycursors", { ...await this.getreplaycursors(), [runid]: cursor });
|
|
5312
|
+
}
|
|
5313
|
+
/** Returns every outputcompare session with its metric results, newest first. */
|
|
5314
|
+
async getcomparesessions() {
|
|
5315
|
+
return await this.adapter.get("comparesessions") ?? [];
|
|
5316
|
+
}
|
|
5317
|
+
/** Records one outputcompare session with the metric set it used. */
|
|
5318
|
+
async addcomparesession(session) {
|
|
5319
|
+
await this.adapter.set("comparesessions", [session, ...await this.getcomparesessions()]);
|
|
5320
|
+
}
|
|
5321
|
+
/** Returns the background run queue state for restart recovery: every entry with its state and its keepalive hold. */
|
|
5322
|
+
async getbackgroundqueue() {
|
|
5323
|
+
return await this.adapter.get("backgroundqueue") ?? [];
|
|
5324
|
+
}
|
|
5325
|
+
/** Replaces the background run queue state after every transition so the restart recovery reads it in one call. */
|
|
5326
|
+
async setbackgroundqueue(queue) {
|
|
5327
|
+
return this.adapter.set("backgroundqueue", queue);
|
|
5328
|
+
}
|
|
5162
5329
|
};
|
|
5163
5330
|
function mediakindof(record2) {
|
|
5164
5331
|
if ("pages" in record2) return "pdf";
|
|
@@ -10712,6 +10879,97 @@ function logstreamegressgate(input) {
|
|
|
10712
10879
|
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
10880
|
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
10881
|
}
|
|
10882
|
+
function quickactiongate(input) {
|
|
10883
|
+
if (input.action.origin.trim() === "") return { allowed: false, reason: `The ${input.action.command} quickaction needs the origin of the clicked tab; an originless entry never registers.` };
|
|
10884
|
+
if (!input.granted.includes(input.action.origin)) return { allowed: false, reason: `The ${input.action.command} quickaction stays off the ${input.action.origin} tab because its origin holds no allowlist entry; only permitted actions surface.` };
|
|
10885
|
+
if (input.action.session === true && !input.sessionactive) return { allowed: false, reason: `The ${input.action.command} quickaction needs an active browser session before it registers; the context menu never offers a run action without its session.` };
|
|
10886
|
+
if (input.action.permission !== void 0 && !(input.capabilities ?? []).includes(input.action.permission)) return { allowed: false, reason: `The ${input.action.command} quickaction needs the ${input.action.permission} capability granted before it registers; the context menu never offers an action the current capability set refuses.` };
|
|
10887
|
+
return { allowed: true, reason: `The ${input.action.command} quickaction rides the origin allowlist of the clicked ${input.action.origin} tab and registers.` };
|
|
10888
|
+
}
|
|
10889
|
+
function omniboxtaskgate(input) {
|
|
10890
|
+
if (input.direct) return { allowed: false, reason: "The omnibox keyword never executes a goal directly; every keyword goal routes through the same proposal and review flow as the api and becomes a reviewed plan first." };
|
|
10891
|
+
if (input.text.trim() === "") return { allowed: false, reason: "The omnibox task needs its natural language goal after the keyword; an empty goal never reaches the proposal flow." };
|
|
10892
|
+
if (input.origin.trim() === "") return { allowed: false, reason: "The omnibox task needs its active origin scope; a goal without an origin never reaches the proposal flow." };
|
|
10893
|
+
return { allowed: true, reason: `The omnibox goal for ${input.origin} rides the same proposal flow as the api: the observation, the capabilities and the plan review all recheck it.` };
|
|
10894
|
+
}
|
|
10895
|
+
function notificationcontentgate(input) {
|
|
10896
|
+
if (!input.content) return { allowed: true, reason: "The notification body carries no page content, so no content consent is needed and it shows." };
|
|
10897
|
+
if (!input.consent) return { allowed: false, reason: "The notification body carries page content and no content consent exists; a content bearing notification never shows without its consent." };
|
|
10898
|
+
return { allowed: true, reason: "The notification body carries page content and its consent exists, so it shows with the content the user agreed to." };
|
|
10899
|
+
}
|
|
10900
|
+
function pickeroverlaygate(input) {
|
|
10901
|
+
if (input.origin.trim() === "") return { allowed: false, reason: "The pickeroverlay session needs its origin; an originless read never starts." };
|
|
10902
|
+
if (!input.granted.includes(input.origin)) return { allowed: false, reason: `The pickeroverlay reads no element candidate of ${input.origin} because the origin holds no allowlist entry; picker reads stay inside the granted origins.` };
|
|
10903
|
+
return { allowed: true, reason: `The pickeroverlay lists the element candidates of the granted origin ${input.origin} with their stability scored selectors.` };
|
|
10904
|
+
}
|
|
10905
|
+
function shotpanelgate(input) {
|
|
10906
|
+
if (input.captureorigin.trim() === "") return { allowed: false, reason: "The shotpanel view needs the origin of its capture; an originless capture never opens." };
|
|
10907
|
+
if (!input.granted.includes(input.captureorigin)) return { allowed: false, reason: `The shotpanel opens no capture of ${input.captureorigin} because the origin holds no allowlist entry; capture views stay inside the granted origins.` };
|
|
10908
|
+
return { allowed: true, reason: `The shotpanel previews the capture of the granted origin ${input.captureorigin} with its redaction verdicts.` };
|
|
10909
|
+
}
|
|
10910
|
+
function siteprofilegate(input) {
|
|
10911
|
+
const origin = input.origin.trim();
|
|
10912
|
+
if (origin === "") return { allowed: false, reason: "The siteprofile needs its origin; an originless profile never stores." };
|
|
10913
|
+
if (!origin.startsWith("https://") || origin.length <= "https://".length) return { allowed: false, reason: `The siteprofile stores per site interface preferences of https origins only; ${origin} holds no https origin shape.` };
|
|
10914
|
+
return { allowed: true, reason: `The siteprofile of ${origin} stores its theme, shortcutkeys and default view beside the originprofiles policy preferences; no profile ever adjusts a policy gate.` };
|
|
10915
|
+
}
|
|
10916
|
+
function importexportgate(input) {
|
|
10917
|
+
if (input.containssecrets) return { allowed: false, reason: "The importexport bundle carries a secretvault value shape; secret values never leave the browser under any flag, so the bundle refuses in full." };
|
|
10918
|
+
if (input.unmaskedlogs) return { allowed: false, reason: "The importexport bundle carries unmasked log entries; only masked summaries ever move between profiles, so the bundle refuses in full." };
|
|
10919
|
+
return { allowed: true, reason: "The importexport bundle carries no secretvault value and no unmasked log; the originprofiles, the siteprofiles, the notes and the preferences move with their honest exclusion list." };
|
|
10920
|
+
}
|
|
10921
|
+
function librarymanifestgate(input) {
|
|
10922
|
+
if (input.errors.length > 0) return { allowed: false, reason: `The flowlibrary manifest fails schemastrict with ${input.errors.length} error${input.errors.length === 1 ? "" : "s"}: ${input.errors.slice(0, 3).map((error) => `${error.path} expected ${error.expected}`).join("; ")}; the import refuses before anything else.` };
|
|
10923
|
+
return { allowed: true, reason: "The flowlibrary manifest passes schemastrict with no shape error; the validation names every field it checked." };
|
|
10924
|
+
}
|
|
10925
|
+
function librarycapabilitygate(input) {
|
|
10926
|
+
const missing = [...new Set(input.kinds)].filter((kind) => !input.capabilities.includes(kind));
|
|
10927
|
+
if (missing.length > 0) return { allowed: false, reason: `The flowlibrary manifest uses the kind${missing.length === 1 ? "" : "s"} ${missing.join(", ")} the installed capability set lacks; the import refuses in full.` };
|
|
10928
|
+
return { allowed: true, reason: `Every kind of the flowlibrary manifest sits inside the installed capability set of ${input.capabilities.length} kind${input.capabilities.length === 1 ? "" : "s"}.` };
|
|
10929
|
+
}
|
|
10930
|
+
function librarygrantgate(input) {
|
|
10931
|
+
const missing = [...new Set(input.requiredgrants)].filter((origin) => !input.heldgrants.includes(origin));
|
|
10932
|
+
if (missing.length > 0) return { allowed: false, reason: `The flowlibrary manifest requires the grant${missing.length === 1 ? "" : "s"} ${missing.join(", ")} the profile does not hold; the grant diff shows them and the user grants them before the import completes.` };
|
|
10933
|
+
return { allowed: true, reason: `The profile holds every grant the flowlibrary manifest requires${input.requiredgrants.length === 0 ? " and the manifest requires none" : ""}.` };
|
|
10934
|
+
}
|
|
10935
|
+
function librarysensitivegate(input) {
|
|
10936
|
+
if (!input.sensitive) return { allowed: true, reason: "The flowlibrary manifest carries no sensitive mark, so no fresh consent prompt stands before its import." };
|
|
10937
|
+
if (!input.freshconsent) return { allowed: false, reason: "The flowlibrary manifest is marked sensitive; its import needs a fresh consent prompt the user answers before anything lands." };
|
|
10938
|
+
return { allowed: true, reason: "The user answered the fresh consent prompt of the sensitive flowlibrary manifest; the import proceeds behind the same review." };
|
|
10939
|
+
}
|
|
10940
|
+
function libraryquarantinegate(input) {
|
|
10941
|
+
if (!input.signaturepresent && !input.verified) return { allowed: false, reason: "The flowlibrary entry carries no publisher signature; the entry quarantines until the user verifies its publisher, and a quarantined entry never installs on its own." };
|
|
10942
|
+
if (input.signaturepresent && !input.signaturevalid) return { allowed: false, reason: "The publisher signature of the flowlibrary entry failed its verification; the entry quarantines and never installs under any flag." };
|
|
10943
|
+
return { allowed: true, reason: "The publisher signature of the flowlibrary entry verified over its manifest digest; the entry stays available for the grant diff and the import." };
|
|
10944
|
+
}
|
|
10945
|
+
function syncbridgeoptingate(input) {
|
|
10946
|
+
if (!input.optin) return { allowed: false, reason: "The syncbridge hook stays off because no explicit opt in exists; no hook ever defaults on and no manifest moves without the user turning the hook on." };
|
|
10947
|
+
return { allowed: true, reason: "The user explicitly opted the syncbridge hook in; the hook moves manifests only and never secrets or logs." };
|
|
10948
|
+
}
|
|
10949
|
+
function syncbridgescopegate(input) {
|
|
10950
|
+
if (input.carriessecrets) return { allowed: false, reason: "The syncbridge payload carries a secretvault value shape; the bridge moves manifests only, so the payload refuses in full." };
|
|
10951
|
+
if (input.carrieslogs) return { allowed: false, reason: "The syncbridge payload carries log entries; the bridge moves manifests only, so the payload refuses in full." };
|
|
10952
|
+
return { allowed: true, reason: "The syncbridge payload carries manifests only; secrets and logs never ride the bridge under any flag." };
|
|
10953
|
+
}
|
|
10954
|
+
function runreplaygate(input) {
|
|
10955
|
+
if (!input.sealed) return { allowed: false, reason: "The runreplay walks sealed runs only; an open run keeps moving and its replay would show a chain that still grows." };
|
|
10956
|
+
if (!input.chainvalid) return { allowed: false, reason: "The sealed chain of the run failed its verification; the replay refuses the walk because only a verified chain stands as audit evidence." };
|
|
10957
|
+
return { allowed: true, reason: "The run sealed and its chain verified from the genesis hash to the seal; the replay walks it read only, restoring the observation and capture of each step." };
|
|
10958
|
+
}
|
|
10959
|
+
function outputcomparegate(input) {
|
|
10960
|
+
if (input.signaturea.trim() === "" || input.signatureb.trim() === "") return { allowed: false, reason: "The outputcompare needs the task input signature of both runs; a signatureless run never compares." };
|
|
10961
|
+
if (input.signaturea !== input.signatureb) return { allowed: false, reason: "The two runs carry different task input signatures; only runs that started from the same input compare their outcomes." };
|
|
10962
|
+
return { allowed: true, reason: "The two runs share their task input signature, so their step outcomes compare under the recorded metric set." };
|
|
10963
|
+
}
|
|
10964
|
+
function outputcomparereadonlygate(input) {
|
|
10965
|
+
if (input.executessteps) return { allowed: false, reason: "The outputcompare never executes a step; it reads the stored outcomes of both runs only, so any executing path refuses in full." };
|
|
10966
|
+
return { allowed: true, reason: "The outputcompare joins the stored outcomes of both runs without touching the page; no step executes inside a comparison." };
|
|
10967
|
+
}
|
|
10968
|
+
function backgroundrungate(input) {
|
|
10969
|
+
if (!input.reviewed) return { allowed: false, reason: "The background run queue executes reviewed workflows only; an unreviewed workflow never starts, with or without an open surface." };
|
|
10970
|
+
if (!input.keepaliveheld) return { allowed: false, reason: "A background run holds the keepalive signal for its whole duration; a run that releases the signal early stops being a background run." };
|
|
10971
|
+
return { allowed: true, reason: "The reviewed workflow runs in the background with the keepalive signal held and every checkpoint restoring it on each worker wake." };
|
|
10972
|
+
}
|
|
10715
10973
|
|
|
10716
10974
|
// progress.ts
|
|
10717
10975
|
function emptyprogress(planid, now) {
|
|
@@ -10962,7 +11220,7 @@ function maskexport(record2, shapes) {
|
|
|
10962
11220
|
}
|
|
10963
11221
|
|
|
10964
11222
|
// version.ts
|
|
10965
|
-
var packageversion = "1.1.
|
|
11223
|
+
var packageversion = "1.1.66";
|
|
10966
11224
|
|
|
10967
11225
|
// types.ts
|
|
10968
11226
|
var protocolversion = packageversion;
|
|
@@ -11942,6 +12200,9 @@ function transparencyreport(input) {
|
|
|
11942
12200
|
function surfacesnapshot(input) {
|
|
11943
12201
|
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
12202
|
}
|
|
12203
|
+
function ecosystemviews(input) {
|
|
12204
|
+
return { version: protocolversion, library: input.library, installed: input.installed, syncbridge: input.syncbridge, attention: input.attention, backgroundruns: input.backgroundruns, ...input.replay !== void 0 ? { replay: input.replay } : {}, ...input.compare !== void 0 ? { compare: input.compare } : {} };
|
|
12205
|
+
}
|
|
11945
12206
|
|
|
11946
12207
|
// capture.ts
|
|
11947
12208
|
var capturekinds = ["shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet"];
|
|
@@ -13367,7 +13628,8 @@ function onboardingsteps() {
|
|
|
13367
13628
|
{ 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
13629
|
{ 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
13630
|
{ 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" }
|
|
13631
|
+
{ 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" },
|
|
13632
|
+
{ id: "library", surface: "dashboardpage", title: "Flow library", body: "The flowlibrary shares reviewed workflow templates: browse an entry, read its step list and grant diff, and every install still lands as a proposal behind the same review.", completion: "librarycompleted", optional: true }
|
|
13371
13633
|
];
|
|
13372
13634
|
}
|
|
13373
13635
|
function onboardingstart(previous, now) {
|
|
@@ -13378,7 +13640,7 @@ function onboardingcomplete(state, stepid, now) {
|
|
|
13378
13640
|
const step = steps.find((candidate) => candidate.id === stepid);
|
|
13379
13641
|
if (step === void 0) throw new Error(`The onboarding knows no ${stepid} step.`);
|
|
13380
13642
|
const completed = state.stepscompleted.includes(stepid) ? state.stepscompleted : [...state.stepscompleted, stepid];
|
|
13381
|
-
const done = steps.every((candidate) => completed.includes(candidate.id));
|
|
13643
|
+
const done = steps.filter((candidate) => candidate.optional !== true).every((candidate) => completed.includes(candidate.id));
|
|
13382
13644
|
if (!done) return { state: { ...state, stepscompleted: completed, done: false } };
|
|
13383
13645
|
const consentevent = "onboardingconsentgranted";
|
|
13384
13646
|
return { state: { stepscompleted: completed, done: true, ...state.startedat !== void 0 ? { startedat: state.startedat } : {}, consentevent, completedat: now }, consentevent };
|
|
@@ -13538,6 +13800,469 @@ function loglevelof(kind) {
|
|
|
13538
13800
|
return "info";
|
|
13539
13801
|
}
|
|
13540
13802
|
|
|
13803
|
+
// datagrid.ts
|
|
13804
|
+
function infercolumntype(values) {
|
|
13805
|
+
const present = values.filter((value) => value.trim() !== "");
|
|
13806
|
+
if (present.length === 0) return "empty";
|
|
13807
|
+
if (present.every((value) => /^-?\d+(?:\.\d+)?$/.test(value.trim()))) return "number";
|
|
13808
|
+
if (present.every((value) => value.trim() === "true" || value.trim() === "false")) return "boolean";
|
|
13809
|
+
if (present.every((value) => !Number.isNaN(Date.parse(value.trim())) && /\d{4}-\d{2}-\d{2}/.test(value.trim()))) return "date";
|
|
13810
|
+
return "text";
|
|
13811
|
+
}
|
|
13812
|
+
function datagridcolumnsof(rows) {
|
|
13813
|
+
const fields = [...new Set(rows.flatMap((row) => Object.keys(row)))];
|
|
13814
|
+
return fields.map((field) => ({ field, label: field, type: infercolumntype(rows.map((row) => row[field] ?? "")), inferred: true }));
|
|
13815
|
+
}
|
|
13816
|
+
function datagridof(input) {
|
|
13817
|
+
if (input.title.trim() === "") throw new Error("The datagrid view needs its title.");
|
|
13818
|
+
if (input.origin.trim() === "") throw new Error("The datagrid view needs its origin.");
|
|
13819
|
+
if (input.rows.length === 0) throw new Error("The datagrid view needs at least one extracted row.");
|
|
13820
|
+
const columns = datagridcolumnsof(input.rows);
|
|
13821
|
+
const rows = input.rows.map((row, index) => ({ index, values: Object.fromEntries(columns.map((column) => [column.field, row[column.field] ?? ""])) }));
|
|
13822
|
+
return { id: randomid(), title: input.title.trim(), origin: input.origin.trim(), runid: input.runid, columns, rows, at: input.at };
|
|
13823
|
+
}
|
|
13824
|
+
function exportmenudescriptors() {
|
|
13825
|
+
return ["csv", "json", "clipboard"].flatMap((format) => ["selection", "step", "run"].map((scope) => ({ format, scope, destination: format === "clipboard" ? "clipboard" : "download" })));
|
|
13826
|
+
}
|
|
13827
|
+
|
|
13828
|
+
// quickactions.ts
|
|
13829
|
+
function quickactioncatalog() {
|
|
13830
|
+
return [
|
|
13831
|
+
{ id: "extractpage", label: "Extract page data", command: "starttask", surface: "sidepanel", session: true },
|
|
13832
|
+
{ id: "captureshot", label: "Capture a shot", command: "starttask", surface: "sidepanel", permission: "downloads", session: true },
|
|
13833
|
+
{ id: "runrecent", label: "Run the recent task", command: "starttask", surface: "popup", session: true },
|
|
13834
|
+
{ id: "opendashboardpage", label: "Open the dashboard", command: "opendashboardpage", surface: "dashboardpage" }
|
|
13835
|
+
];
|
|
13836
|
+
}
|
|
13837
|
+
function quickactionsfor(catalog, input) {
|
|
13838
|
+
const capabilities = input.grantedcapabilities ?? ["activeTab", "storage", "scripting", "sidePanel"];
|
|
13839
|
+
const grantedcapabilities2 = capabilities;
|
|
13840
|
+
return catalog.filter((action) => quickactiongate({ action: { command: action.command, origin: input.origin, ...action.permission !== void 0 ? { permission: action.permission } : {}, ...action.session !== void 0 ? { session: action.session } : {} }, granted: input.granted, sessionactive: input.sessionactive, capabilities: grantedcapabilities2 }).allowed);
|
|
13841
|
+
}
|
|
13842
|
+
function shortcutdefaults() {
|
|
13843
|
+
return [
|
|
13844
|
+
{ command: "starttask", key: "Enter", modifiers: [], editable: true, surface: "popup" },
|
|
13845
|
+
{ command: "pauserun", key: "p", modifiers: ["ctrl", "shift"], editable: true, surface: "sidepanel" },
|
|
13846
|
+
{ command: "resumerun", key: "r", modifiers: ["ctrl", "shift"], editable: true, surface: "sidepanel" },
|
|
13847
|
+
{ command: "cancelrun", key: "x", modifiers: ["ctrl", "shift"], editable: true, surface: "sidepanel" },
|
|
13848
|
+
{ command: "commandpalette", key: ".", modifiers: ["ctrl"], editable: true, surface: "popup" }
|
|
13849
|
+
];
|
|
13850
|
+
}
|
|
13851
|
+
function parseshortcut(text2) {
|
|
13852
|
+
const parts = text2.trim().toLowerCase().split("+").map((part) => part.trim()).filter((part) => part !== "");
|
|
13853
|
+
if (parts.length === 0) throw new Error("The shortcut binding needs its key.");
|
|
13854
|
+
const modifiers = ["ctrl", "alt", "shift", "meta"];
|
|
13855
|
+
const key = parts.filter((part) => !modifiers.includes(part))[0];
|
|
13856
|
+
if (key === void 0 || key === "") throw new Error("The shortcut binding needs its key beside its modifiers.");
|
|
13857
|
+
return { key, modifiers: parts.filter((part) => modifiers.includes(part)) };
|
|
13858
|
+
}
|
|
13859
|
+
function shortcuttext(binding) {
|
|
13860
|
+
return [...binding.modifiers, binding.key].join("+");
|
|
13861
|
+
}
|
|
13862
|
+
function shortcutbindingafter(bindings, command, text2) {
|
|
13863
|
+
const existing = bindings.find((binding) => binding.command === command);
|
|
13864
|
+
if (existing === void 0) throw new Error(`The shortcutkeys know no ${command} command to edit.`);
|
|
13865
|
+
const parsed = parseshortcut(text2);
|
|
13866
|
+
return bindings.map((binding) => binding.command === command ? { ...binding, key: parsed.key, modifiers: parsed.modifiers } : binding);
|
|
13867
|
+
}
|
|
13868
|
+
function shortcutcommandof(bindings, input) {
|
|
13869
|
+
const pressed = [...input.modifiers].map((modifier) => modifier.toLowerCase()).sort();
|
|
13870
|
+
return bindings.find((binding) => binding.key.toLowerCase() === input.key.toLowerCase() && [...binding.modifiers].sort().join("+") === pressed.join("+") && (binding.command === "commandpalette" || binding.surface === input.surface))?.command;
|
|
13871
|
+
}
|
|
13872
|
+
function shortcutdispatchable(command, entries, input) {
|
|
13873
|
+
const entry = entries.find((candidate) => candidate.action.command === command);
|
|
13874
|
+
if (entry === void 0) return command === "commandpalette";
|
|
13875
|
+
return paletteactiongate({ action: { command: entry.action.command, ...entry.action.permission !== void 0 ? { permission: entry.action.permission } : {}, ...entry.action.session !== void 0 ? { session: entry.action.session } : {} }, granted: input.granted, sessionactive: input.sessionactive }).allowed;
|
|
13876
|
+
}
|
|
13877
|
+
function parseomniboxtask(input) {
|
|
13878
|
+
const text2 = input.text.trim();
|
|
13879
|
+
if (text2 === "") throw new Error("The omnibox task needs its natural language goal after the keyword.");
|
|
13880
|
+
if (input.origin.trim() === "") throw new Error("The omnibox task needs its active origin scope.");
|
|
13881
|
+
return { id: randomid(), text: text2, origin: input.origin.trim(), surface: "omnibox", at: input.at };
|
|
13882
|
+
}
|
|
13883
|
+
function omniboxtasktotaskinput(submission) {
|
|
13884
|
+
return { id: submission.id, text: submission.text, context: "", origin: submission.origin, surface: "omnibox", at: submission.at };
|
|
13885
|
+
}
|
|
13886
|
+
|
|
13887
|
+
// statusviews.ts
|
|
13888
|
+
function statusbadgeof(input) {
|
|
13889
|
+
if (input.planstate === void 0) return { state: "idle", waitingcount: 0 };
|
|
13890
|
+
if (input.waitingcount > 0) return { state: "attention", waitingcount: input.waitingcount, ...input.runid !== void 0 ? { runid: input.runid } : {} };
|
|
13891
|
+
if (input.planstate === "approved") return { state: "running", waitingcount: 0, ...input.runid !== void 0 ? { runid: input.runid } : {} };
|
|
13892
|
+
if (input.planstate === "pending") return { state: "waiting", waitingcount: 0, ...input.runid !== void 0 ? { runid: input.runid } : {} };
|
|
13893
|
+
return { state: "idle", waitingcount: 0, ...input.runid !== void 0 ? { runid: input.runid } : {} };
|
|
13894
|
+
}
|
|
13895
|
+
function badgetextof(state) {
|
|
13896
|
+
if (state.state === "attention") return String(state.waitingcount);
|
|
13897
|
+
if (state.state === "running") return "run";
|
|
13898
|
+
if (state.state === "waiting") return "wait";
|
|
13899
|
+
return "";
|
|
13900
|
+
}
|
|
13901
|
+
function badgecolorof(state) {
|
|
13902
|
+
if (state.state === "attention") return "#b3261e";
|
|
13903
|
+
if (state.state === "running") return "#1a73e8";
|
|
13904
|
+
if (state.state === "waiting") return "#e37400";
|
|
13905
|
+
return "#5f6368";
|
|
13906
|
+
}
|
|
13907
|
+
function notifydoneof(input) {
|
|
13908
|
+
if (input.runid.trim() === "") throw new Error("The done notification needs its run id.");
|
|
13909
|
+
return { id: randomid(), kind: "done", title: "The run completed", body: input.summary.trim() === "" ? `The run of ${input.origin} completed; the runsummary holds every step outcome.` : input.summary, deeplink: `#run-${input.runid}`, runid: input.runid, content: false, at: input.at };
|
|
13910
|
+
}
|
|
13911
|
+
function notifyattentionof(input) {
|
|
13912
|
+
if (input.stepid.trim() === "") throw new Error("The attention notification needs its waiting step.");
|
|
13913
|
+
const gate = notificationcontentgate({ content: input.content === true, consent: input.consent === true });
|
|
13914
|
+
if (!gate.allowed) throw new Error(gate.reason ?? "The attention notification refuses its page content.");
|
|
13915
|
+
return { id: randomid(), kind: "attention", title: input.cause === "gatewait" ? "The run waits for review" : input.cause === "phishguard" ? "The phishguard blocked a step" : "The run deferred a step", body: input.reason, deeplink: `#step-${input.stepid}`, runid: input.runid, stepid: input.stepid, content: input.content === true, at: input.at };
|
|
13916
|
+
}
|
|
13917
|
+
function notificationrespectsdnd(payload, dnd) {
|
|
13918
|
+
if (dnd) return { show: false, reason: `The os stays in do not disturb, so the ${payload.kind} notification holds its deep link ${payload.deeplink} in the history instead of showing.` };
|
|
13919
|
+
return { show: true, reason: `The ${payload.kind} notification shows with its deep link ${payload.deeplink}.` };
|
|
13920
|
+
}
|
|
13921
|
+
function recenttrayentryof(input) {
|
|
13922
|
+
if (input.runid.trim() === "") throw new Error("The recenttray entry needs its run id.");
|
|
13923
|
+
return { runid: input.runid, origin: input.origin, outcome: input.outcome, title: input.title, at: input.at, resumable: input.outcome === "halted", reopenable: input.outcome === "completed" || input.outcome === "failed" };
|
|
13924
|
+
}
|
|
13925
|
+
function recenttrayactions(entry) {
|
|
13926
|
+
const actions = [];
|
|
13927
|
+
if (entry.resumable) actions.push("resume");
|
|
13928
|
+
if (entry.reopenable) actions.push("reopen");
|
|
13929
|
+
return actions;
|
|
13930
|
+
}
|
|
13931
|
+
function stetoastof(input) {
|
|
13932
|
+
if (input.stepid.trim() === "") throw new Error("The stetoast needs its step.");
|
|
13933
|
+
return { id: randomid(), stepid: input.stepid, kind: input.kind, durationms: input.durationms, at: input.at };
|
|
13934
|
+
}
|
|
13935
|
+
function stetoaststackafter(toasts, toast, livecount) {
|
|
13936
|
+
const history2 = [...toasts, toast];
|
|
13937
|
+
if (livecount === void 0 || !Number.isInteger(livecount) || livecount <= 0) return { live: history2, history: history2 };
|
|
13938
|
+
return { live: history2.slice(-livecount), history: history2 };
|
|
13939
|
+
}
|
|
13940
|
+
function stetoasthistory(toasts) {
|
|
13941
|
+
return [...toasts].reverse();
|
|
13942
|
+
}
|
|
13943
|
+
|
|
13944
|
+
// pickerviews.ts
|
|
13945
|
+
function stabilityscoreof(input) {
|
|
13946
|
+
let score = 0;
|
|
13947
|
+
if (input.hasid) score += 40;
|
|
13948
|
+
if (input.hasstableattributes) score += 25;
|
|
13949
|
+
if (input.hasrole) score += 15;
|
|
13950
|
+
if (input.textunique) score += 10;
|
|
13951
|
+
if (input.selector.trim() === "") score -= 20;
|
|
13952
|
+
else if (input.selector.includes(":nth-child") || input.selector.includes(":nth-of-type")) score -= 15;
|
|
13953
|
+
return Math.max(0, Math.min(100, score));
|
|
13954
|
+
}
|
|
13955
|
+
function pickercandidateof(input) {
|
|
13956
|
+
const score = stabilityscoreof(input);
|
|
13957
|
+
const reasons = [];
|
|
13958
|
+
if (input.hasid) reasons.push("the id anchors the selector");
|
|
13959
|
+
if (input.hasstableattributes) reasons.push("stable attributes back the selector");
|
|
13960
|
+
if (input.hasrole) reasons.push("the aria role names the element");
|
|
13961
|
+
if (input.textunique) reasons.push("the text stays unique on the page");
|
|
13962
|
+
if (reasons.length === 0) reasons.push("only the positional shape anchors the selector");
|
|
13963
|
+
return { selector: input.selector, ...input.text !== void 0 && input.text !== "" ? { text: input.text } : {}, ...input.role !== void 0 && input.role !== "" ? { role: input.role } : {}, stabilityscore: score, reason: `The stability score of ${score} stands because ${reasons.join(", ")}.` };
|
|
13964
|
+
}
|
|
13965
|
+
function pickersessionstart(input) {
|
|
13966
|
+
const gate = pickeroverlaygate({ origin: input.origin, granted: input.granted });
|
|
13967
|
+
if (!gate.allowed) throw new Error(gate.reason);
|
|
13968
|
+
return { id: randomid(), origin: input.origin, candidates: rankcandidates(input.candidates), startedat: input.at };
|
|
13969
|
+
}
|
|
13970
|
+
function rankcandidates(candidates) {
|
|
13971
|
+
return [...candidates].sort((left, right) => right.stabilityscore - left.stabilityscore);
|
|
13972
|
+
}
|
|
13973
|
+
function haloof(input) {
|
|
13974
|
+
if (input.selector.trim() === "") throw new Error("The targethalo needs its target selector.");
|
|
13975
|
+
return { stepid: input.stepid, selector: input.selector, rect: input.rect, state: input.state };
|
|
13976
|
+
}
|
|
13977
|
+
function halocolorof(state) {
|
|
13978
|
+
if (state === "running") return "#1a73e8";
|
|
13979
|
+
if (state === "waiting") return "#e37400";
|
|
13980
|
+
if (state === "done") return "#188038";
|
|
13981
|
+
if (state === "failed") return "#b3261e";
|
|
13982
|
+
if (state === "halted") return "#3c4043";
|
|
13983
|
+
return "#5f6368";
|
|
13984
|
+
}
|
|
13985
|
+
function guidedtips() {
|
|
13986
|
+
return [
|
|
13987
|
+
{ id: "selectorstability", surface: "sidepanel", title: "Selector stability", body: "Devthink scores every candidate selector by its stability: an id anchor, stable attributes, an aria role and a unique text each lift the score while a positional shape lowers it, so the proposed step binds to the selector least likely to break.", pickerstep: "candidatepick" },
|
|
13988
|
+
{ id: "candidatelock", surface: "sidepanel", title: "Locking a candidate", body: "Lock one candidate to bind it to the proposed step; one picker session locks one candidate and the locked selector rides the step for its review.", pickerstep: "candidatelock" },
|
|
13989
|
+
{ id: "haloreadout", surface: "sidepanel", title: "The halo read out", body: "During a run the targethalo outlines the active target element and its color follows the step state: gray while pending, blue while running, amber at a gate, green when done, red on failure and dark when halted.", pickerstep: "halotracking" }
|
|
13990
|
+
];
|
|
13991
|
+
}
|
|
13992
|
+
function guidedtipdismiss(tips, dismissed, tipid) {
|
|
13993
|
+
const tip = tips.find((candidate) => candidate.id === tipid);
|
|
13994
|
+
if (tip === void 0) throw new Error(`The guidedtips know no ${tipid} tip.`);
|
|
13995
|
+
return [.../* @__PURE__ */ new Set([...dismissed, tipid])];
|
|
13996
|
+
}
|
|
13997
|
+
function guidedtiprecall(dismissed) {
|
|
13998
|
+
return [];
|
|
13999
|
+
}
|
|
14000
|
+
function pagechipof(input) {
|
|
14001
|
+
if (input.stepid.trim() === "") throw new Error("The pagechip needs its step.");
|
|
14002
|
+
if (input.selector.trim() === "") throw new Error("The pagechip needs its anchor selector.");
|
|
14003
|
+
return { id: randomid(), stepid: input.stepid, selector: input.selector, origin: input.origin, at: input.at };
|
|
14004
|
+
}
|
|
14005
|
+
function pagechipresolve(chip, resolution, surface, at) {
|
|
14006
|
+
if (surface === "background") throw new Error("The pagechip resolution needs its distinct human action from a surface; the background never resolves a review on its own.");
|
|
14007
|
+
const resolved = { ...chip, resolution, resolvedat: at };
|
|
14008
|
+
return {
|
|
14009
|
+
chip: resolved,
|
|
14010
|
+
logevent: { kind: "review", stepid: chip.stepid, summary: `The user ${resolution === "approve" ? "approved" : "rejected"} the step ${chip.stepid} of ${chip.origin} from the pagechip anchored to ${chip.selector} on the ${surface}; one distinct human action resolved the step alone.` }
|
|
14011
|
+
};
|
|
14012
|
+
}
|
|
14013
|
+
|
|
14014
|
+
// evidenceviews.ts
|
|
14015
|
+
function shotpanelof(input) {
|
|
14016
|
+
const gate = shotpanelgate({ captureorigin: input.origin, granted: input.granted });
|
|
14017
|
+
if (!gate.allowed) throw new Error(gate.reason);
|
|
14018
|
+
if (input.stepid.trim() === "") throw new Error("The shotpanel view needs its step.");
|
|
14019
|
+
return { id: randomid(), stepid: input.stepid, runid: input.runid, captureid: input.captureid, provenance: input.provenance, origin: input.origin, redactions: input.redactions ?? [], zoom: 1, pan: { x: 0, y: 0 }, at: input.at };
|
|
14020
|
+
}
|
|
14021
|
+
function comparepairof(input) {
|
|
14022
|
+
if (input.stepid.trim() === "") throw new Error("The compareviewer pair needs its step.");
|
|
14023
|
+
if (input.beforecaptureid === input.aftercaptureid) throw new Error("The compareviewer pair needs its distinct before and after captures.");
|
|
14024
|
+
return { id: randomid(), stepid: input.stepid, beforecaptureid: input.beforecaptureid, aftercaptureid: input.aftercaptureid, slidervalue: 50 };
|
|
14025
|
+
}
|
|
14026
|
+
|
|
14027
|
+
// siteprefs.ts
|
|
14028
|
+
function siteprofileof(input) {
|
|
14029
|
+
const gate = siteprofilegate({ origin: input.origin });
|
|
14030
|
+
if (!gate.allowed) throw new Error(gate.reason);
|
|
14031
|
+
return { origin: input.origin, ...input.theme !== void 0 ? { theme: input.theme } : {}, ...input.shortcuts !== void 0 ? { shortcuts: input.shortcuts } : {}, ...input.defaultview !== void 0 ? { defaultview: input.defaultview } : {}, updatedat: input.at };
|
|
14032
|
+
}
|
|
14033
|
+
function siteprofileactive(profile, origin) {
|
|
14034
|
+
return profile.origin === origin;
|
|
14035
|
+
}
|
|
14036
|
+
function siteprofilefor(profiles, origin) {
|
|
14037
|
+
return profiles.find((profile) => siteprofileactive(profile, origin));
|
|
14038
|
+
}
|
|
14039
|
+
function darklighttokensof(mode) {
|
|
14040
|
+
const tokens = mode === "dark" ? { surface: "#1f1f1f", elevated: "#2b2b2b", text: "#e3e3e3", muted: "#9aa0a6", accent: "#8ab4f8", border: "#3c4043", focus: "#aecbfa", error: "#f28b82", success: "#81c995", warning: "#fdd663" } : { surface: "#ffffff", elevated: "#f8f9fa", text: "#202124", muted: "#5f6368", accent: "#1a73e8", border: "#dadce0", focus: "#174ea6", error: "#b3261e", success: "#188038", warning: "#e37400" };
|
|
14041
|
+
return { mode, tokens };
|
|
14042
|
+
}
|
|
14043
|
+
function resolveappearance(input) {
|
|
14044
|
+
if (input.siteprofile?.theme !== void 0 && input.siteprofile.theme !== "system") return { ...darklighttokensof(input.siteprofile.theme), source: "site" };
|
|
14045
|
+
if (input.useroverride !== void 0 && input.useroverride !== "system") return { ...darklighttokensof(input.useroverride), source: "user" };
|
|
14046
|
+
return { ...darklighttokensof(input.ospreference), source: "os" };
|
|
14047
|
+
}
|
|
14048
|
+
function localebundles() {
|
|
14049
|
+
return [
|
|
14050
|
+
{
|
|
14051
|
+
language: "en",
|
|
14052
|
+
strings: {
|
|
14053
|
+
"popup.title": "Devthink",
|
|
14054
|
+
"popup.taskinput.placeholder": "Describe the goal for the active tab",
|
|
14055
|
+
"popup.taskinput.submit": "Propose the plan",
|
|
14056
|
+
"popup.palette.open": "Open the commandpalette",
|
|
14057
|
+
"popup.recent.title": "Recent runs",
|
|
14058
|
+
"popup.recent.resume": "Resume",
|
|
14059
|
+
"popup.recent.reopen": "Reopen",
|
|
14060
|
+
"sidepanel.tab.plan": "Plan",
|
|
14061
|
+
"sidepanel.tab.run": "Run",
|
|
14062
|
+
"sidepanel.tab.review": "Review",
|
|
14063
|
+
"sidepanel.data.export": "Export",
|
|
14064
|
+
"dashboard.title": "Dashboard",
|
|
14065
|
+
"options.title": "Options",
|
|
14066
|
+
"options.theme.label": "Theme",
|
|
14067
|
+
"options.theme.dark": "Dark",
|
|
14068
|
+
"options.theme.light": "Light",
|
|
14069
|
+
"options.theme.system": "Follow the system",
|
|
14070
|
+
"options.locale.label": "Language",
|
|
14071
|
+
"options.shortcuts.label": "Shortcutkeys",
|
|
14072
|
+
"options.notifications.label": "Notifications",
|
|
14073
|
+
"options.importexport.label": "Import and export",
|
|
14074
|
+
"options.tour.label": "Feature tour",
|
|
14075
|
+
"stepapprove.approve": "Approve",
|
|
14076
|
+
"stepapprove.reject": "Reject",
|
|
14077
|
+
"stepapprove.edit": "Edit",
|
|
14078
|
+
"pagechip.approve": "Approve",
|
|
14079
|
+
"pagechip.reject": "Reject",
|
|
14080
|
+
"grid.empty": "No extracted rows yet",
|
|
14081
|
+
"toast.stepdone": "Step completed"
|
|
14082
|
+
}
|
|
14083
|
+
},
|
|
14084
|
+
{
|
|
14085
|
+
language: "pt",
|
|
14086
|
+
strings: {
|
|
14087
|
+
"popup.title": "Devthink",
|
|
14088
|
+
"popup.taskinput.placeholder": "Descreva o objetivo para a aba ativa",
|
|
14089
|
+
"popup.taskinput.submit": "Propor o plano",
|
|
14090
|
+
"popup.palette.open": "Abrir a paleta de comandos",
|
|
14091
|
+
"popup.recent.title": "Execu\xE7\xF5es recentes",
|
|
14092
|
+
"popup.recent.resume": "Retomar",
|
|
14093
|
+
"popup.recent.reopen": "Reabrir",
|
|
14094
|
+
"sidepanel.tab.plan": "Plano",
|
|
14095
|
+
"sidepanel.tab.run": "Execu\xE7\xE3o",
|
|
14096
|
+
"sidepanel.tab.review": "Revis\xE3o",
|
|
14097
|
+
"sidepanel.data.export": "Exportar",
|
|
14098
|
+
"dashboard.title": "Painel",
|
|
14099
|
+
"options.title": "Op\xE7\xF5es",
|
|
14100
|
+
"options.theme.label": "Tema",
|
|
14101
|
+
"options.theme.dark": "Escuro",
|
|
14102
|
+
"options.theme.light": "Claro",
|
|
14103
|
+
"options.theme.system": "Seguir o sistema",
|
|
14104
|
+
"options.locale.label": "Idioma",
|
|
14105
|
+
"options.shortcuts.label": "Atalhos",
|
|
14106
|
+
"options.notifications.label": "Notifica\xE7\xF5es",
|
|
14107
|
+
"options.importexport.label": "Importar e exportar",
|
|
14108
|
+
"options.tour.label": "Tour de recursos",
|
|
14109
|
+
"stepapprove.approve": "Aprovar",
|
|
14110
|
+
"stepapprove.reject": "Rejeitar",
|
|
14111
|
+
"stepapprove.edit": "Editar",
|
|
14112
|
+
"pagechip.approve": "Aprovar",
|
|
14113
|
+
"pagechip.reject": "Rejeitar",
|
|
14114
|
+
"grid.empty": "Nenhuma linha extra\xEDda ainda",
|
|
14115
|
+
"toast.stepdone": "Etapa conclu\xEDda"
|
|
14116
|
+
}
|
|
14117
|
+
}
|
|
14118
|
+
];
|
|
14119
|
+
}
|
|
14120
|
+
function localestring(bundles, language, key) {
|
|
14121
|
+
const requested = bundles.find((bundle) => bundle.language === language);
|
|
14122
|
+
const english = bundles.find((bundle) => bundle.language === "en");
|
|
14123
|
+
return requested?.strings[key] ?? english?.strings[key] ?? key;
|
|
14124
|
+
}
|
|
14125
|
+
function supportedlanguages(bundles) {
|
|
14126
|
+
return bundles.map((bundle) => bundle.language);
|
|
14127
|
+
}
|
|
14128
|
+
function localeformat(input) {
|
|
14129
|
+
if (input.kind === "date") {
|
|
14130
|
+
const date = new Date(input.value);
|
|
14131
|
+
const year = date.getUTCFullYear();
|
|
14132
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
14133
|
+
const day = String(date.getUTCDate()).padStart(2, "0");
|
|
14134
|
+
const hours = String(date.getUTCHours()).padStart(2, "0");
|
|
14135
|
+
const minutes = String(date.getUTCMinutes()).padStart(2, "0");
|
|
14136
|
+
return input.language === "pt" ? `${day}/${month}/${year} ${hours}:${minutes}` : `${year}-${month}-${day} ${hours}:${minutes}`;
|
|
14137
|
+
}
|
|
14138
|
+
if (input.kind === "duration") {
|
|
14139
|
+
const seconds = Math.round(input.value / 1e3);
|
|
14140
|
+
const minutes = Math.floor(seconds / 60);
|
|
14141
|
+
const rest = seconds % 60;
|
|
14142
|
+
return input.language === "pt" ? `${minutes} min ${rest} s` : `${minutes}m ${rest}s`;
|
|
14143
|
+
}
|
|
14144
|
+
const text2 = String(input.value);
|
|
14145
|
+
const parts = text2.split(".");
|
|
14146
|
+
const whole = parts[0] ?? "0";
|
|
14147
|
+
const fraction = parts[1];
|
|
14148
|
+
const grouped = whole.replace(/\B(?=(\d{3})+(?!\d))/g, input.language === "pt" ? "." : ",");
|
|
14149
|
+
return fraction !== void 0 ? `${grouped}${input.language === "pt" ? "," : "."}${fraction}` : grouped;
|
|
14150
|
+
}
|
|
14151
|
+
|
|
14152
|
+
// portability.ts
|
|
14153
|
+
function importexportpayloadof(input) {
|
|
14154
|
+
if (input.profile.trim() === "") throw new Error("The importexport payload needs its profile name.");
|
|
14155
|
+
const secrets = [...input.originprofiles, ...input.siteprofiles, ...input.notes, ...Object.values(input.preferences)].find((record2) => secretcarrying(record2)) !== void 0;
|
|
14156
|
+
const gate = importexportgate({ containssecrets: secrets, unmaskedlogs: false });
|
|
14157
|
+
if (!gate.allowed) throw new Error(gate.reason);
|
|
14158
|
+
return { version: 1, kind: "settings", profile: input.profile.trim(), exportedat: input.at, contents: { originprofiles: input.originprofiles, siteprofiles: input.siteprofiles, notes: input.notes, preferences: input.preferences }, exclusions: ["secretvault values", "unmasked logs"] };
|
|
14159
|
+
}
|
|
14160
|
+
function secretcarrying(record2) {
|
|
14161
|
+
if (record2 === null || typeof record2 !== "object") return false;
|
|
14162
|
+
const entries = Object.entries(record2);
|
|
14163
|
+
const secretkeys = ["secret", "token", "password", "apikey", "authorization"];
|
|
14164
|
+
return entries.some(([key, value]) => secretkeys.some((shape) => key.toLowerCase().includes(shape)) && typeof value === "string" && value.trim() !== "");
|
|
14165
|
+
}
|
|
14166
|
+
function importexportvalidate(payload) {
|
|
14167
|
+
const records = [...payload.contents.originprofiles, ...payload.contents.siteprofiles, ...payload.contents.notes, ...Object.values(payload.contents.preferences)];
|
|
14168
|
+
const preferencessecrets = Object.entries(payload.contents.preferences).some(([key, value]) => secretcarrying({ [key]: value }));
|
|
14169
|
+
const gate = importexportgate({ containssecrets: records.some((record2) => secretcarrying(record2)) || preferencessecrets, unmaskedlogs: payload.contents.unmaskedlogs !== void 0 });
|
|
14170
|
+
if (!gate.allowed) return { ok: false, reason: gate.reason ?? "The importexport bundle refuses." };
|
|
14171
|
+
if (payload.profile.trim() === "") return { ok: false, reason: "The importexport bundle needs its profile name." };
|
|
14172
|
+
return { ok: true, reason: `The importexport bundle of the profile ${payload.profile} validates with ${payload.contents.originprofiles.length} origin profile${payload.contents.originprofiles.length === 1 ? "" : "s"}, ${payload.contents.siteprofiles.length} site profile${payload.contents.siteprofiles.length === 1 ? "" : "s"} and ${payload.contents.notes.length} note${payload.contents.notes.length === 1 ? "" : "s"}; ${payload.exclusions.join(" and ")} never enter any bundle.` };
|
|
14173
|
+
}
|
|
14174
|
+
function applyimport(payload, current) {
|
|
14175
|
+
const validation = importexportvalidate(payload);
|
|
14176
|
+
if (!validation.ok) throw new Error(validation.reason);
|
|
14177
|
+
const applied = Object.keys(payload.contents.preferences);
|
|
14178
|
+
return { preferences: { ...current, ...payload.contents.preferences }, applied };
|
|
14179
|
+
}
|
|
14180
|
+
function detectfilekind(filename, head) {
|
|
14181
|
+
const extension = filename.toLowerCase().split(".").pop() ?? "";
|
|
14182
|
+
if (extension === "csv") return "csv";
|
|
14183
|
+
if (extension === "json") {
|
|
14184
|
+
const trimmed = head.trim();
|
|
14185
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) return trimmed.includes('"steps"') ? "workflow" : "json";
|
|
14186
|
+
return "json";
|
|
14187
|
+
}
|
|
14188
|
+
if (extension === "yaml" || extension === "yml") return "workflow";
|
|
14189
|
+
return void 0;
|
|
14190
|
+
}
|
|
14191
|
+
function dropimportof(input) {
|
|
14192
|
+
if (input.filename.trim() === "") throw new Error("The dropimport session needs its filename.");
|
|
14193
|
+
const kind = detectfilekind(input.filename, input.head);
|
|
14194
|
+
if (kind === void 0) throw new Error(`The dropimport detects no csv, json or workflow kind in ${input.filename}; the import path refuses the file.`);
|
|
14195
|
+
return { id: `${input.filename}:${input.at}`, filename: input.filename, kind, bytes: input.bytes, accepted: true, at: input.at };
|
|
14196
|
+
}
|
|
14197
|
+
|
|
14198
|
+
// tourviews.ts
|
|
14199
|
+
function featuretourstops() {
|
|
14200
|
+
return [
|
|
14201
|
+
{ id: "origingrants", surface: "popup", focus: "#allowlist", title: "Origin grants", body: "Devthink denies automation by default; grant one exact origin at a time and every run stays inside the granted origins.", order: 1 },
|
|
14202
|
+
{ id: "planreview", surface: "sidepanel", focus: "#plancards", 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.", order: 2 },
|
|
14203
|
+
{ id: "runcontrol", surface: "sidepanel", focus: "#timeline", title: "Run control", body: "Runs start, pause, resume and cancel under your hand while the stepstimeline follows every transition.", order: 3 },
|
|
14204
|
+
{ id: "logaudit", surface: "dashboardpage", focus: "#sessiongrid", title: "Log audit", body: "The immutable log chains every step transition with masked values; verify the chain and copy a verified range as an audit excerpt.", order: 4 },
|
|
14205
|
+
{ id: "datagrid", surface: "sidepanel", focus: "#datagrid", title: "The datagrid", body: "Extraction results render as a grid with inferred column types; sort and filter locally, select a row range and export csv, json or clipboard with masked values only.", order: 5 },
|
|
14206
|
+
{ id: "compareviewer", surface: "sidepanel", focus: "#compareviewer", title: "The compareviewer", body: "Every executed write step pairs its before and after captures; the slider overlays the two so you see exactly what the step changed.", order: 6 },
|
|
14207
|
+
{ id: "pickeroverlay", surface: "sidepanel", focus: "#picker", title: "The pickeroverlay", body: "Start a picker session to list the element candidates of the granted origin with stability scored selectors; lock one candidate for the proposed step.", order: 7 }
|
|
14208
|
+
];
|
|
14209
|
+
}
|
|
14210
|
+
function featuretourordered(stops) {
|
|
14211
|
+
return [...stops].sort((left, right) => left.order - right.order);
|
|
14212
|
+
}
|
|
14213
|
+
function a11ylabelof(input) {
|
|
14214
|
+
if (input.control.trim() === "") throw new Error("The a11ylabel needs its control.");
|
|
14215
|
+
if (input.name.trim() === "") throw new Error("The a11ylabel needs its accessible name.");
|
|
14216
|
+
return { control: input.control, role: input.role, name: input.name, ...input.state !== void 0 ? { state: input.state } : {}, ...input.value !== void 0 ? { value: input.value } : {} };
|
|
14217
|
+
}
|
|
14218
|
+
function a11ylabelsfor(surface) {
|
|
14219
|
+
const labels = {
|
|
14220
|
+
popup: [
|
|
14221
|
+
a11ylabelof({ control: "taskinput", role: "textbox", name: "popup.taskinput.placeholder", state: "idle" }),
|
|
14222
|
+
a11ylabelof({ control: "submit", role: "button", name: "popup.taskinput.submit" }),
|
|
14223
|
+
a11ylabelof({ control: "palette", role: "button", name: "popup.palette.open" }),
|
|
14224
|
+
a11ylabelof({ control: "recenttray", role: "list", name: "popup.recent.title", value: "0 runs" })
|
|
14225
|
+
],
|
|
14226
|
+
sidepanel: [
|
|
14227
|
+
a11ylabelof({ control: "plantab", role: "tab", name: "sidepanel.tab.plan", state: "selected" }),
|
|
14228
|
+
a11ylabelof({ control: "runtab", role: "tab", name: "sidepanel.tab.run", state: "unselected" }),
|
|
14229
|
+
a11ylabelof({ control: "reviewtab", role: "tab", name: "sidepanel.tab.review", state: "unselected" }),
|
|
14230
|
+
a11ylabelof({ control: "datagrid", role: "table", name: "grid.empty" }),
|
|
14231
|
+
a11ylabelof({ control: "compareviewer", role: "slider", name: "sidepanel.data.compare", value: "50" }),
|
|
14232
|
+
a11ylabelof({ control: "picker", role: "button", name: "sidepanel.data.picker" })
|
|
14233
|
+
],
|
|
14234
|
+
dashboardpage: [
|
|
14235
|
+
a11ylabelof({ control: "sessiongrid", role: "table", name: "dashboard.title", value: "0 runs" }),
|
|
14236
|
+
a11ylabelof({ control: "historysearch", role: "search", name: "dashboard.history" }),
|
|
14237
|
+
a11ylabelof({ control: "dropzone", role: "region", name: "options.importexport.label" })
|
|
14238
|
+
],
|
|
14239
|
+
optionspage: [
|
|
14240
|
+
a11ylabelof({ control: "theme", role: "radiogroup", name: "options.theme.label", value: "system" }),
|
|
14241
|
+
a11ylabelof({ control: "locale", role: "combobox", name: "options.locale.label", value: "en" }),
|
|
14242
|
+
a11ylabelof({ control: "shortcuts", role: "group", name: "options.shortcuts.label" }),
|
|
14243
|
+
a11ylabelof({ control: "notifications", role: "switch", name: "options.notifications.label", state: "off" }),
|
|
14244
|
+
a11ylabelof({ control: "importexport", role: "region", name: "options.importexport.label" }),
|
|
14245
|
+
a11ylabelof({ control: "tour", role: "button", name: "options.tour.label" })
|
|
14246
|
+
],
|
|
14247
|
+
onboarding: [
|
|
14248
|
+
a11ylabelof({ control: "onboarding", role: "dialog", name: "options.tour.label", state: "open" })
|
|
14249
|
+
],
|
|
14250
|
+
omnibox: [
|
|
14251
|
+
a11ylabelof({ control: "omnibox", role: "textbox", name: "popup.taskinput.placeholder" })
|
|
14252
|
+
],
|
|
14253
|
+
page: [
|
|
14254
|
+
a11ylabelof({ control: "pagechip", role: "group", name: "pagechip.approve", state: "pending" })
|
|
14255
|
+
]
|
|
14256
|
+
};
|
|
14257
|
+
return labels[surface];
|
|
14258
|
+
}
|
|
14259
|
+
function a11ylabellocalized(label, bundles, language) {
|
|
14260
|
+
return { ...label, name: localestring(bundles, language, label.name) };
|
|
14261
|
+
}
|
|
14262
|
+
function a11ylabelslocalizedfor(surface, bundles, language) {
|
|
14263
|
+
return a11ylabelsfor(surface).map((label) => a11ylabellocalized(label, bundles, language));
|
|
14264
|
+
}
|
|
14265
|
+
|
|
13541
14266
|
// llm.ts
|
|
13542
14267
|
var defaultrefusalmarkers = ["i cannot", "i can't", "i'm unable", "refusal:", "cannot comply"];
|
|
13543
14268
|
function buildrequest(input) {
|
|
@@ -14510,6 +15235,385 @@ function connectallowlist(entries) {
|
|
|
14510
15235
|
return entries.map((entry) => ({ senderid: entry.senderid, displayname: entry.displayname, ...entry.origin !== void 0 ? { origin: entry.origin } : {}, addedat: entry.addedat }));
|
|
14511
15236
|
}
|
|
14512
15237
|
|
|
15238
|
+
// flowlibrary.ts
|
|
15239
|
+
async function sha2563(payload) {
|
|
15240
|
+
const bytes = new TextEncoder().encode(payload);
|
|
15241
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
15242
|
+
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
15243
|
+
}
|
|
15244
|
+
function manifestbody(manifest) {
|
|
15245
|
+
return JSON.stringify({ id: manifest.id, title: manifest.title, description: manifest.description, version: manifest.version, publisher: manifest.publisher, ...manifest.registry !== void 0 ? { registry: manifest.registry } : {}, steps: manifest.steps, kinds: manifest.kinds, requiredgrants: manifest.requiredgrants, dataexpectations: manifest.dataexpectations, sensitive: manifest.sensitive });
|
|
15246
|
+
}
|
|
15247
|
+
async function manifestdigest(manifest) {
|
|
15248
|
+
const { publish, ...body } = manifest;
|
|
15249
|
+
void publish;
|
|
15250
|
+
return sha2563(manifestbody(body));
|
|
15251
|
+
}
|
|
15252
|
+
async function validatemanifest(input) {
|
|
15253
|
+
const errors = [];
|
|
15254
|
+
const raw = input.manifest;
|
|
15255
|
+
if (!Boolean(raw) || typeof raw !== "object" || Array.isArray(raw)) return { ok: false, errors: [{ path: "manifest", expected: "object", found: Array.isArray(raw) ? "array" : typeof raw, reason: "Every flowlibrary manifest travels as one plain object." }], reason: "The flowlibrary manifest is no plain object; schemastrict refuses the carrier before any import." };
|
|
15256
|
+
const candidate = raw;
|
|
15257
|
+
if (typeof candidate.id !== "string" || candidate.id.trim() === "") errors.push({ path: "id", expected: "string", found: typeof candidate.id, reason: "The flowlibrary manifest needs its id." });
|
|
15258
|
+
if (typeof candidate.title !== "string" || candidate.title.trim() === "") errors.push({ path: "title", expected: "string", found: typeof candidate.title, reason: "The flowlibrary manifest needs its title." });
|
|
15259
|
+
if (typeof candidate.description !== "string") errors.push({ path: "description", expected: "string", found: typeof candidate.description, reason: "The flowlibrary manifest needs its description." });
|
|
15260
|
+
if (typeof candidate.version !== "string" || candidate.version.trim() === "") errors.push({ path: "version", expected: "string", found: typeof candidate.version, reason: "The flowlibrary manifest needs its version." });
|
|
15261
|
+
if (typeof candidate.publisher !== "string" || candidate.publisher.trim() === "") errors.push({ path: "publisher", expected: "string", found: typeof candidate.publisher, reason: "The flowlibrary manifest names its publisher." });
|
|
15262
|
+
if (!Array.isArray(candidate.steps) || candidate.steps.length === 0) errors.push({ path: "steps", expected: "array", found: Array.isArray(candidate.steps) ? "empty array" : typeof candidate.steps, reason: "The flowlibrary manifest declares its steps." });
|
|
15263
|
+
if (Array.isArray(candidate.steps)) {
|
|
15264
|
+
candidate.steps.forEach((step, index) => {
|
|
15265
|
+
const shape = step;
|
|
15266
|
+
if (!Boolean(shape) || typeof shape !== "object" || typeof shape.id !== "string" || shape.id.trim() === "" || typeof shape.kind !== "string" || shape.kind.trim() === "" || typeof shape.label !== "string") errors.push({ path: `steps.${index}`, expected: "flowlibrarystep", found: typeof step, reason: "Every flowlibrary step needs its id, kind and label." });
|
|
15267
|
+
});
|
|
15268
|
+
}
|
|
15269
|
+
if (!Array.isArray(candidate.kinds) || candidate.kinds.length === 0) errors.push({ path: "kinds", expected: "array", found: typeof candidate.kinds, reason: "The flowlibrary manifest declares the action kinds its steps use." });
|
|
15270
|
+
if (!Array.isArray(candidate.requiredgrants)) errors.push({ path: "requiredgrants", expected: "array", found: typeof candidate.requiredgrants, reason: "The flowlibrary manifest declares its required origin grants." });
|
|
15271
|
+
if (!Array.isArray(candidate.dataexpectations)) errors.push({ path: "dataexpectations", expected: "array", found: typeof candidate.dataexpectations, reason: "The flowlibrary manifest declares its data expectations with minimization hints." });
|
|
15272
|
+
if (typeof candidate.sensitive !== "boolean") errors.push({ path: "sensitive", expected: "boolean", found: typeof candidate.sensitive, reason: "The flowlibrary manifest marks whether it is sensitive." });
|
|
15273
|
+
const unknownfields = Object.keys(candidate).filter((key) => !["id", "title", "description", "version", "publisher", "registry", "steps", "kinds", "requiredgrants", "dataexpectations", "sensitive", "publish"].includes(key));
|
|
15274
|
+
for (const field of unknownfields) errors.push({ path: field, expected: "absent", found: "present", reason: `The flowlibrary manifest carries the unknown field ${field}; schemastrict refuses unknown fields.` });
|
|
15275
|
+
const schemagate = librarymanifestgate({ errors });
|
|
15276
|
+
if (!schemagate.allowed) return { ok: false, errors, reason: schemagate.reason ?? "The flowlibrary manifest fails schemastrict." };
|
|
15277
|
+
const manifest = { id: candidate.id, title: candidate.title, description: candidate.description, version: candidate.version, publisher: candidate.publisher, ...typeof candidate.registry === "string" && candidate.registry.trim() !== "" ? { registry: candidate.registry } : {}, steps: candidate.steps.map((step) => ({ id: step.id, kind: step.kind, label: step.label, ...step.target !== void 0 ? { target: step.target } : {}, ...step.value !== void 0 ? { value: step.value } : {}, ...step.namespace !== void 0 ? { namespace: step.namespace } : {} })), kinds: candidate.kinds, requiredgrants: candidate.requiredgrants, dataexpectations: candidate.dataexpectations, sensitive: candidate.sensitive, ...isPublish(candidate.publish) ? { publish: candidate.publish } : {} };
|
|
15278
|
+
const kinds = [...new Set(manifest.steps.map((step) => step.kind))];
|
|
15279
|
+
const capabilitygate = librarycapabilitygate({ kinds, capabilities: input.capabilities });
|
|
15280
|
+
if (!capabilitygate.allowed) return { ok: false, errors: [], reason: capabilitygate.reason ?? "", manifest };
|
|
15281
|
+
return { ok: true, errors: [], reason: `The manifest ${manifest.id} of ${manifest.publisher} validates under schemastrict with ${manifest.steps.length} step${manifest.steps.length === 1 ? "" : "s"} and ${kinds.length} kind${kinds.length === 1 ? "" : "s"} inside the installed capability set.`, manifest };
|
|
15282
|
+
}
|
|
15283
|
+
function isPublish(value) {
|
|
15284
|
+
if (!Boolean(value) || typeof value !== "object") return false;
|
|
15285
|
+
const shape = value;
|
|
15286
|
+
return typeof shape.publisher === "string" && shape.publisher.trim() !== "" && typeof shape.signature === "string" && shape.signature.trim() !== "" && typeof shape.digest === "string" && shape.digest.trim() !== "" && typeof shape.provenance === "string" && typeof shape.publishedat === "number";
|
|
15287
|
+
}
|
|
15288
|
+
async function verifypublishersignature(manifest) {
|
|
15289
|
+
if (manifest.publish === void 0) return { verified: false, reason: `The manifest ${manifest.id} of ${manifest.publisher} carries no publisher signature; the entry quarantines until the user verifies its publisher.` };
|
|
15290
|
+
const digest = await manifestdigest(manifest);
|
|
15291
|
+
if (manifest.publish.digest !== digest) return { verified: false, reason: `The publisher signature of ${manifest.publish.publisher} covers the digest ${manifest.publish.digest} while the manifest body hashes to ${digest}; the verification refuses the signature in full.` };
|
|
15292
|
+
if (manifest.publish.publisher !== manifest.publisher) return { verified: false, reason: `The publisher signature names ${manifest.publish.publisher} while the manifest carries the publisher ${manifest.publisher}; the verification refuses the signature in full.` };
|
|
15293
|
+
const seal = await sha2563(`${manifest.publish.publisher}
|
|
15294
|
+
${manifest.publish.digest}
|
|
15295
|
+
${manifest.publish.provenance}`);
|
|
15296
|
+
if (manifest.publish.signature !== seal) return { verified: false, reason: `The publisher signature of ${manifest.publish.publisher} seals neither the manifest digest nor its provenance; the verification refuses the signature in full.` };
|
|
15297
|
+
return { verified: true, reason: `The publisher signature of ${manifest.publish.publisher} verifies over the manifest digest ${digest.slice(0, 12)}\u2026 with its provenance ${manifest.publish.provenance}.` };
|
|
15298
|
+
}
|
|
15299
|
+
async function libraryentryof(input) {
|
|
15300
|
+
const digest = await manifestdigest(input.manifest);
|
|
15301
|
+
const verification = await verifypublishersignature(input.manifest);
|
|
15302
|
+
const quarantinegate = libraryquarantinegate({ verified: verification.verified, signaturepresent: input.manifest.publish !== void 0, signaturevalid: verification.verified });
|
|
15303
|
+
const state = quarantinegate.allowed ? "available" : "quarantined";
|
|
15304
|
+
return { id: `${input.manifest.id}@${input.manifest.version}`, manifest: input.manifest, digest, state, provenance: `${input.provenance}; ${verification.reason}`, addedat: input.now };
|
|
15305
|
+
}
|
|
15306
|
+
function grantdiffof(input) {
|
|
15307
|
+
const required = [...new Set(input.manifest.requiredgrants)];
|
|
15308
|
+
const added = required.filter((origin) => !input.heldgrants.includes(origin));
|
|
15309
|
+
const kept = required.filter((origin) => input.heldgrants.includes(origin));
|
|
15310
|
+
const originmappings = required.map((origin) => ({ origin, kinds: [...new Set(input.manifest.steps.map((step) => step.kind))] }));
|
|
15311
|
+
return { added, kept, originmappings };
|
|
15312
|
+
}
|
|
15313
|
+
function sensitiveconsentfor(manifest, freshconsent) {
|
|
15314
|
+
const gate = librarysensitivegate({ sensitive: manifest.sensitive, freshconsent });
|
|
15315
|
+
return { required: manifest.sensitive, reason: gate.reason ?? "" };
|
|
15316
|
+
}
|
|
15317
|
+
function manifestrisk(manifest) {
|
|
15318
|
+
let risk = "read";
|
|
15319
|
+
for (const step of manifest.steps) {
|
|
15320
|
+
const candidate = actionrisk(step.kind);
|
|
15321
|
+
if (candidate === "sensitive") return "sensitive";
|
|
15322
|
+
if (candidate === "interaction") risk = "interaction";
|
|
15323
|
+
}
|
|
15324
|
+
return risk;
|
|
15325
|
+
}
|
|
15326
|
+
function installlibrary(input) {
|
|
15327
|
+
const manifest = input.entry.manifest;
|
|
15328
|
+
const prefix = input.selectornamespace?.trim() ?? "";
|
|
15329
|
+
const steps = manifest.steps.map((step) => ({
|
|
15330
|
+
id: `${manifest.id}-${step.id}`,
|
|
15331
|
+
kind: step.kind,
|
|
15332
|
+
label: step.label,
|
|
15333
|
+
...step.target !== void 0 ? { target: prefix === "" ? step.target : `${prefix} ${step.target}`.trim() } : {},
|
|
15334
|
+
...step.value !== void 0 ? { value: step.value } : {}
|
|
15335
|
+
}));
|
|
15336
|
+
return { id: `library:${manifest.id}:${manifest.version}`, name: manifest.title, version: 1, origins: [...new Set(manifest.requiredgrants)], steps, blocks: [], risk: manifestrisk(manifest), reviewstate: "pending", createdat: input.now };
|
|
15337
|
+
}
|
|
15338
|
+
function updatelibrary(input) {
|
|
15339
|
+
if (input.incoming.digest === input.existing.digest) return { changedsteps: [], addedgrants: [], versionfrom: input.existing.manifest.version, versionto: input.incoming.manifest.version, replace: false, reason: `The incoming ${input.incoming.manifest.id} carries the same manifest digest as the installed entry; the update replaces nothing.` };
|
|
15340
|
+
const existingsteps = new Set(input.existing.manifest.steps.map((step) => step.id));
|
|
15341
|
+
const incomingsteps = new Set(input.incoming.manifest.steps.map((step) => step.id));
|
|
15342
|
+
const changedsteps = [.../* @__PURE__ */ new Set([...existingsteps, ...incomingsteps])].filter((id) => {
|
|
15343
|
+
const before = input.existing.manifest.steps.find((step) => step.id === id);
|
|
15344
|
+
const after = input.incoming.manifest.steps.find((step) => step.id === id);
|
|
15345
|
+
return before === void 0 || after === void 0 || before.kind !== after.kind || before.target !== after.target || before.value !== after.value;
|
|
15346
|
+
});
|
|
15347
|
+
const addedgrants = [...new Set(input.incoming.manifest.requiredgrants)].filter((origin) => !input.existing.manifest.requiredgrants.includes(origin));
|
|
15348
|
+
return { changedsteps, addedgrants, versionfrom: input.existing.manifest.version, versionto: input.incoming.manifest.version, replace: true, reason: `The update of ${input.incoming.manifest.id} from version ${input.existing.manifest.version} to ${input.incoming.manifest.version} changes ${changedsteps.length} step${changedsteps.length === 1 ? "" : "s"} and adds ${addedgrants.length} grant${addedgrants.length === 1 ? "" : "s"}; the version diff surfaces before the replace.` };
|
|
15349
|
+
}
|
|
15350
|
+
function removelibrary(input) {
|
|
15351
|
+
return { removed: input.entry.id, keptforks: input.forks.map((fork) => fork.id), reason: `The library entry ${input.entry.manifest.title} leaves the store while its ${input.forks.length} local fork${input.forks.length === 1 ? "" : "s"} stay untouched; a fork is an independent local workflow.` };
|
|
15352
|
+
}
|
|
15353
|
+
function forklibrary(input) {
|
|
15354
|
+
const manifest = input.entry.manifest;
|
|
15355
|
+
const steps = manifest.steps.map((step) => ({ id: `fork-${step.id}`, kind: step.kind, label: step.label, ...step.target !== void 0 ? { target: step.target } : {}, ...step.value !== void 0 ? { value: step.value } : {} }));
|
|
15356
|
+
return { id: `fork:${manifest.id}:${input.now}`, name: `${manifest.title} fork`, version: 1, origins: [...new Set(manifest.requiredgrants)], steps, blocks: [], risk: manifestrisk(manifest), reviewstate: "pending", createdat: input.now };
|
|
15357
|
+
}
|
|
15358
|
+
function librarysearch(input) {
|
|
15359
|
+
const query = input.query?.trim().toLowerCase() ?? "";
|
|
15360
|
+
return input.entries.filter((entry) => {
|
|
15361
|
+
if (input.filter?.publisher !== void 0 && entry.manifest.publisher !== input.filter.publisher) return false;
|
|
15362
|
+
if (input.filter?.sensitive !== void 0 && entry.manifest.sensitive !== input.filter.sensitive) return false;
|
|
15363
|
+
if (input.filter?.state !== void 0 && entry.state !== input.filter.state) return false;
|
|
15364
|
+
if (query === "") return true;
|
|
15365
|
+
return [entry.manifest.title, entry.manifest.description, entry.manifest.publisher].some((text2) => text2.toLowerCase().includes(query));
|
|
15366
|
+
});
|
|
15367
|
+
}
|
|
15368
|
+
function librarybrowserow(entry) {
|
|
15369
|
+
return { id: entry.id, title: entry.manifest.title, publisher: entry.manifest.publisher, version: entry.manifest.version, grants: entry.manifest.requiredgrants, sensitive: entry.manifest.sensitive, state: entry.state, ...entry.manifest.registry !== void 0 ? { registry: entry.manifest.registry } : {} };
|
|
15370
|
+
}
|
|
15371
|
+
function librarystepsview(entry) {
|
|
15372
|
+
return entry.manifest.steps.map((step) => {
|
|
15373
|
+
const expectations = entry.manifest.dataexpectations.filter((expectation) => expectation.stepid === step.id);
|
|
15374
|
+
return { id: step.id, kind: step.kind, label: step.label, ...step.target !== void 0 ? { target: step.target } : {}, ...step.namespace !== void 0 ? { namespace: step.namespace } : {}, families: expectations.map((expectation) => expectation.family), fields: [...new Set(expectations.flatMap((expectation) => expectation.fields))] };
|
|
15375
|
+
});
|
|
15376
|
+
}
|
|
15377
|
+
function libraryeventof(input) {
|
|
15378
|
+
if (input.entryid.trim() === "") throw new Error("The library event needs its entry id.");
|
|
15379
|
+
return { id: `libraryevent:${input.kind}:${input.entryid}:${input.now}`, kind: input.kind, entryid: input.entryid, title: input.title, version: input.version, detail: input.detail, at: input.now };
|
|
15380
|
+
}
|
|
15381
|
+
|
|
15382
|
+
// syncbridge.ts
|
|
15383
|
+
async function sha2564(payload) {
|
|
15384
|
+
const bytes = new TextEncoder().encode(payload);
|
|
15385
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
15386
|
+
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
15387
|
+
}
|
|
15388
|
+
async function syncdigestof(manifest) {
|
|
15389
|
+
const { publish, ...body } = manifest;
|
|
15390
|
+
void publish;
|
|
15391
|
+
return sha2564(JSON.stringify(body));
|
|
15392
|
+
}
|
|
15393
|
+
function syncbridgehookof(input) {
|
|
15394
|
+
if (input.endpoint.trim() === "") throw new Error(`The ${input.provider} hook needs its user configured endpoint; no provider address is ever hardcoded.`);
|
|
15395
|
+
const gate = syncbridgeoptingate({ optin: false });
|
|
15396
|
+
if (gate.allowed) throw new Error("The syncbridge hook never starts with its opt in on; no hook ever defaults on.");
|
|
15397
|
+
return { id: `sync:${input.provider}:${input.now}`, provider: input.provider, direction: input.direction, optin: false, endpoint: input.endpoint.trim(), state: "idle", createdat: input.now };
|
|
15398
|
+
}
|
|
15399
|
+
function syncbridgeoptinflip(hook, optin) {
|
|
15400
|
+
return { ...hook, optin, state: "idle" };
|
|
15401
|
+
}
|
|
15402
|
+
function syncbridgeproviders() {
|
|
15403
|
+
return [
|
|
15404
|
+
{ provider: "file", label: "File provider", operations: ["pull", "push", "list"], note: "The file provider moves manifests through manual import and export files the user picks; the bridge carries no secret and no log entry under any flag." },
|
|
15405
|
+
{ provider: "web", label: "Web provider stub", operations: ["pull", "push", "list"], note: "The web provider stays a stub behind its opt in gate: the endpoint stays the user's configured registry value and no network call ships before the ecosystem part two backend exists." }
|
|
15406
|
+
];
|
|
15407
|
+
}
|
|
15408
|
+
async function syncbridgeexportpayload(manifests) {
|
|
15409
|
+
const digests = [];
|
|
15410
|
+
for (const manifest of manifests) digests.push(await syncdigestof(manifest));
|
|
15411
|
+
return { version: 1, kind: "syncbridge", manifests, digests, exclusions: ["secretvault values", "logs"] };
|
|
15412
|
+
}
|
|
15413
|
+
function syncbridgevalidate(payload) {
|
|
15414
|
+
const carriessecrets = payload.secrets !== void 0 || Array.isArray(payload.manifests) && payload.manifests.some((manifest) => secretcarrying2(manifest));
|
|
15415
|
+
const gate = syncbridgescopegate({ carriessecrets, carrieslogs: payload.logs !== void 0 });
|
|
15416
|
+
if (!gate.allowed) return { ok: false, reason: gate.reason ?? "The syncbridge payload refuses." };
|
|
15417
|
+
if (payload.kind !== "syncbridge") return { ok: false, reason: "The syncbridge payload names its kind; a foreign payload never imports." };
|
|
15418
|
+
if (!Array.isArray(payload.manifests)) return { ok: false, reason: "The syncbridge payload carries its manifest list." };
|
|
15419
|
+
return { ok: true, reason: `The syncbridge payload carries ${payload.manifests.length} manifest${payload.manifests.length === 1 ? "" : "s"} and no secret and no log entry; the bridge moves manifests only.` };
|
|
15420
|
+
}
|
|
15421
|
+
function secretcarrying2(record2) {
|
|
15422
|
+
if (record2 === null || typeof record2 !== "object") return false;
|
|
15423
|
+
const entries = Object.entries(record2);
|
|
15424
|
+
const secretkeys = ["secret", "token", "password", "apikey", "authorization"];
|
|
15425
|
+
return entries.some(([key, value]) => secretkeys.some((shape) => key.toLowerCase().includes(shape)) && typeof value === "string" && value.trim() !== "");
|
|
15426
|
+
}
|
|
15427
|
+
async function syncbridgescan(input) {
|
|
15428
|
+
const gate = syncbridgeoptingate({ optin: input.hook.optin });
|
|
15429
|
+
if (!gate.allowed) return { conflicts: [], synced: [], reason: gate.reason ?? "" };
|
|
15430
|
+
const conflicts = [];
|
|
15431
|
+
const synced = [];
|
|
15432
|
+
for (const remotemanifest of input.remote) {
|
|
15433
|
+
const localmanifest = input.local.find((candidate) => candidate.manifestid === remotemanifest.manifestid);
|
|
15434
|
+
if (localmanifest === void 0) {
|
|
15435
|
+
synced.push(remotemanifest.manifestid);
|
|
15436
|
+
continue;
|
|
15437
|
+
}
|
|
15438
|
+
if (localmanifest.digest === remotemanifest.digest) {
|
|
15439
|
+
synced.push(remotemanifest.manifestid);
|
|
15440
|
+
continue;
|
|
15441
|
+
}
|
|
15442
|
+
conflicts.push({ id: `conflict:${remotemanifest.manifestid}:${input.now}`, hookid: input.hook.id, manifestid: remotemanifest.manifestid, local: { digest: localmanifest.digest, version: localmanifest.version }, remote: { digest: remotemanifest.digest, version: remotemanifest.version }, detectedat: input.now });
|
|
15443
|
+
}
|
|
15444
|
+
return { conflicts, synced, reason: `The ${input.hook.provider} hook of ${input.hook.endpoint} scanned ${input.remote.length} remote manifest${input.remote.length === 1 ? "" : "s"} against ${input.local.length} local entr${input.local.length === 1 ? "y" : "ies"}: ${synced.length} moved while ${conflicts.length} conflict${conflicts.length === 1 ? "" : "s"} surfaced both versions instead of a silent overwrite.` };
|
|
15445
|
+
}
|
|
15446
|
+
function webproviderstub(hook) {
|
|
15447
|
+
const gate = syncbridgeoptingate({ optin: hook.optin });
|
|
15448
|
+
return { hookid: hook.id, endpoint: hook.endpoint, optin: hook.optin, operations: ["pull", "push", "list"], stub: true, reason: gate.allowed ? `The web provider of ${hook.endpoint} stays a stub behind its explicit opt in; the ecosystem part two backend brings its network path.` : gate.reason ?? "" };
|
|
15449
|
+
}
|
|
15450
|
+
|
|
15451
|
+
// attentionfeed.ts
|
|
15452
|
+
function attentionseverityof(cause) {
|
|
15453
|
+
if (cause === "gatewait" || cause === "phishguard") return "critical";
|
|
15454
|
+
if (cause === "deferral") return "warning";
|
|
15455
|
+
return "info";
|
|
15456
|
+
}
|
|
15457
|
+
function attentiondeeplinkof(cause, runid, gateref) {
|
|
15458
|
+
if (cause === "gatewait") return `devthink://gate/${encodeURIComponent(gateref ?? "unknown")}?run=${encodeURIComponent(runid)}`;
|
|
15459
|
+
if (cause === "phishguard") return `devthink://phishguard?run=${encodeURIComponent(runid)}`;
|
|
15460
|
+
if (cause === "deferral") return `devthink://deferred?run=${encodeURIComponent(runid)}`;
|
|
15461
|
+
return `devthink://error?run=${encodeURIComponent(runid)}`;
|
|
15462
|
+
}
|
|
15463
|
+
function attentionentryof(input) {
|
|
15464
|
+
if (input.runid.trim() === "") throw new Error("The attention entry needs its run ref.");
|
|
15465
|
+
if (input.summary.trim() === "") throw new Error("The attention entry needs its summary in plain language.");
|
|
15466
|
+
return { id: `attention:${input.cause}:${input.runid}:${input.gateref ?? "none"}`, cause: input.cause, severity: attentionseverityof(input.cause), runid: input.runid, ...input.gateref !== void 0 && input.gateref.trim() !== "" ? { gateref: input.gateref } : {}, origin: input.origin, summary: input.summary, deeplink: attentiondeeplinkof(input.cause, input.runid, input.gateref), at: input.at };
|
|
15467
|
+
}
|
|
15468
|
+
function dedupeattention(entries) {
|
|
15469
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15470
|
+
const kept = [];
|
|
15471
|
+
for (const entry of [...entries].sort((a, b) => a.at - b.at)) {
|
|
15472
|
+
const key = `${entry.cause}:${entry.runid}:${entry.gateref ?? "none"}`;
|
|
15473
|
+
if (seen.has(key)) continue;
|
|
15474
|
+
seen.add(key);
|
|
15475
|
+
kept.push(entry);
|
|
15476
|
+
}
|
|
15477
|
+
return kept;
|
|
15478
|
+
}
|
|
15479
|
+
function rankattention(entries) {
|
|
15480
|
+
const order = { critical: 0, warning: 1, info: 2 };
|
|
15481
|
+
return [...entries].sort((a, b) => order[a.severity] - order[b.severity] || b.at - a.at);
|
|
15482
|
+
}
|
|
15483
|
+
function attentioncountof(entries) {
|
|
15484
|
+
return rankattention(dedupeattention(entries)).length;
|
|
15485
|
+
}
|
|
15486
|
+
function attentionnotifications(entries, now) {
|
|
15487
|
+
return rankattention(dedupeattention(entries)).map((entry) => ({ id: `notify:${entry.id}`, kind: "attention", title: entry.cause === "gatewait" ? "A gate waits for you" : entry.cause === "phishguard" ? "The phishguard blocked a step" : entry.cause === "deferral" ? "A command deferred" : "A step failed", body: entry.summary, deeplink: entry.deeplink, runid: entry.runid, ...entry.gateref !== void 0 ? { stepid: entry.gateref } : {}, content: false, at: now }));
|
|
15488
|
+
}
|
|
15489
|
+
|
|
15490
|
+
// backgroundruns.ts
|
|
15491
|
+
function backgroundqueueentryof(input) {
|
|
15492
|
+
if (input.workflowid.trim() === "") throw new Error("The background queue entry needs its workflow id.");
|
|
15493
|
+
return { id: `background:${input.workflowid}:${input.now}`, workflowid: input.workflowid, state: "queued", keepaliveheld: false, queuedat: input.now, summary: input.summary };
|
|
15494
|
+
}
|
|
15495
|
+
function enqueuebackgroundrun(input) {
|
|
15496
|
+
return [...input.queue, backgroundqueueentryof({ workflowid: input.workflowid, summary: input.summary, now: input.now })];
|
|
15497
|
+
}
|
|
15498
|
+
function nextbackgroundrun(queue) {
|
|
15499
|
+
return [...queue].filter((entry) => entry.state === "queued").sort((a, b) => a.queuedat - b.queuedat)[0];
|
|
15500
|
+
}
|
|
15501
|
+
function beginbackgroundrun(entry, now, reviewed) {
|
|
15502
|
+
const gate = backgroundrungate({ reviewed, keepaliveheld: true });
|
|
15503
|
+
return { entry: gate.allowed ? { ...entry, state: "running", keepaliveheld: true, startedat: now } : entry, gate: { allowed: gate.allowed, reason: gate.reason ?? "" } };
|
|
15504
|
+
}
|
|
15505
|
+
function finishbackgroundrun(entry, state, now) {
|
|
15506
|
+
return { ...entry, state, keepaliveheld: false, endedat: now };
|
|
15507
|
+
}
|
|
15508
|
+
function resumebackgroundqueue(queue, now) {
|
|
15509
|
+
const requeued = [];
|
|
15510
|
+
const next = queue.map((entry) => {
|
|
15511
|
+
if (entry.state !== "running") return entry;
|
|
15512
|
+
requeued.push(entry.id);
|
|
15513
|
+
const { startedat, ...rest } = entry;
|
|
15514
|
+
void startedat;
|
|
15515
|
+
return { ...rest, state: "queued", keepaliveheld: false };
|
|
15516
|
+
});
|
|
15517
|
+
void now;
|
|
15518
|
+
return { queue: next, requeued };
|
|
15519
|
+
}
|
|
15520
|
+
function backgroundrunsview(queue) {
|
|
15521
|
+
return queue.map((entry) => ({ id: entry.id, workflowid: entry.workflowid, state: entry.state, keepaliveheld: entry.keepaliveheld, queuedat: entry.queuedat, ...entry.startedat !== void 0 ? { startedat: entry.startedat } : {}, ...entry.endedat !== void 0 ? { endedat: entry.endedat } : {}, summary: entry.summary, progress: entry.state === "running" ? `Running with the keepalive signal held since ${entry.startedat ?? entry.queuedat}` : entry.state === "queued" ? "Queued for the next executor turn" : entry.state === "done" ? "Completed in the background" : "Failed; the attentionfeed carries the cause" }));
|
|
15522
|
+
}
|
|
15523
|
+
function backgroundtrayrows(queue, origin) {
|
|
15524
|
+
return queue.filter((entry) => entry.state === "done" || entry.state === "failed").map((entry) => ({ runid: entry.id, origin, outcome: entry.state === "done" ? "completed" : "failed", title: `Background run of ${entry.workflowid}`, at: entry.endedat ?? entry.queuedat, resumable: false, reopenable: true }));
|
|
15525
|
+
}
|
|
15526
|
+
function cancelbackgroundentry(queue, id) {
|
|
15527
|
+
const cancelled = [];
|
|
15528
|
+
const next = queue.filter((entry) => {
|
|
15529
|
+
if (entry.id === id && entry.state === "queued") {
|
|
15530
|
+
cancelled.push(entry.id);
|
|
15531
|
+
return false;
|
|
15532
|
+
}
|
|
15533
|
+
return true;
|
|
15534
|
+
});
|
|
15535
|
+
return { queue: next, cancelled };
|
|
15536
|
+
}
|
|
15537
|
+
|
|
15538
|
+
// runreplay.ts
|
|
15539
|
+
function restoredrefsof(entry) {
|
|
15540
|
+
const observation = /observation (\d+)/i.exec(entry.summary);
|
|
15541
|
+
const capture = /capture ([a-z0-9-]+)/i.exec(entry.summary);
|
|
15542
|
+
return { ...observation !== null ? { observationversion: Number(observation[1]) } : {}, ...capture !== null ? { captureid: capture[1] } : {} };
|
|
15543
|
+
}
|
|
15544
|
+
function replaystepof(entry, index, gates) {
|
|
15545
|
+
const refs = restoredrefsof(entry);
|
|
15546
|
+
return { stepid: entry.stepid ?? entry.id, index, summary: entry.summary, ...refs.observationversion !== void 0 ? { observationversion: refs.observationversion } : {}, ...refs.captureid !== void 0 ? { captureid: refs.captureid } : {}, gateresolutions: entry.stepid === void 0 ? [] : gates.filter((gate) => gate.gateid === entry.stepid) };
|
|
15547
|
+
}
|
|
15548
|
+
function runreplaysessionof(input) {
|
|
15549
|
+
if (input.runid.trim() === "") throw new Error("The runreplay session needs its recorded run id.");
|
|
15550
|
+
if (input.entries.length === 0) throw new Error("The runreplay session needs at least one verified log entry to walk.");
|
|
15551
|
+
const steps = input.entries.map((entry, index) => replaystepof(entry, index, input.gates ?? []));
|
|
15552
|
+
return { id: `replay:${input.runid}:${input.now}`, runid: input.runid, cursor: 0, playing: false, steps, openedat: input.now, actions: [] };
|
|
15553
|
+
}
|
|
15554
|
+
function replaymove(session, direction, now) {
|
|
15555
|
+
const next = direction === "forward" ? Math.min(session.steps.length - 1, session.cursor + 1) : Math.max(0, session.cursor - 1);
|
|
15556
|
+
const step = session.steps[next];
|
|
15557
|
+
return { ...session, cursor: next, playing: false, actions: [...session.actions, { kind: "step", at: now, ...step !== void 0 ? { stepid: step.stepid } : {} }] };
|
|
15558
|
+
}
|
|
15559
|
+
function replayjump(session, stepid, now) {
|
|
15560
|
+
const index = session.steps.findIndex((step) => step.stepid === stepid);
|
|
15561
|
+
if (index === -1) throw new Error(`The runreplay knows no ${stepid} step in the recorded chain of ${session.runid}.`);
|
|
15562
|
+
return { ...session, cursor: index, playing: false, actions: [...session.actions, { kind: "jump", stepid, at: now }] };
|
|
15563
|
+
}
|
|
15564
|
+
function replayplay(session, playing, now) {
|
|
15565
|
+
return { ...session, playing, actions: [...session.actions, { kind: playing ? "play" : "pause", at: now }] };
|
|
15566
|
+
}
|
|
15567
|
+
function replayrestoredview(step) {
|
|
15568
|
+
return { stepid: step.stepid, index: step.index, summary: step.summary, ...step.observationversion !== void 0 ? { observationversion: step.observationversion } : {}, ...step.captureid !== void 0 ? { captureid: step.captureid } : {}, gateresolutions: step.gateresolutions };
|
|
15569
|
+
}
|
|
15570
|
+
|
|
15571
|
+
// outputcompare.ts
|
|
15572
|
+
function taskinputsignatureof(input) {
|
|
15573
|
+
return `${input.objective.trim()}|${input.steps.length}|${input.steps.join(",")}`;
|
|
15574
|
+
}
|
|
15575
|
+
function comparemetricdefaults() {
|
|
15576
|
+
return ["agreement", "divergence", "durationdelta"];
|
|
15577
|
+
}
|
|
15578
|
+
function joinruns(logsa, logsb) {
|
|
15579
|
+
const sequence = [];
|
|
15580
|
+
for (const entry of logsa) if (!sequence.includes(entry.stepid)) sequence.push(entry.stepid);
|
|
15581
|
+
for (const entry of logsb) if (!sequence.includes(entry.stepid)) sequence.push(entry.stepid);
|
|
15582
|
+
return sequence.map((stepid, index) => {
|
|
15583
|
+
const a = logsa.find((entry) => entry.stepid === stepid);
|
|
15584
|
+
const b = logsb.find((entry) => entry.stepid === stepid);
|
|
15585
|
+
return { stepid, index, ...a !== void 0 ? { a } : {}, ...b !== void 0 ? { b } : {} };
|
|
15586
|
+
});
|
|
15587
|
+
}
|
|
15588
|
+
function stepcomparisonof(pair) {
|
|
15589
|
+
const a = pair.a;
|
|
15590
|
+
const b = pair.b;
|
|
15591
|
+
if (a === void 0 || b === void 0) return { stepid: pair.stepid, index: pair.index, agreement: "onlyone", summarya: a?.summary ?? "", summaryb: b?.summary ?? "", durationdelta: 0 };
|
|
15592
|
+
const agree = a.state === b.state && a.summary === b.summary;
|
|
15593
|
+
return { stepid: pair.stepid, index: pair.index, agreement: agree ? "agree" : "diverge", summarya: a.summary, summaryb: b.summary, durationdelta: a.duration - b.duration };
|
|
15594
|
+
}
|
|
15595
|
+
function firstdivergenceof(steps) {
|
|
15596
|
+
const divergent = steps.find((step) => step.agreement !== "agree");
|
|
15597
|
+
return divergent === void 0 ? void 0 : divergent.index;
|
|
15598
|
+
}
|
|
15599
|
+
function outputcomparesessionof(input) {
|
|
15600
|
+
if (input.runids[0].trim() === "" || input.runids[1].trim() === "") throw new Error("The outputcompare session needs both run ids.");
|
|
15601
|
+
if (input.runids[0] === input.runids[1]) throw new Error("The outputcompare session compares two distinct runs; one run never stands beside itself.");
|
|
15602
|
+
const metrics = input.metrics ?? comparemetricdefaults();
|
|
15603
|
+
const steps = joinruns(input.logsa, input.logsb).map((pair) => stepcomparisonof(pair));
|
|
15604
|
+
const firstdivergence = firstdivergenceof(steps);
|
|
15605
|
+
return { id: `compare:${input.runids[0]}:${input.runids[1]}:${input.now}`, runids: input.runids, metrics, steps, ...firstdivergence !== void 0 ? { firstdivergence } : {}, openedat: input.now };
|
|
15606
|
+
}
|
|
15607
|
+
function comparesessionmetrics(session) {
|
|
15608
|
+
const agree = session.steps.filter((step) => step.agreement === "agree").length;
|
|
15609
|
+
const diverge = session.steps.filter((step) => step.agreement === "diverge").length;
|
|
15610
|
+
const onlyone = session.steps.filter((step) => step.agreement === "onlyone").length;
|
|
15611
|
+
return { metrics: session.metrics, agree, diverge, onlyone, ...session.firstdivergence !== void 0 ? { firstdivergence: session.firstdivergence } : {}, reason: `The comparison of ${session.runids[0]} and ${session.runids[1]} graded ${agree} agreeing, ${diverge} divergent and ${onlyone} single run step${agree + diverge + onlyone === 1 ? "" : "s"} under the metric set ${session.metrics.join(", ")}${session.firstdivergence !== void 0 ? ` with the first divergence at step index ${session.firstdivergence}` : " with agreement across the whole sequence"}.` };
|
|
15612
|
+
}
|
|
15613
|
+
function outputcompareview(session) {
|
|
15614
|
+
return session.steps.map((step) => ({ stepid: step.stepid, index: step.index, agreement: step.agreement, summarya: step.summarya, summaryb: step.summaryb, durationdelta: step.durationdelta, highlighted: session.firstdivergence !== void 0 && step.index === session.firstdivergence }));
|
|
15615
|
+
}
|
|
15616
|
+
|
|
14513
15617
|
// modelroute.ts
|
|
14514
15618
|
function routevalid(route) {
|
|
14515
15619
|
if (route.kind.trim() === "") return { allowed: false, reason: "The model route needs its task kind." };
|
|
@@ -15737,6 +16841,13 @@ function extensionpage(sender) {
|
|
|
15737
16841
|
async function audit(kind, summary, extra = {}) {
|
|
15738
16842
|
await memory.addaudi({ id: randomid(), kind, at: Date.now(), summary, ...extra });
|
|
15739
16843
|
await recordsurfaceevent(kind, summary, extra);
|
|
16844
|
+
if (kind === "session" || kind === "pause" || kind === "resume" || kind === "complete" || kind === "cancel" || kind === "gate" || kind === "notify" || kind === "attention" || kind === "backgroundrun") void updatestatusbadge().catch(() => {
|
|
16845
|
+
});
|
|
16846
|
+
}
|
|
16847
|
+
async function recordattention(cause, runid, origin, summary, gateref) {
|
|
16848
|
+
const entry = attentionentryof({ cause, runid, origin, summary, ...gateref !== void 0 && gateref.trim() !== "" ? { gateref } : {}, at: Date.now() });
|
|
16849
|
+
await memory.addattentionentry(entry);
|
|
16850
|
+
await audit("attention", `The attentionfeed collected ${entry.cause} of the run ${entry.runid}${entry.gateref !== void 0 ? ` at the gate ${entry.gateref}` : ""}: ${entry.summary} The deep link ${entry.deeplink} opens the exact waiting surface.`, { ...runid !== "" ? { planid: runid } : {} });
|
|
15740
16851
|
}
|
|
15741
16852
|
var surfacechannel = typeof BroadcastChannel === "function" ? new BroadcastChannel("devthinksurfaces") : void 0;
|
|
15742
16853
|
var logstreamhistory = [];
|
|
@@ -16024,6 +17135,8 @@ async function confirmgatechain(step, session, plan, origin, classification, now
|
|
|
16024
17135
|
await appendrunevent("gate", `The ${gate.kind} gate opened for the ${step.kind} step ${step.id} on ${origin}: ${gateprompttext(gate)}`, session, origin, step.id).catch(() => {
|
|
16025
17136
|
});
|
|
16026
17137
|
await audit("gate", `The ${gate.kind} gate opened for the ${step.kind} step ${step.id} on ${origin}; the executor pauses until one distinct human action resolves it and no timeout ever resolves a gate.`, { sessionid: session.id, planid: plan.id, stepid: step.id });
|
|
17138
|
+
await recordattention("gatewait", plan.id, origin, `The ${gate.kind} gate of the ${step.kind} step ${step.id} waits for one human action; the executor pauses with no timeout.`, gate.gateid).catch(() => {
|
|
17139
|
+
});
|
|
16027
17140
|
return { allowed: false, reason: `The ${gate.kind} gate opened for the ${step.kind} step ${step.id} on ${origin}: ${gateprompttext(gate)} The executor pauses until the human resolves it.` };
|
|
16028
17141
|
}
|
|
16029
17142
|
}
|
|
@@ -16049,6 +17162,8 @@ async function confirmgatechain(step, session, plan, origin, classification, now
|
|
|
16049
17162
|
await appendrunevent("phish", `The phishguard blocked the credential step ${step.id} on ${origin}: ${verdict.reason}`, session, origin, step.id).catch(() => {
|
|
16050
17163
|
});
|
|
16051
17164
|
await audit("phish", `The phishguard blocked the ${step.kind} step ${step.id} on ${origin}: ${verdict.reason}`, { sessionid: session.id, planid: plan.id, stepid: step.id });
|
|
17165
|
+
await recordattention("phishguard", plan.id, origin, `The phishguard blocked the credential step ${step.id} on ${origin}: ${verdict.reason}`).catch(() => {
|
|
17166
|
+
});
|
|
16052
17167
|
return { allowed: false, reason: phishgate.reason ?? "" };
|
|
16053
17168
|
}
|
|
16054
17169
|
}
|
|
@@ -16063,6 +17178,8 @@ async function confirmgatechain(step, session, plan, origin, classification, now
|
|
|
16063
17178
|
await appendrunevent("suspend", `The ratelimit bucket of ${origin} deferred the ${step.kind} step ${step.id} until the window resets at ${deferred.resetsat}: ${consumed.reason}`, session, origin, step.id).catch(() => {
|
|
16064
17179
|
});
|
|
16065
17180
|
await audit("defer", `The ratelimit bucket of ${origin} deferred the ${step.kind} step ${step.id} until the window resets at ${deferred.resetsat}; the bounds stay user configured choices with no hidden ceiling.`, { sessionid: session.id, planid: plan.id, stepid: step.id });
|
|
17181
|
+
await recordattention("deferral", plan.id, origin, `The ratelimit bucket deferred the ${step.kind} step ${step.id} until the window resets at ${deferred.resetsat}.`).catch(() => {
|
|
17182
|
+
});
|
|
16066
17183
|
return { allowed: false, reason: consumed.reason };
|
|
16067
17184
|
}
|
|
16068
17185
|
await memory.saveratelimitbucket(consumed.bucket);
|
|
@@ -20908,10 +22025,16 @@ async function executeworkflowrun(step, session, plan, tabid2, origin, dry) {
|
|
|
20908
22025
|
await audit("error", `The background run ${run.id} of the workflow ${record2.name} failed: ${reason}.`, { sessionid: session.id, planid: plan.id, stepid: step.id });
|
|
20909
22026
|
await refreshbadge().catch(() => {
|
|
20910
22027
|
});
|
|
22028
|
+
}).finally(() => {
|
|
22029
|
+
void advancebackgroundqueue().catch(() => {
|
|
22030
|
+
});
|
|
20911
22031
|
});
|
|
20912
22032
|
return { ok: true, summary: `The workflow run ${run.id} started in the background with ${record2.steps.length} reviewed steps and the panel may close; the checkpoints restore it on every worker wake.`, details: { runid: run.id, state: "running", background: true, executed: 0, total: record2.steps.length } };
|
|
20913
22033
|
}
|
|
20914
|
-
|
|
22034
|
+
const foregroundoutput = await performworkflowrun({ record: record2, run, step, session, plan, tabid: tabid2, origin, options, dry });
|
|
22035
|
+
void advancebackgroundqueue().catch(() => {
|
|
22036
|
+
});
|
|
22037
|
+
return foregroundoutput;
|
|
20915
22038
|
}
|
|
20916
22039
|
function runcauseof(options) {
|
|
20917
22040
|
const variables = options.variables;
|
|
@@ -21401,6 +22524,8 @@ async function executeaction(step, session, plan, tabid2, origin, settings, verd
|
|
|
21401
22524
|
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
22525
|
await memory.adderrorsurface(surface).catch(() => {
|
|
21403
22526
|
});
|
|
22527
|
+
await recordattention("failure", plan.id, origin, `The ${step.kind} step ${step.id} failed: ${summary} The retry stays a reviewed dispatch.`).catch(() => {
|
|
22528
|
+
});
|
|
21404
22529
|
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
22530
|
}
|
|
21406
22531
|
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(() => {
|
|
@@ -21529,6 +22654,8 @@ var commandschemas = {
|
|
|
21529
22654
|
environments: { action: "string", grants: "array", requestcapability: "boolean", pool: "object", offscreenclose: "boolean", sandbox: "object", settings: "object", render: "object", sandboxhost: "object" },
|
|
21530
22655
|
transparency: {},
|
|
21531
22656
|
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" },
|
|
22657
|
+
views: { datagrid: "object", export: "object", quickaction: "object", shortcut: "object", omnibox: "object", badge: "object", notify: "object", recent: "object", picker: "object", halo: "object", tips: "object", shotpanel: "object", compare: "object", siteprofile: "object", theme: "object", locale: "object", importexport: "object", dropimport: "object", tour: "object", a11y: "object", chip: "object", toast: "object", settings: "object" },
|
|
22658
|
+
ecosystem: { library: "object", sync: "object", attention: "object", replay: "object", compare: "object", background: "object", settings: "object" },
|
|
21532
22659
|
execute: { stepid: "string" },
|
|
21533
22660
|
configure: { endpoint: "string" }
|
|
21534
22661
|
};
|
|
@@ -22028,6 +23155,709 @@ async function handlesurfacecommand(message) {
|
|
|
22028
23155
|
}
|
|
22029
23156
|
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
23157
|
}
|
|
23158
|
+
async function updatestatusbadge() {
|
|
23159
|
+
try {
|
|
23160
|
+
const plan = await memory.getplan();
|
|
23161
|
+
const progress = await memory.getprogress();
|
|
23162
|
+
const attentioncount = attentioncountof(await memory.getattentionentries());
|
|
23163
|
+
const waitingcount = (plan !== void 0 && progress !== void 0 && progress.planid === plan.id ? Object.keys(progress.gatewaits ?? {}).length : 0) + attentioncount;
|
|
23164
|
+
const state = statusbadgeof({ ...plan !== void 0 && (plan.state === "pending" || plan.state === "approved") ? { planstate: plan.state } : {}, waitingcount, ...plan !== void 0 ? { runid: plan.id } : {} });
|
|
23165
|
+
await chrome.action.setBadgeText({ text: badgetextof(state) });
|
|
23166
|
+
await chrome.action.setBadgeBackgroundColor({ color: badgecolorof(state) });
|
|
23167
|
+
} catch {
|
|
23168
|
+
}
|
|
23169
|
+
}
|
|
23170
|
+
function windowmatchmedia() {
|
|
23171
|
+
const query = globalThis.matchMedia?.("(prefers-color-scheme: dark)");
|
|
23172
|
+
return query?.matches === true ? "dark" : "light";
|
|
23173
|
+
}
|
|
23174
|
+
async function installedkindcapabilities() {
|
|
23175
|
+
const granted = await grantedcapabilities();
|
|
23176
|
+
return reviewedkinds().filter((kind) => {
|
|
23177
|
+
const capability = requiredcapability(kind);
|
|
23178
|
+
return capability === void 0 || granted.includes(capability);
|
|
23179
|
+
});
|
|
23180
|
+
}
|
|
23181
|
+
async function ecosystemviewof() {
|
|
23182
|
+
const entries = await memory.getflowlibrary();
|
|
23183
|
+
const installed = entries.filter((entry) => entry.state === "installed");
|
|
23184
|
+
const hooks = await memory.getsyncbridgehooks();
|
|
23185
|
+
const conflicts = await memory.getsyncbridgeconflicts();
|
|
23186
|
+
const attention = rankattention(await memory.getattentionentries()).slice(0, 50).map((entry) => ({ id: entry.id, cause: entry.cause, severity: entry.severity, runid: entry.runid, summary: entry.summary, deeplink: entry.deeplink, at: entry.at }));
|
|
23187
|
+
const queue = await memory.getbackgroundqueue();
|
|
23188
|
+
return ecosystemviews({
|
|
23189
|
+
library: librarysearch({ entries }).map((entry) => librarybrowserow(entry)),
|
|
23190
|
+
installed: installed.map((entry) => ({ id: entry.id, title: entry.manifest.title, version: entry.manifest.version, forkable: true })),
|
|
23191
|
+
syncbridge: { hooks: hooks.map((hook) => ({ id: hook.id, provider: hook.provider, direction: hook.direction, optin: hook.optin, endpoint: hook.endpoint, state: hook.state })), conflicts: conflicts.filter((conflict) => conflict.resolution === void 0).map((conflict) => ({ id: conflict.id, manifestid: conflict.manifestid, localversion: conflict.local.version, remoteversion: conflict.remote.version, ...conflict.resolution !== void 0 ? { resolution: conflict.resolution } : {} })) },
|
|
23192
|
+
attention,
|
|
23193
|
+
backgroundruns: backgroundrunsview(queue)
|
|
23194
|
+
});
|
|
23195
|
+
}
|
|
23196
|
+
async function handleecosystemcommand(message) {
|
|
23197
|
+
const input = message;
|
|
23198
|
+
const now = Date.now();
|
|
23199
|
+
const session = await memory.getsession();
|
|
23200
|
+
const settings = await memory.getsettings();
|
|
23201
|
+
if (input.library !== void 0) {
|
|
23202
|
+
const library = input.library;
|
|
23203
|
+
if (library.browse !== void 0) {
|
|
23204
|
+
const entries = librarysearch({ entries: await memory.getflowlibrary(), ...library.browse.query !== void 0 ? { query: library.browse.query } : {}, ...library.browse.publisher !== void 0 ? { filter: { publisher: library.browse.publisher } } : {}, ...library.browse.sensitive !== void 0 ? { filter: { sensitive: library.browse.sensitive } } : {}, ...library.browse.state !== void 0 ? { filter: { state: library.browse.state } } : {} });
|
|
23205
|
+
await audit("library", `The dashboardpage browsed the flowlibrary with ${entries.length} matching entr${entries.length === 1 ? "y" : "ies"}; every row names its publisher, version, required grants and state.`, { ...session ? { sessionid: session.id } : {} });
|
|
23206
|
+
return { ...await ecosystemviewof(), rows: entries.map((entry) => librarybrowserow(entry)) };
|
|
23207
|
+
}
|
|
23208
|
+
if (library.entry !== void 0) {
|
|
23209
|
+
const entry = (await memory.getflowlibrary()).find((candidate) => candidate.id === (library.entry?.entryid ?? ""));
|
|
23210
|
+
if (!entry) throw new Error(`No flowlibrary entry matches ${library.entry.entryid ?? ""}.`);
|
|
23211
|
+
return { entry, steps: librarystepsview(entry), expectations: entry.manifest.dataexpectations };
|
|
23212
|
+
}
|
|
23213
|
+
if (library.grants !== void 0) {
|
|
23214
|
+
const entry = (await memory.getflowlibrary()).find((candidate) => candidate.id === (library.grants?.entryid ?? ""));
|
|
23215
|
+
if (!entry) throw new Error(`No flowlibrary entry matches ${library.grants.entryid ?? ""}.`);
|
|
23216
|
+
const grants = session ? [.../* @__PURE__ */ new Set([session.origin, ...session.grants ?? []])] : [];
|
|
23217
|
+
const diff = grantdiffof({ manifest: entry.manifest, heldgrants: grants });
|
|
23218
|
+
await audit("library", `The grant diff of ${entry.manifest.title} surfaces ${diff.added.length} added grant${diff.added.length === 1 ? "" : "s"} and ${diff.kept.length} held grant${diff.kept.length === 1 ? "" : "s"} before any import completes.`, { ...session ? { sessionid: session.id } : {} });
|
|
23219
|
+
return { diff };
|
|
23220
|
+
}
|
|
23221
|
+
if (library.import !== void 0) {
|
|
23222
|
+
const capabilities = await installedkindcapabilities();
|
|
23223
|
+
const validation = await validatemanifest({ manifest: library.import.manifest, capabilities });
|
|
23224
|
+
if (!validation.ok || validation.manifest === void 0) throw new Error(validation.reason);
|
|
23225
|
+
const entry = await libraryentryof({ manifest: validation.manifest, provenance: `Imported from ${validation.manifest.registry ?? "a local file"} by the user`, now });
|
|
23226
|
+
const stored = await memory.addlibraryentry(entry);
|
|
23227
|
+
await audit("library", `The manifest ${entry.manifest.id} of ${entry.manifest.publisher} entered the flowlibrary as ${entry.state}${entry.state === "quarantined" ? "; the unverified publisher quarantines the entry until the user verifies it" : " with its verified publisher signature"}; the store deduplicates by the manifest digest and holds ${stored.length} entr${stored.length === 1 ? "y" : "ies"}.`, { ...session ? { sessionid: session.id } : {} });
|
|
23228
|
+
return { ...await ecosystemviewof(), entry };
|
|
23229
|
+
}
|
|
23230
|
+
if (library.install !== void 0) {
|
|
23231
|
+
const entry = (await memory.getflowlibrary()).find((candidate) => candidate.id === (library.install?.entryid ?? ""));
|
|
23232
|
+
if (!entry) throw new Error(`No flowlibrary entry matches ${library.install.entryid ?? ""}.`);
|
|
23233
|
+
if (entry.state === "quarantined") {
|
|
23234
|
+
const quarantinegate = libraryquarantinegate({ verified: false, signaturepresent: entry.manifest.publish !== void 0, signaturevalid: false });
|
|
23235
|
+
throw new Error(quarantinegate.reason ?? "The quarantined entry never installs on its own.");
|
|
23236
|
+
}
|
|
23237
|
+
const capabilities = await installedkindcapabilities();
|
|
23238
|
+
const capabilitygate = librarycapabilitygate({ kinds: [...new Set(entry.manifest.steps.map((step) => step.kind))], capabilities });
|
|
23239
|
+
if (!capabilitygate.allowed) throw new Error(capabilitygate.reason);
|
|
23240
|
+
const grants = session ? [.../* @__PURE__ */ new Set([session.origin, ...session.grants ?? []])] : [];
|
|
23241
|
+
const grantgate = librarygrantgate({ requiredgrants: entry.manifest.requiredgrants, heldgrants: grants });
|
|
23242
|
+
if (!grantgate.allowed) throw new Error(grantgate.reason);
|
|
23243
|
+
const consent = sensitiveconsentfor(entry.manifest, library.install.consent === true);
|
|
23244
|
+
if (consent.required && !library.install.consent) throw new Error(consent.reason);
|
|
23245
|
+
const record2 = installlibrary({ entry, ...library.install.selectornamespace !== void 0 && library.install.selectornamespace.trim() !== "" ? { selectornamespace: library.install.selectornamespace } : {}, now });
|
|
23246
|
+
await memory.addworkflowrecord(record2);
|
|
23247
|
+
await memory.addworkflowimport({ id: randomid(), record: record2, importedat: now, libraryversion: entry.manifest.version });
|
|
23248
|
+
const installedentry = { ...entry, state: "installed", installedat: now };
|
|
23249
|
+
await memory.setflowlibrary((await memory.getflowlibrary()).map((candidate) => candidate.id === entry.id ? installedentry : candidate));
|
|
23250
|
+
const event = libraryeventof({ kind: "install", entryid: entry.id, title: entry.manifest.title, version: entry.manifest.version, detail: `The manifest ${entry.manifest.id} of ${entry.manifest.publisher} resolved into the pending workflow ${record2.id} with ${record2.steps.length} steps and the selector namespace ${library.install.selectornamespace?.trim() || "unchanged"}.`, now });
|
|
23251
|
+
await memory.addlibraryevent(event);
|
|
23252
|
+
if (session) await appendrunevent("library", `The flowlibrary installed ${entry.manifest.title} version ${entry.manifest.version} of ${entry.manifest.publisher} as the pending workflow ${record2.id}; the import review approves its step list before anything runs.`, session, session.origin).catch(() => {
|
|
23253
|
+
});
|
|
23254
|
+
await audit("library", `The user installed ${entry.manifest.title} version ${entry.manifest.version} of ${entry.manifest.publisher}: the import landed as the pending workflow ${record2.id} with ${record2.steps.length} steps, the selector namespaces rewrote for the local profile and the import review gates every step before any run.`, { ...session ? { sessionid: session.id } : {}, planid: record2.id });
|
|
23255
|
+
return { ...await ecosystemviewof(), record: record2, event };
|
|
23256
|
+
}
|
|
23257
|
+
if (library.update !== void 0) {
|
|
23258
|
+
const entries = await memory.getflowlibrary();
|
|
23259
|
+
const existing = entries.find((candidate) => candidate.id === (library.update?.entryid ?? ""));
|
|
23260
|
+
if (!existing) throw new Error(`No installed flowlibrary entry matches ${library.update.entryid ?? ""}.`);
|
|
23261
|
+
const incoming = await libraryentryof({ manifest: { ...existing.manifest, version: `${existing.manifest.version}+${now}` }, provenance: existing.provenance, now });
|
|
23262
|
+
const diff = updatelibrary({ incoming, existing });
|
|
23263
|
+
if (diff.replace) {
|
|
23264
|
+
await memory.setflowlibrary((await memory.getflowlibrary()).map((candidate) => candidate.id === existing.id ? incoming : candidate));
|
|
23265
|
+
const event = libraryeventof({ kind: "update", entryid: existing.id, title: existing.manifest.title, version: incoming.manifest.version, detail: diff.reason, now });
|
|
23266
|
+
await memory.addlibraryevent(event);
|
|
23267
|
+
if (session) await appendrunevent("library", `The flowlibrary updated ${existing.manifest.title} from version ${diff.versionfrom} to ${diff.versionto} with ${diff.changedsteps.length} changed steps and ${diff.addedgrants.length} added grants; the version diff surfaced before the replace.`, session, session.origin).catch(() => {
|
|
23268
|
+
});
|
|
23269
|
+
await audit("library", `The user updated ${existing.manifest.title} from version ${diff.versionfrom} to ${diff.versionto}: ${diff.changedsteps.length} step${diff.changedsteps.length === 1 ? "" : "s"} changed and ${diff.addedgrants.length} grant${diff.addedgrants.length === 1 ? "" : "s"} added; the version diff surfaced before the replace and the local forks stay untouched.`, { ...session ? { sessionid: session.id } : {} });
|
|
23270
|
+
}
|
|
23271
|
+
return { diff, ...await ecosystemviewof() };
|
|
23272
|
+
}
|
|
23273
|
+
if (library.remove !== void 0) {
|
|
23274
|
+
const entry = (await memory.getflowlibrary()).find((candidate) => candidate.id === (library.remove?.entryid ?? ""));
|
|
23275
|
+
if (!entry) throw new Error(`No flowlibrary entry matches ${library.remove.entryid ?? ""}.`);
|
|
23276
|
+
const forks = (await memory.getworkflowrecordversions()).filter((record2) => record2.id.startsWith(`fork:${entry.manifest.id}:`));
|
|
23277
|
+
const outcome = removelibrary({ entry, forks });
|
|
23278
|
+
await memory.removelibraryentry(entry.id);
|
|
23279
|
+
const event = libraryeventof({ kind: "remove", entryid: entry.id, title: entry.manifest.title, version: entry.manifest.version, detail: outcome.reason, now });
|
|
23280
|
+
await memory.addlibraryevent(event);
|
|
23281
|
+
if (session) await appendrunevent("library", `The flowlibrary removed ${entry.manifest.title} version ${entry.manifest.version} while its ${forks.length} local fork${forks.length === 1 ? "" : "s"} stayed untouched.`, session, session.origin).catch(() => {
|
|
23282
|
+
});
|
|
23283
|
+
await audit("library", `The user removed ${entry.manifest.title} version ${entry.manifest.version} from the flowlibrary; the ${forks.length} local fork${forks.length === 1 ? "" : "s"} stay untouched because a fork is an independent local workflow.`, { ...session ? { sessionid: session.id } : {} });
|
|
23284
|
+
return { ...outcome, ...await ecosystemviewof() };
|
|
23285
|
+
}
|
|
23286
|
+
if (library.fork !== void 0) {
|
|
23287
|
+
const entry = (await memory.getflowlibrary()).find((candidate) => candidate.id === (library.fork?.entryid ?? ""));
|
|
23288
|
+
if (!entry) throw new Error(`No flowlibrary entry matches ${library.fork.entryid ?? ""}.`);
|
|
23289
|
+
const fork = forklibrary({ entry, now });
|
|
23290
|
+
await memory.addworkflowrecord(fork);
|
|
23291
|
+
await audit("library", `The user forked ${entry.manifest.title} version ${entry.manifest.version} into the independent local workflow ${fork.id} with ${fork.steps.length} steps; the fork owns its name from its creation on and reviews pending.`, { ...session ? { sessionid: session.id } : {} });
|
|
23292
|
+
return { fork, ...await ecosystemviewof() };
|
|
23293
|
+
}
|
|
23294
|
+
if (library.export === true) {
|
|
23295
|
+
const manifests = await memory.exportlibrarymanifests();
|
|
23296
|
+
await audit("library", `The flowlibrary exported its manifest list for audit: ${manifests.length} entr${manifests.length === 1 ? "y" : "ies"} with digest, publisher, version, state and provenance and no step payload.`, { ...session ? { sessionid: session.id } : {} });
|
|
23297
|
+
return { manifests };
|
|
23298
|
+
}
|
|
23299
|
+
throw new Error("The library command carries no browse, entry, grants, import, install, update, remove, fork or export action.");
|
|
23300
|
+
}
|
|
23301
|
+
if (input.sync !== void 0) {
|
|
23302
|
+
const sync = input.sync;
|
|
23303
|
+
if (sync.providers === true) return { providers: syncbridgeproviders() };
|
|
23304
|
+
if (sync.hooks === true) return { hooks: await memory.getsyncbridgehooks() };
|
|
23305
|
+
if (sync.hook?.add !== void 0) {
|
|
23306
|
+
const provider = sync.hook.add.provider === "web" ? "web" : "file";
|
|
23307
|
+
const direction = sync.hook.add.direction === "pull" ? "pull" : sync.hook.add.direction === "push" ? "push" : "both";
|
|
23308
|
+
const hook = syncbridgehookof({ provider, direction, endpoint: sync.hook.add.endpoint ?? "", now });
|
|
23309
|
+
await memory.setsyncbridgehooks([...await memory.getsyncbridgehooks(), hook]);
|
|
23310
|
+
await audit("syncbridge", `The user added the ${provider} hook of ${hook.endpoint} with the direction ${direction}; the hook starts opted out because no hook ever defaults on.`, { ...session ? { sessionid: session.id } : {} });
|
|
23311
|
+
return { hook, hooks: await memory.getsyncbridgehooks() };
|
|
23312
|
+
}
|
|
23313
|
+
if (sync.hook?.optin !== void 0) {
|
|
23314
|
+
const hooks = await memory.getsyncbridgehooks();
|
|
23315
|
+
const hook = hooks.find((candidate) => candidate.id === sync.hook?.optin?.hookid);
|
|
23316
|
+
if (!hook) throw new Error(`No syncbridge hook matches ${sync.hook.optin.hookid ?? ""}.`);
|
|
23317
|
+
const flipped = syncbridgeoptinflip(hook, sync.hook.optin.optin === true);
|
|
23318
|
+
await memory.setsyncbridgehooks(hooks.map((candidate) => candidate.id === hook.id ? flipped : candidate));
|
|
23319
|
+
await audit("syncbridge", `The user ${flipped.optin ? "opted the" : "opted the"} ${hook.provider} hook of ${hook.endpoint} ${flipped.optin ? "in" : "out"}; the explicit opt in stays the only switch and no hook ever defaults on.`, { ...session ? { sessionid: session.id } : {} });
|
|
23320
|
+
return { hook: flipped, hooks: await memory.getsyncbridgehooks() };
|
|
23321
|
+
}
|
|
23322
|
+
if (sync.pull !== void 0) {
|
|
23323
|
+
const hooks = await memory.getsyncbridgehooks();
|
|
23324
|
+
const hook = hooks.find((candidate) => candidate.id === sync.pull?.hookid);
|
|
23325
|
+
if (!hook) throw new Error(`No syncbridge hook matches ${sync.pull.hookid ?? ""}.`);
|
|
23326
|
+
const optingate = syncbridgeoptingate({ optin: hook.optin });
|
|
23327
|
+
if (!optingate.allowed) throw new Error(optingate.reason);
|
|
23328
|
+
if (hook.provider === "web") return { stub: webproviderstub(hook) };
|
|
23329
|
+
const validation = syncbridgevalidate(sync.pull.payload ?? {});
|
|
23330
|
+
if (!validation.ok) throw new Error(validation.reason);
|
|
23331
|
+
const payload = sync.pull.payload;
|
|
23332
|
+
const entries = await memory.getflowlibrary();
|
|
23333
|
+
const local = [];
|
|
23334
|
+
for (const entry of entries) local.push({ manifestid: entry.manifest.id, version: entry.manifest.version, digest: entry.digest });
|
|
23335
|
+
const remote = [];
|
|
23336
|
+
for (const manifest of payload.manifests) remote.push({ manifestid: manifest.id, version: manifest.version, digest: await syncdigestof(manifest) });
|
|
23337
|
+
const scan = await syncbridgescan({ hook, local, remote, now });
|
|
23338
|
+
for (const manifest of payload.manifests) {
|
|
23339
|
+
if (scan.synced.includes(manifest.id)) {
|
|
23340
|
+
const entry = await libraryentryof({ manifest, provenance: `Pulled from the ${hook.provider} hook of ${hook.endpoint}`, now });
|
|
23341
|
+
await memory.addlibraryentry(entry);
|
|
23342
|
+
}
|
|
23343
|
+
}
|
|
23344
|
+
for (const conflict of scan.conflicts) await memory.addsyncbridgeconflict(conflict);
|
|
23345
|
+
await memory.setsyncbridgehooks(hooks.map((candidate) => candidate.id === hook.id ? { ...candidate, state: "idle", lastsyncat: now } : candidate));
|
|
23346
|
+
await audit("syncbridge", `The file hook of ${hook.endpoint} pulled ${payload.manifests.length} manifest${payload.manifests.length === 1 ? "" : "s"}: ${scan.synced.length} moved while ${scan.conflicts.length} conflict${scan.conflicts.length === 1 ? "" : "s"} surfaced both versions instead of a silent overwrite.`, { ...session ? { sessionid: session.id } : {} });
|
|
23347
|
+
return { scan, ...await ecosystemviewof() };
|
|
23348
|
+
}
|
|
23349
|
+
if (sync.push !== void 0) {
|
|
23350
|
+
const hooks = await memory.getsyncbridgehooks();
|
|
23351
|
+
const hook = hooks.find((candidate) => candidate.id === sync.push?.hookid);
|
|
23352
|
+
if (!hook) throw new Error(`No syncbridge hook matches ${sync.push.hookid ?? ""}.`);
|
|
23353
|
+
const optingate = syncbridgeoptingate({ optin: hook.optin });
|
|
23354
|
+
if (!optingate.allowed) throw new Error(optingate.reason);
|
|
23355
|
+
if (hook.provider === "web") return { stub: webproviderstub(hook) };
|
|
23356
|
+
const payload = await syncbridgeexportpayload((await memory.getflowlibrary()).map((entry) => entry.manifest));
|
|
23357
|
+
await memory.setsyncbridgehooks(hooks.map((candidate) => candidate.id === hook.id ? { ...candidate, state: "idle", lastsyncat: now } : candidate));
|
|
23358
|
+
await audit("syncbridge", `The file hook of ${hook.endpoint} pushed ${payload.manifests.length} manifest${payload.manifests.length === 1 ? "" : "s"} with its digest list; secrets and logs never ride the bridge under any flag.`, { ...session ? { sessionid: session.id } : {} });
|
|
23359
|
+
return { payload, providers: syncbridgeproviders() };
|
|
23360
|
+
}
|
|
23361
|
+
if (sync.conflicts === true) return { conflicts: await memory.getsyncbridgeconflicts() };
|
|
23362
|
+
if (sync.resolve !== void 0) {
|
|
23363
|
+
const resolution = sync.resolve.resolution === "local" ? "local" : sync.resolve.resolution === "remote" ? "remote" : "merge";
|
|
23364
|
+
const conflicts = await memory.resolvesyncbridgeconflict(sync.resolve.conflictid ?? "", resolution, now);
|
|
23365
|
+
await audit("syncbridge", `The user resolved the syncbridge conflict ${sync.resolve.conflictid} with ${resolution}; one distinct action resolved the conflict and both versions stayed on record.`, { ...session ? { sessionid: session.id } : {} });
|
|
23366
|
+
return { conflicts };
|
|
23367
|
+
}
|
|
23368
|
+
throw new Error("The sync command carries no providers, hooks, hook, pull, push, conflicts or resolve action.");
|
|
23369
|
+
}
|
|
23370
|
+
if (input.attention !== void 0) {
|
|
23371
|
+
if (input.attention.dismiss !== void 0) {
|
|
23372
|
+
const entries2 = await memory.dismissattentionentry(input.attention.dismiss.id ?? "");
|
|
23373
|
+
await updatestatusbadge();
|
|
23374
|
+
await audit("attention", `The user dismissed the attention entry ${input.attention.dismiss.id ?? ""}; the dismissal removes the feed row only while the waiting cause keeps its own resolution path.`, { ...session ? { sessionid: session.id } : {} });
|
|
23375
|
+
return { attention: rankattention(entries2).map((entry) => ({ id: entry.id, cause: entry.cause, severity: entry.severity, runid: entry.runid, summary: entry.summary, deeplink: entry.deeplink, at: entry.at })) };
|
|
23376
|
+
}
|
|
23377
|
+
const entries = rankattention(await memory.getattentionentries());
|
|
23378
|
+
const pruned = await memory.pruneattentionentries(now);
|
|
23379
|
+
if (pruned.pruned.length > 0) await audit("attention", `The attentionfeed pruned ${pruned.pruned.length} entr${pruned.pruned.length === 1 ? "y" : "ies"} past the user configured retention window while the resolutions stay in the audit trail.`, {});
|
|
23380
|
+
await updatestatusbadge();
|
|
23381
|
+
return { attention: entries.map((entry) => ({ id: entry.id, cause: entry.cause, severity: entry.severity, runid: entry.runid, summary: entry.summary, deeplink: entry.deeplink, at: entry.at })), notifications: attentionnotifications(entries, now) };
|
|
23382
|
+
}
|
|
23383
|
+
if (input.replay !== void 0) {
|
|
23384
|
+
if (input.replay.open !== void 0) {
|
|
23385
|
+
const runid = input.replay.open.runid?.trim() ?? "";
|
|
23386
|
+
if (runid === "") throw new Error("The runreplay needs its recorded run id.");
|
|
23387
|
+
const log = await memory.getimmutablelog(runid);
|
|
23388
|
+
if (!log) throw new Error(`No sealed run log matches ${runid}.`);
|
|
23389
|
+
const report = await chainreportof(log);
|
|
23390
|
+
const gate = runreplaygate({ sealed: log.seal !== void 0, chainvalid: report.valid });
|
|
23391
|
+
if (!gate.allowed) throw new Error(gate.reason);
|
|
23392
|
+
const read = await readverifiedlog(log);
|
|
23393
|
+
const gates = (await memory.getgates()).map((record2) => ({ gateid: record2.gateid, kind: record2.kind, resolution: record2.state === "resolved" ? "approve" : record2.state === "refused" ? "refuse" : "waiting", at: record2.openedat }));
|
|
23394
|
+
const cursors = await memory.getreplaycursors();
|
|
23395
|
+
const stored = cursors[runid];
|
|
23396
|
+
const sessionbuilt = runreplaysessionof({ runid, entries: read.entries, gates, now });
|
|
23397
|
+
const replay = stored === void 0 ? sessionbuilt : { ...sessionbuilt, cursor: Math.min(stored.cursor, sessionbuilt.steps.length - 1), playing: stored.playing };
|
|
23398
|
+
const current = replay.steps[replay.cursor] ?? replay.steps[0];
|
|
23399
|
+
await audit("runreplay", `The runreplay opened the sealed run ${runid} whose chain verified across ${read.entries.length} entries; the replay walks it read only with the cursor at step ${replay.cursor} and every viewer action records here.`, { ...session ? { sessionid: session.id } : {} });
|
|
23400
|
+
return { replay, ...current !== void 0 ? { restored: replayrestoredview(current) } : {} };
|
|
23401
|
+
}
|
|
23402
|
+
if (input.replay.move !== void 0 || input.replay.jump !== void 0 || input.replay.play !== void 0) {
|
|
23403
|
+
const runid = (input.replay.move?.runid ?? input.replay.jump?.runid ?? input.replay.play?.runid ?? "").trim();
|
|
23404
|
+
if (runid === "") throw new Error("The runreplay action needs its recorded run id.");
|
|
23405
|
+
const log = await memory.getimmutablelog(runid);
|
|
23406
|
+
if (!log) throw new Error(`No sealed run log matches ${runid}.`);
|
|
23407
|
+
const report = await chainreportof(log);
|
|
23408
|
+
const gate = runreplaygate({ sealed: log.seal !== void 0, chainvalid: report.valid });
|
|
23409
|
+
if (!gate.allowed) throw new Error(gate.reason);
|
|
23410
|
+
const read = await readverifiedlog(log);
|
|
23411
|
+
const gates = (await memory.getgates()).map((record2) => ({ gateid: record2.gateid, kind: record2.kind, resolution: record2.state === "resolved" ? "approve" : record2.state === "refused" ? "refuse" : "waiting", at: record2.openedat }));
|
|
23412
|
+
let replay = runreplaysessionof({ runid, entries: read.entries, gates, now });
|
|
23413
|
+
const cursors = await memory.getreplaycursors();
|
|
23414
|
+
const stored = cursors[runid];
|
|
23415
|
+
if (stored !== void 0) replay = { ...replay, cursor: Math.min(stored.cursor, replay.steps.length - 1), playing: stored.playing };
|
|
23416
|
+
if (input.replay.move !== void 0) replay = replaymove(replay, input.replay.move.direction === "backward" ? "backward" : "forward", now);
|
|
23417
|
+
if (input.replay.jump !== void 0) replay = replayjump(replay, input.replay.jump.stepid ?? "", now);
|
|
23418
|
+
if (input.replay.play !== void 0) replay = replayplay(replay, input.replay.play.playing === true, now);
|
|
23419
|
+
await memory.setreplaycursor(runid, { cursor: replay.cursor, playing: replay.playing });
|
|
23420
|
+
const current = replay.steps[replay.cursor] ?? replay.steps[0];
|
|
23421
|
+
await audit("runreplay", `The runreplay viewer moved the cursor of ${runid} to step ${replay.cursor}${replay.playing ? " and plays" : ""}; the ${replay.actions[replay.actions.length - 1]?.kind ?? "step"} action records with its time and the replay touched no page state.`, { ...session ? { sessionid: session.id } : {} });
|
|
23422
|
+
return { replay, ...current !== void 0 ? { restored: replayrestoredview(current) } : {} };
|
|
23423
|
+
}
|
|
23424
|
+
throw new Error("The replay command carries no open, move, jump or play action.");
|
|
23425
|
+
}
|
|
23426
|
+
if (input.compare !== void 0 && input.compare.open !== void 0) {
|
|
23427
|
+
const runida = input.compare.open.runida?.trim() ?? "";
|
|
23428
|
+
const runidb = input.compare.open.runidb?.trim() ?? "";
|
|
23429
|
+
if (runida === "" || runidb === "") throw new Error("The outputcompare needs both run ids.");
|
|
23430
|
+
const runa = (await memory.listworkflowruns()).find((run) => run.id === runida);
|
|
23431
|
+
const runb = (await memory.listworkflowruns()).find((run) => run.id === runidb);
|
|
23432
|
+
if (!runa || !runb) throw new Error("The outputcompare needs the stored runs of both run ids.");
|
|
23433
|
+
const recorda = await memory.getworkflowrecord(runa.workflowid);
|
|
23434
|
+
const recordb = await memory.getworkflowrecord(runb.workflowid);
|
|
23435
|
+
if (!recorda || !recordb) throw new Error("The outputcompare needs the composed workflows of both runs.");
|
|
23436
|
+
const logsa = await memory.getrunlog(runida);
|
|
23437
|
+
const logsb = await memory.getrunlog(runidb);
|
|
23438
|
+
const signaturea = taskinputsignatureof({ objective: recorda.name, steps: recorda.steps.map((step) => step.kind) });
|
|
23439
|
+
const signatureb = taskinputsignatureof({ objective: recordb.name, steps: recordb.steps.map((step) => step.kind) });
|
|
23440
|
+
const comparegate = outputcomparegate({ signaturea, signatureb });
|
|
23441
|
+
if (!comparegate.allowed) throw new Error(comparegate.reason);
|
|
23442
|
+
const readonlygate = outputcomparereadonlygate({ executessteps: false });
|
|
23443
|
+
if (!readonlygate.allowed) throw new Error(readonlygate.reason);
|
|
23444
|
+
const sessionbuilt = outputcomparesessionof({ runids: [runida, runidb], logsa, logsb, now });
|
|
23445
|
+
await memory.addcomparesession(sessionbuilt);
|
|
23446
|
+
const metrics = comparesessionmetrics(sessionbuilt);
|
|
23447
|
+
await audit("outputcompare", `The outputcompare opened the runs ${runida} and ${runidb} that share their step sequence: ${metrics.reason} The comparison read the stored runlog outcomes only and executed no step.`, { ...session ? { sessionid: session.id } : {} });
|
|
23448
|
+
return { session: sessionbuilt, metrics, view: outputcompareview(sessionbuilt) };
|
|
23449
|
+
}
|
|
23450
|
+
if (input.background !== void 0) {
|
|
23451
|
+
if (input.background.queue !== void 0) {
|
|
23452
|
+
const workflowid = input.background.queue.workflowid?.trim() ?? "";
|
|
23453
|
+
if (workflowid === "") throw new Error("The background run queue needs its workflow id.");
|
|
23454
|
+
const record2 = await memory.getworkflowrecord(workflowid);
|
|
23455
|
+
if (!record2) throw new Error(`No composed workflow matches ${workflowid}.`);
|
|
23456
|
+
const reviewgate = runreviewgranted(record2);
|
|
23457
|
+
if (!reviewgate.allowed) throw new Error(reviewgate.reason ?? "The background run queue executes reviewed workflows only.");
|
|
23458
|
+
const queue = enqueuebackgroundrun({ queue: await memory.getbackgroundqueue(), workflowid, summary: input.background.queue.summary ?? `Background run of ${record2.name}`, now });
|
|
23459
|
+
await memory.setbackgroundqueue(queue);
|
|
23460
|
+
await audit("backgroundrun", `The user queued the workflow ${record2.name} for a background run; the queue holds ${queue.filter((entry) => entry.state === "queued").length} waiting entr${queue.filter((entry) => entry.state === "queued").length === 1 ? "y" : "ies"} and the executor picks the oldest first.`, { ...session ? { sessionid: session.id } : {} });
|
|
23461
|
+
void advancebackgroundqueue().catch(() => {
|
|
23462
|
+
});
|
|
23463
|
+
return { queue: backgroundrunsview(await memory.getbackgroundqueue()) };
|
|
23464
|
+
}
|
|
23465
|
+
if (input.background.cancel !== void 0) {
|
|
23466
|
+
const cancelled = cancelbackgroundentry(await memory.getbackgroundqueue(), input.background.cancel.id ?? "");
|
|
23467
|
+
await memory.setbackgroundqueue(cancelled.queue);
|
|
23468
|
+
await audit("backgroundrun", `The user cancelled ${cancelled.cancelled.length} queued background run${cancelled.cancelled.length === 1 ? "" : "s"}; a running entry keeps its executor path.`, { ...session ? { sessionid: session.id } : {} });
|
|
23469
|
+
return { queue: backgroundrunsview(cancelled.queue) };
|
|
23470
|
+
}
|
|
23471
|
+
await advancebackgroundqueue();
|
|
23472
|
+
return { queue: backgroundrunsview(await memory.getbackgroundqueue()), tray: backgroundtrayrows(await memory.getbackgroundqueue(), session?.origin ?? "") };
|
|
23473
|
+
}
|
|
23474
|
+
if (input.settings !== void 0) {
|
|
23475
|
+
const next = { ...settings ?? {}, ...input.settings.attentionretention !== void 0 ? { attentionretention: input.settings.attentionretention } : {} };
|
|
23476
|
+
await memory.setsettings(next);
|
|
23477
|
+
const pruned = await memory.pruneattentionentries(now);
|
|
23478
|
+
await audit("attention", `The user set the attentionfeed retention window${input.settings.attentionretention !== void 0 ? ` to ${input.settings.attentionretention} milliseconds` : ""}; ${pruned.pruned.length} entr${pruned.pruned.length === 1 ? "y" : "ies"} left the feed while the resolutions stay in the audit trail.`, { ...session ? { sessionid: session.id } : {} });
|
|
23479
|
+
return { settings: next };
|
|
23480
|
+
}
|
|
23481
|
+
return ecosystemviewof();
|
|
23482
|
+
}
|
|
23483
|
+
async function advancebackgroundqueue() {
|
|
23484
|
+
const now = Date.now();
|
|
23485
|
+
let queue = await memory.getbackgroundqueue();
|
|
23486
|
+
if (queue.length === 0) return;
|
|
23487
|
+
const session = await memory.getsession();
|
|
23488
|
+
for (const entry of queue.filter((candidate) => candidate.state === "running")) {
|
|
23489
|
+
if (await runofworkflowactive(entry.workflowid)) continue;
|
|
23490
|
+
const runs = (await memory.listworkflowruns()).filter((run) => run.workflowid === entry.workflowid).sort((a, b) => b.startedat - a.startedat);
|
|
23491
|
+
const last = runs[0];
|
|
23492
|
+
const state = last?.state === "failed" ? "failed" : "done";
|
|
23493
|
+
queue = queue.map((candidate) => candidate.id === entry.id ? finishbackgroundrun(candidate, state, now) : candidate);
|
|
23494
|
+
if (session) await appendrunevent("background", `The background run of ${entry.workflowid} ${state === "done" ? "completed" : "failed"} and released its keepalive hold.`, session, session.origin).catch(() => {
|
|
23495
|
+
});
|
|
23496
|
+
await audit("backgroundrun", `The background run of ${entry.workflowid} ${state === "done" ? "completed" : "failed"} and released its keepalive hold${state === "failed" ? "; the attentionfeed carries the failure with its deep link" : ""}.`, { ...session ? { sessionid: session.id } : {} });
|
|
23497
|
+
if (state === "failed") await recordattention("failure", entry.id, session?.origin ?? "", `The background run of ${entry.workflowid} failed: ${entry.summary}`).catch(() => {
|
|
23498
|
+
});
|
|
23499
|
+
}
|
|
23500
|
+
await memory.setbackgroundqueue(queue);
|
|
23501
|
+
if (queue.some((entry) => entry.state === "running")) return;
|
|
23502
|
+
const next = nextbackgroundrun(queue);
|
|
23503
|
+
if (next === void 0) return;
|
|
23504
|
+
const record2 = await memory.getworkflowrecord(next.workflowid);
|
|
23505
|
+
if (!record2) return;
|
|
23506
|
+
const reviewgate = runreviewgranted(record2);
|
|
23507
|
+
const begun = beginbackgroundrun(next, now, reviewgate.allowed);
|
|
23508
|
+
if (!begun.gate.allowed) {
|
|
23509
|
+
await audit("backgroundrun", `The background run of ${record2.name} stayed queued: ${begun.gate.reason}`, {});
|
|
23510
|
+
return;
|
|
23511
|
+
}
|
|
23512
|
+
queue = queue.map((candidate) => candidate.id === next.id ? begun.entry : candidate);
|
|
23513
|
+
await memory.setbackgroundqueue(queue);
|
|
23514
|
+
if (!session || session.stoppedat || session.pausedat || session.expiresat <= now) return;
|
|
23515
|
+
const plan = await memory.getplan();
|
|
23516
|
+
if (!plan || plan.state !== "approved") return;
|
|
23517
|
+
let granted = true;
|
|
23518
|
+
for (const workfloworigin of record2.origins) {
|
|
23519
|
+
if (!origingranted(session, workfloworigin)) granted = false;
|
|
23520
|
+
}
|
|
23521
|
+
if (!granted) return;
|
|
23522
|
+
startkeepaliveport(next.workflowid);
|
|
23523
|
+
await appendrunevent("background", `The background run queue started the workflow ${record2.name} version ${record2.version} with ${record2.steps.length} reviewed steps and holds the keepalive signal for its duration.`, session, session.origin).catch(() => {
|
|
23524
|
+
});
|
|
23525
|
+
await audit("backgroundrun", `The background run queue started the workflow ${record2.name} version ${record2.version} with ${record2.steps.length} reviewed steps; the keepalive signal holds for the whole run and every checkpoint restores it on each worker wake.`, { sessionid: session.id, planid: plan.id });
|
|
23526
|
+
const runstep2 = { id: `background${next.id}`, kind: "runworkflow", summary: `The background queue run of ${record2.name}`, risk: "sensitive", options: JSON.stringify({ workflowid: next.workflowid, reviewed: true, background: true, variables: { backgroundqueue: next.id } }) };
|
|
23527
|
+
const output = await executeworkflowstep(runstep2, session, plan, session.tabid, session.origin).catch((error) => ({ ok: false, summary: error instanceof Error ? error.message : String(error), details: {} }));
|
|
23528
|
+
if (!output.ok) {
|
|
23529
|
+
queue = queue.map((candidate) => candidate.id === next.id ? finishbackgroundrun(candidate, "failed", Date.now()) : candidate);
|
|
23530
|
+
await memory.setbackgroundqueue(queue);
|
|
23531
|
+
await recordattention("failure", next.id, session.origin, `The background run of ${record2.name} failed to start: ${output.summary}`).catch(() => {
|
|
23532
|
+
});
|
|
23533
|
+
await audit("backgroundrun", `The background run of ${record2.name} failed to start: ${output.summary}.`, { sessionid: session.id, planid: plan.id });
|
|
23534
|
+
}
|
|
23535
|
+
await refreshbadge().catch(() => {
|
|
23536
|
+
});
|
|
23537
|
+
}
|
|
23538
|
+
async function recoverbackgroundqueue() {
|
|
23539
|
+
const recovered = resumebackgroundqueue(await memory.getbackgroundqueue(), Date.now());
|
|
23540
|
+
if (recovered.requeued.length > 0) {
|
|
23541
|
+
await memory.setbackgroundqueue(recovered.queue);
|
|
23542
|
+
await audit("backgroundrun", `The worker restart requeued ${recovered.requeued.length} interrupted background run${recovered.requeued.length === 1 ? "" : "s"}; the queue state persisted for exactly this recovery.`, {});
|
|
23543
|
+
}
|
|
23544
|
+
await advancebackgroundqueue().catch(() => {
|
|
23545
|
+
});
|
|
23546
|
+
}
|
|
23547
|
+
async function handlesurfaceviewcommand(message) {
|
|
23548
|
+
const input = message;
|
|
23549
|
+
const now = Date.now();
|
|
23550
|
+
const session = await memory.getsession();
|
|
23551
|
+
const sessionactive = Boolean(session && !session.stoppedat && session.expiresat > now);
|
|
23552
|
+
const settings = await memory.getsettings();
|
|
23553
|
+
const plan = await memory.getplan();
|
|
23554
|
+
const grantedorigins = session ? [.../* @__PURE__ */ new Set([session.origin, ...session.grants ?? []])] : [];
|
|
23555
|
+
const surfaceof = (value, fallback) => ["popup", "sidepanel", "dashboardpage", "optionspage", "onboarding", "omnibox", "page"].includes(value ?? "") ? value : fallback;
|
|
23556
|
+
if (input.datagrid !== void 0) {
|
|
23557
|
+
if (input.datagrid.view !== void 0) {
|
|
23558
|
+
const rows = input.datagrid.view.rows ?? [];
|
|
23559
|
+
if (rows.length === 0) throw new Error("The datagrid view needs at least one extracted row.");
|
|
23560
|
+
const view = datagridof({ title: input.datagrid.view.title ?? "Extraction result", origin: session?.origin ?? "", runid: plan?.id ?? "", rows, at: now });
|
|
23561
|
+
await audit("datagrid", `The user opened the datagrid ${view.id} of ${view.rows.length} row${view.rows.length === 1 ? "" : "s"} and ${view.columns.length} inferred column${view.columns.length === 1 ? "" : "s"}; the grid previews the extraction result before any export.`, { ...session ? { sessionid: session.id } : {} });
|
|
23562
|
+
return { view, menu: exportmenudescriptors() };
|
|
23563
|
+
}
|
|
23564
|
+
if (input.datagrid.sort !== void 0) return { hint: "The datagrid sort runs inside the surface module on the opened view; the sort needs its view id and its column field." };
|
|
23565
|
+
if (input.datagrid.filter !== void 0) return { hint: "The datagrid filter runs locally inside the surface module; no row ever leaves the surface to filter." };
|
|
23566
|
+
if (input.datagrid.select !== void 0) return { hint: "The row range selection marks the partial export scope inside the surface module." };
|
|
23567
|
+
}
|
|
23568
|
+
if (input.export !== void 0) {
|
|
23569
|
+
if (input.export.menu === true) return { menu: exportmenudescriptors() };
|
|
23570
|
+
if (input.export.run !== void 0) {
|
|
23571
|
+
const format = input.export.run.format === "json" ? "json" : input.export.run.format === "clipboard" ? "clipboard" : "csv";
|
|
23572
|
+
const scope = input.export.run.scope === "selection" ? "selection" : input.export.run.scope === "step" ? "step" : "run";
|
|
23573
|
+
await audit("datagrid", `The user ran the ${format} export of the ${scope} scope; the masked values only rule honors the maskinputs verdicts of every sensitive field shape.`, { ...session ? { sessionid: session.id } : {} });
|
|
23574
|
+
return { hint: "The export renders inside the surface module on the datagrid view the surface holds; the background never rebuilds the masked values twice.", format, scope };
|
|
23575
|
+
}
|
|
23576
|
+
}
|
|
23577
|
+
if (input.quickaction !== void 0) {
|
|
23578
|
+
const origin = input.quickaction.list?.origin?.trim() || session?.origin || "";
|
|
23579
|
+
const capabilities = await grantedcapabilities();
|
|
23580
|
+
const actions = quickactionsfor(quickactioncatalog(), { origin, granted: grantedorigins, sessionactive, grantedcapabilities: capabilities });
|
|
23581
|
+
if (input.quickaction.surface === true) {
|
|
23582
|
+
await audit("quickaction", `The ${origin || "originless"} tab lists ${actions.length} permitted quickaction${actions.length === 1 ? "" : "s"}; only the actions the origin allowlist and the capability set permit register.`, { ...session ? { sessionid: session.id } : {} });
|
|
23583
|
+
}
|
|
23584
|
+
return { actions, origin };
|
|
23585
|
+
}
|
|
23586
|
+
if (input.shortcut !== void 0) {
|
|
23587
|
+
const stored = await memory.getshortcutbindings();
|
|
23588
|
+
const bindings = stored.length > 0 ? stored : shortcutdefaults();
|
|
23589
|
+
if (input.shortcut.edit !== void 0) {
|
|
23590
|
+
const command = input.shortcut.edit.command?.trim() ?? "";
|
|
23591
|
+
const text2 = input.shortcut.edit.text?.trim() ?? "";
|
|
23592
|
+
const edited = shortcutbindingafter(bindings, command, text2);
|
|
23593
|
+
await memory.setshortcutbindings(edited);
|
|
23594
|
+
await audit("shortcut", `The user edited the ${command} shortcut to ${text2}; the binding stays user editable and the command keeps its palette gates.`, { ...session ? { sessionid: session.id } : {} });
|
|
23595
|
+
return { bindings: edited };
|
|
23596
|
+
}
|
|
23597
|
+
if (input.shortcut.match !== void 0) {
|
|
23598
|
+
const key = input.shortcut.match.key ?? "";
|
|
23599
|
+
const modifiers = input.shortcut.match.modifiers ?? [];
|
|
23600
|
+
const surface = surfaceof(input.shortcut.match.surface, "popup");
|
|
23601
|
+
const command = shortcutcommandof(bindings, { key, modifiers, surface });
|
|
23602
|
+
if (command !== void 0) {
|
|
23603
|
+
const entries = palettecommandsof(surfacepalette(), { granted: await grantedcapabilities(), sessionactive });
|
|
23604
|
+
const dispatchable = shortcutdispatchable(command, entries, { granted: await grantedcapabilities(), sessionactive });
|
|
23605
|
+
await audit("shortcut", `The ${surface} pressed ${[...modifiers, key].join("+")} and the ${command} command matched${dispatchable ? "; the palette action gate allows the dispatch" : "; the palette action gate refuses the dispatch"}.`, { ...session ? { sessionid: session.id } : {} });
|
|
23606
|
+
return { command, dispatchable };
|
|
23607
|
+
}
|
|
23608
|
+
return { command: void 0, dispatchable: false };
|
|
23609
|
+
}
|
|
23610
|
+
return { bindings: bindings.map((binding) => ({ ...binding, display: shortcuttext(binding) })) };
|
|
23611
|
+
}
|
|
23612
|
+
if (input.omnibox !== void 0 && input.omnibox.parse !== void 0) {
|
|
23613
|
+
const text2 = input.omnibox.parse.text ?? "";
|
|
23614
|
+
const origin = session?.origin ?? "";
|
|
23615
|
+
const gate = omniboxtaskgate({ text: text2, origin, direct: false });
|
|
23616
|
+
if (!gate.allowed) throw new Error(gate.reason);
|
|
23617
|
+
const submission = parseomniboxtask({ text: text2, origin, at: now });
|
|
23618
|
+
const taskinput = omniboxtasktotaskinput(submission);
|
|
23619
|
+
await memory.addtaskinput(taskinput);
|
|
23620
|
+
const proposed = await propose(taskinput.text, false);
|
|
23621
|
+
await audit("omnibox", `The omnibox keyword parsed into the taskinput ${submission.id} for ${origin} and routed through the same proposal flow as the api; the plan ${proposed.id} awaits its plancard review.`, { ...session ? { sessionid: session.id } : {}, planid: proposed.id });
|
|
23622
|
+
await broadcastsurfaceframe({ channel: "runstate", surface: "omnibox", summary: `An omnibox goal became the plan ${proposed.id} and awaits review.` });
|
|
23623
|
+
return { submission, plan: proposed };
|
|
23624
|
+
}
|
|
23625
|
+
if (input.badge !== void 0) {
|
|
23626
|
+
await updatestatusbadge();
|
|
23627
|
+
const progress = await memory.getprogress();
|
|
23628
|
+
const waitingcount = plan !== void 0 && progress !== void 0 && progress.planid === plan.id ? Object.keys(progress.gatewaits ?? {}).length : 0;
|
|
23629
|
+
const state = statusbadgeof({ ...plan !== void 0 && (plan.state === "pending" || plan.state === "approved") ? { planstate: plan.state } : {}, waitingcount, ...plan !== void 0 ? { runid: plan.id } : {} });
|
|
23630
|
+
return { badge: state, text: badgetextof(state), color: badgecolorof(state) };
|
|
23631
|
+
}
|
|
23632
|
+
if (input.notify !== void 0) {
|
|
23633
|
+
const prefs = await memory.getnotificationprefs();
|
|
23634
|
+
if (input.notify.consent !== void 0 || input.notify.enabled !== void 0) {
|
|
23635
|
+
const next = { consent: input.notify.consent === true || prefs?.consent === true, enabled: input.notify.enabled === void 0 ? prefs?.enabled !== false : input.notify.enabled };
|
|
23636
|
+
await memory.setnotificationprefs(next);
|
|
23637
|
+
await audit("notify", `The user set the notification preference${input.notify.consent !== void 0 ? ` with the page content consent ${next.consent ? "granted" : "withheld"}` : ""}${input.notify.enabled !== void 0 ? ` and the notifications ${next.enabled ? "on" : "off"}` : ""}; a content bearing body never shows without its consent.`, { ...session ? { sessionid: session.id } : {} });
|
|
23638
|
+
return { prefs: next };
|
|
23639
|
+
}
|
|
23640
|
+
if (input.notify.done !== void 0) {
|
|
23641
|
+
const runid = input.notify.done.runid?.trim() || plan?.id || "";
|
|
23642
|
+
if (runid === "") throw new Error("The done notification needs its run id.");
|
|
23643
|
+
const payload = notifydoneof({ runid, origin: session?.origin ?? "", summary: input.notify.done.summary ?? "", at: now });
|
|
23644
|
+
await memory.addnotificationhistory(payload);
|
|
23645
|
+
await updatestatusbadge();
|
|
23646
|
+
await broadcastsurfaceframe({ channel: "runstate", surface: "background", summary: `The run ${runid} completed; the deep link ${payload.deeplink} opens its runsummary.` });
|
|
23647
|
+
await audit("notify", `The run ${runid} completed and its done notification carries the deep link ${payload.deeplink} to the runsummary; the body carries no page content so no consent is needed.`, { ...session ? { sessionid: session.id } : {} });
|
|
23648
|
+
return { payload, dnd: notificationrespectsdnd(payload, input.notify.dnd === true) };
|
|
23649
|
+
}
|
|
23650
|
+
if (input.notify.attention !== void 0) {
|
|
23651
|
+
const stepid = input.notify.attention.stepid?.trim() ?? "";
|
|
23652
|
+
if (stepid === "") throw new Error("The attention notification needs its waiting step.");
|
|
23653
|
+
const contentgate = notificationcontentgate({ content: input.notify.attention.content === true, consent: prefs?.consent === true || input.notify.attention.content !== true });
|
|
23654
|
+
if (!contentgate.allowed) throw new Error(contentgate.reason);
|
|
23655
|
+
const payload = notifyattentionof({ runid: input.notify.attention.runid?.trim() || plan?.id || "", stepid, cause: input.notify.attention.cause === "phishguard" ? "phishguard" : input.notify.attention.cause === "deferral" ? "deferral" : "gatewait", reason: input.notify.attention.reason ?? "The run waits for a human.", ...input.notify.attention.content === true ? { content: true } : {}, consent: prefs?.consent === true, at: now });
|
|
23656
|
+
await memory.addnotificationhistory(payload);
|
|
23657
|
+
await updatestatusbadge();
|
|
23658
|
+
await broadcastsurfaceframe({ channel: "runstate", surface: "background", summary: `The step ${stepid} needs attention; the deep link ${payload.deeplink} opens the exact waiting step.` });
|
|
23659
|
+
await audit("notify", `The step ${stepid} of the run ${payload.runid} needs attention (${payload.title.toLowerCase()}); the deep link ${payload.deeplink} opens the exact waiting step${input.notify.dnd === true ? " while the os do not disturb state holds the toast in the history" : ""}.`, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {}, stepid });
|
|
23660
|
+
return { payload, dnd: notificationrespectsdnd(payload, input.notify.dnd === true) };
|
|
23661
|
+
}
|
|
23662
|
+
if (input.notify.history === true) return { history: await memory.getnotificationhistory() };
|
|
23663
|
+
return { prefs: prefs ?? { consent: false, enabled: true } };
|
|
23664
|
+
}
|
|
23665
|
+
if (input.recent !== void 0) {
|
|
23666
|
+
if (input.recent.add !== void 0) {
|
|
23667
|
+
const runid = input.recent.add.runid?.trim() ?? "";
|
|
23668
|
+
if (runid === "") throw new Error("The recenttray entry needs its run id.");
|
|
23669
|
+
const outcome = input.recent.add.outcome === "completed" ? "completed" : input.recent.add.outcome === "halted" ? "halted" : input.recent.add.outcome === "failed" ? "failed" : "running";
|
|
23670
|
+
const entry = recenttrayentryof({ runid, origin: input.recent.add.origin?.trim() || session?.origin || "", outcome, title: input.recent.add.title ?? "", at: now });
|
|
23671
|
+
await memory.addrecenttrayentry(entry);
|
|
23672
|
+
await audit("recent", `The recenttray recorded the run ${runid} with its ${outcome} outcome${settings?.recenttraydepth !== void 0 ? ` inside the user depth of ${settings.recenttraydepth}` : ""}; a halted run offers resume and a completed run offers reopen.`, { ...session ? { sessionid: session.id } : {} });
|
|
23673
|
+
return { tray: await memory.getrecenttray() };
|
|
23674
|
+
}
|
|
23675
|
+
if (input.recent.action !== void 0) {
|
|
23676
|
+
const runid = input.recent.action.runid?.trim() ?? "";
|
|
23677
|
+
const entry = (await memory.getrecenttray()).find((candidate) => candidate.runid === runid);
|
|
23678
|
+
if (entry === void 0) throw new Error(`The recenttray knows no run ${runid}.`);
|
|
23679
|
+
const actions = recenttrayactions(entry);
|
|
23680
|
+
await audit("recent", `The user read the actions of the run ${runid} from the recenttray: ${actions.length === 0 ? "no action offers while the run stays live" : actions.join(" and ")}.`, { ...session ? { sessionid: session.id } : {} });
|
|
23681
|
+
return { actions };
|
|
23682
|
+
}
|
|
23683
|
+
return { tray: await memory.getrecenttray() };
|
|
23684
|
+
}
|
|
23685
|
+
if (input.picker !== void 0) {
|
|
23686
|
+
if (input.picker.start !== void 0) {
|
|
23687
|
+
const candidates = (input.picker.start.candidates ?? []).map((candidate) => pickercandidateof({ selector: candidate.selector ?? "", ...candidate.text !== void 0 ? { text: candidate.text } : {}, ...candidate.role !== void 0 ? { role: candidate.role } : {}, hasid: candidate.hasid === true, hasstableattributes: candidate.hasstableattributes === true, hasrole: candidate.hasrole === true, textunique: candidate.textunique === true }));
|
|
23688
|
+
const picker = pickersessionstart({ origin: session?.origin ?? "", granted: grantedorigins, candidates, at: now });
|
|
23689
|
+
await audit("picker", `The sidepanel started the picker session ${picker.id} on the granted origin ${picker.origin} with ${picker.candidates.length} stability scored candidate${picker.candidates.length === 1 ? "" : "s"}.`, { ...session ? { sessionid: session.id } : {} });
|
|
23690
|
+
return { picker, tips: guidedtips() };
|
|
23691
|
+
}
|
|
23692
|
+
if (input.picker.lock !== void 0) {
|
|
23693
|
+
const stepid = input.picker.lock.stepid?.trim() ?? "";
|
|
23694
|
+
const candidateindex = input.picker.lock.candidateindex ?? 0;
|
|
23695
|
+
if (stepid === "") throw new Error("The candidate lock needs its step.");
|
|
23696
|
+
return { hint: "The candidate lock binds one candidate of the open picker session inside the surface module; the locked selector rides the proposed step for its review.", stepid, candidateindex };
|
|
23697
|
+
}
|
|
23698
|
+
}
|
|
23699
|
+
if (input.halo !== void 0) {
|
|
23700
|
+
const halo = haloof({ stepid: input.halo.stepid?.trim() ?? "", selector: input.halo.selector ?? "", rect: { x: input.halo.rect?.x ?? 0, y: input.halo.rect?.y ?? 0, width: input.halo.rect?.width ?? 0, height: input.halo.rect?.height ?? 0 }, state: input.halo.state === "running" ? "running" : input.halo.state === "waiting" ? "waiting" : input.halo.state === "done" ? "done" : input.halo.state === "failed" ? "failed" : input.halo.state === "halted" ? "halted" : "pending" });
|
|
23701
|
+
return { halo, color: halocolorof(halo.state) };
|
|
23702
|
+
}
|
|
23703
|
+
if (input.tips !== void 0) {
|
|
23704
|
+
const tips = guidedtips();
|
|
23705
|
+
if (input.tips.dismiss !== void 0) {
|
|
23706
|
+
const dismissed = guidedtipdismiss(tips, (await memory.getsurfacelayout("optionspage"))?.preferences.dismissedtips?.split(",").filter((id) => id !== "") ?? [], input.tips.dismiss.tipid?.trim() ?? "");
|
|
23707
|
+
await memory.setsurfacelayout({ surface: "optionspage", preferences: { dismissedtips: dismissed.join(",") }, updatedat: now });
|
|
23708
|
+
await audit("picker", `The user dismissed the ${input.tips.dismiss.tipid} guidedtip; the optionspage recalls every dismissed tip on demand.`, { ...session ? { sessionid: session.id } : {} });
|
|
23709
|
+
return { tips, dismissed };
|
|
23710
|
+
}
|
|
23711
|
+
if (input.tips.recall === true) {
|
|
23712
|
+
const recalled = guidedtiprecall((await memory.getsurfacelayout("optionspage"))?.preferences.dismissedtips?.split(",").filter((id) => id !== "") ?? []);
|
|
23713
|
+
await memory.setsurfacelayout({ surface: "optionspage", preferences: { dismissedtips: "" }, updatedat: now });
|
|
23714
|
+
return { tips, dismissed: recalled };
|
|
23715
|
+
}
|
|
23716
|
+
return { tips, dismissed: (await memory.getsurfacelayout("optionspage"))?.preferences.dismissedtips?.split(",").filter((id) => id !== "") ?? [] };
|
|
23717
|
+
}
|
|
23718
|
+
if (input.shotpanel !== void 0) {
|
|
23719
|
+
if (input.shotpanel.open !== void 0) {
|
|
23720
|
+
const view = shotpanelof({ stepid: input.shotpanel.open.stepid?.trim() ?? "", runid: plan?.id ?? "", captureid: input.shotpanel.open.captureid?.trim() ?? "", provenance: input.shotpanel.open.provenance === "fullpage" ? "fullpage" : input.shotpanel.open.provenance === "element" ? "element" : input.shotpanel.open.provenance === "region" ? "region" : "viewport", origin: session?.origin ?? "", granted: grantedorigins, at: now });
|
|
23721
|
+
await audit("shotpanel", `The user opened the shotpanel of the capture ${view.captureid} (${view.provenance}) of the step ${view.stepid}; the capture origin holds its allowlist entry and the redaction verdicts render beside the preview.`, { ...session ? { sessionid: session.id } : {} });
|
|
23722
|
+
return { view };
|
|
23723
|
+
}
|
|
23724
|
+
return { hint: "The zoom and pan run inside the surface module on the opened shotpanel view." };
|
|
23725
|
+
}
|
|
23726
|
+
if (input.compare !== void 0) {
|
|
23727
|
+
if (input.compare.pair !== void 0) {
|
|
23728
|
+
const pair = comparepairof({ stepid: input.compare.pair.stepid?.trim() ?? "", beforecaptureid: input.compare.pair.beforecaptureid?.trim() ?? "", aftercaptureid: input.compare.pair.aftercaptureid?.trim() ?? "" });
|
|
23729
|
+
return { pair };
|
|
23730
|
+
}
|
|
23731
|
+
return { hint: "The compareviewer pairs the before and after captures of every executed write step inside the surface module; the slider overlays the two captures." };
|
|
23732
|
+
}
|
|
23733
|
+
if (input.siteprofile !== void 0) {
|
|
23734
|
+
if (input.siteprofile.save !== void 0) {
|
|
23735
|
+
const origin = input.siteprofile.save.origin?.trim() || session?.origin || "";
|
|
23736
|
+
const profile = siteprofileof({ origin, ...input.siteprofile.save.theme === "dark" || input.siteprofile.save.theme === "light" || input.siteprofile.save.theme === "system" ? { theme: input.siteprofile.save.theme } : {}, ...input.siteprofile.save.defaultview !== void 0 ? { defaultview: input.siteprofile.save.defaultview } : {}, at: now });
|
|
23737
|
+
const profiles = [...(await memory.listsiteprofiles()).filter((candidate) => candidate.origin !== profile.origin), profile];
|
|
23738
|
+
await memory.setsiteprofile(profile);
|
|
23739
|
+
await memory.setsiteprofiles(profiles);
|
|
23740
|
+
await audit("siteprofile", `The user saved the siteprofile of ${profile.origin}${profile.theme !== void 0 ? ` with the ${profile.theme} theme` : ""}${profile.defaultview !== void 0 ? ` and the ${profile.defaultview} default view` : ""}; the profile adjusts interface preferences only and never a policy gate.`, { ...session ? { sessionid: session.id } : {} });
|
|
23741
|
+
return { profile, profiles };
|
|
23742
|
+
}
|
|
23743
|
+
if (input.siteprofile.get !== void 0) {
|
|
23744
|
+
const origin = input.siteprofile.get.origin?.trim() || session?.origin || "";
|
|
23745
|
+
const profiles = await memory.listsiteprofiles();
|
|
23746
|
+
return { profile: siteprofilefor(profiles, origin), active: profiles.some((profile) => siteprofileactive(profile, origin)) };
|
|
23747
|
+
}
|
|
23748
|
+
return { profiles: await memory.listsiteprofiles() };
|
|
23749
|
+
}
|
|
23750
|
+
if (input.theme !== void 0) {
|
|
23751
|
+
const ospreference = input.theme.ospreference === "dark" ? "dark" : input.theme.ospreference === "light" ? "light" : windowmatchmedia();
|
|
23752
|
+
const preference = input.theme.preference === "dark" || input.theme.preference === "light" || input.theme.preference === "system" ? input.theme.preference : await memory.getthemepreference();
|
|
23753
|
+
if (preference !== void 0 && input.theme.preference !== void 0) await memory.setthemepreference(preference);
|
|
23754
|
+
const profile = session ? siteprofilefor(await memory.listsiteprofiles(), session.origin) : void 0;
|
|
23755
|
+
const appearance = resolveappearance({ ospreference, ...preference !== void 0 ? { useroverride: preference } : {}, ...profile !== void 0 ? { siteprofile: profile } : {} });
|
|
23756
|
+
if (input.theme.preference !== void 0) await audit("theme", `The user set the ${preference} theme preference; the resolved appearance stays ${appearance.mode} from ${appearance.source} and the tokens cover every surface including the dashboardpage.`, { ...session ? { sessionid: session.id } : {} });
|
|
23757
|
+
return { appearance, ospreference, ...preference !== void 0 ? { preference } : {} };
|
|
23758
|
+
}
|
|
23759
|
+
if (input.locale !== void 0) {
|
|
23760
|
+
const bundles = localebundles();
|
|
23761
|
+
const language = settings?.uilanguage ?? "en";
|
|
23762
|
+
if (input.locale.string !== void 0) return { value: localestring(bundles, language, input.locale.string.key ?? "") };
|
|
23763
|
+
if (input.locale.format !== void 0) {
|
|
23764
|
+
const kind = input.locale.format.kind === "date" ? "date" : input.locale.format.kind === "duration" ? "duration" : "number";
|
|
23765
|
+
return { value: localeformat({ language, value: input.locale.format.value ?? 0, kind }) };
|
|
23766
|
+
}
|
|
23767
|
+
return { bundles, languages: supportedlanguages(bundles), language };
|
|
23768
|
+
}
|
|
23769
|
+
if (input.importexport !== void 0) {
|
|
23770
|
+
if (input.importexport.export === true) {
|
|
23771
|
+
const payload = importexportpayloadof({ profile: "profile", originprofiles: [], siteprofiles: (await memory.listsiteprofiles()).map((profile) => ({ origin: profile.origin, ...profile.theme !== void 0 ? { theme: profile.theme } : {}, ...profile.defaultview !== void 0 ? { defaultview: profile.defaultview } : {} })), notes: (await memory.getsitenotes()).map((note) => ({ origin: note.origin, title: note.title, sensitive: note.sensitive })), preferences: { ...settings ?? {} }, at: now });
|
|
23772
|
+
await audit("importexport", `The user exported the settings bundle with ${payload.contents.originprofiles.length} origin profile${payload.contents.originprofiles.length === 1 ? "" : "s"}, ${payload.contents.siteprofiles.length} site profile${payload.contents.siteprofiles.length === 1 ? "" : "s"} and ${payload.contents.notes.length} note${payload.contents.notes.length === 1 ? "" : "s"}; ${payload.exclusions.join(" and ")} never enter any bundle.`, { ...session ? { sessionid: session.id } : {} });
|
|
23773
|
+
return { payload };
|
|
23774
|
+
}
|
|
23775
|
+
if (input.importexport.validate !== void 0) {
|
|
23776
|
+
const validation = importexportvalidate(input.importexport.validate);
|
|
23777
|
+
await audit("importexport", `The import bundle validation ${validation.ok ? "passed" : "refused"}: ${validation.reason}`, { ...session ? { sessionid: session.id } : {} });
|
|
23778
|
+
return validation;
|
|
23779
|
+
}
|
|
23780
|
+
if (input.importexport.apply !== void 0) {
|
|
23781
|
+
const current = settings ?? {};
|
|
23782
|
+
const applied = applyimport(input.importexport.apply, current);
|
|
23783
|
+
await memory.setsettings(applied.preferences);
|
|
23784
|
+
await audit("importexport", `The user imported ${applied.applied.length} preference key${applied.applied.length === 1 ? "" : "s"}; the secrets exclusion list stays untouched because no secret ever rides a bundle.`, { ...session ? { sessionid: session.id } : {} });
|
|
23785
|
+
await broadcastsurfaceframe({ channel: "settings", surface: "optionspage", summary: "An importexport bundle applied its preferences." });
|
|
23786
|
+
return applied;
|
|
23787
|
+
}
|
|
23788
|
+
}
|
|
23789
|
+
if (input.dropimport !== void 0 && input.dropimport.file !== void 0) {
|
|
23790
|
+
const sessionfile = dropimportof({ filename: input.dropimport.file.filename ?? "", bytes: input.dropimport.file.bytes ?? 0, head: input.dropimport.file.head ?? "", at: now });
|
|
23791
|
+
await audit("dropimport", `The user dropped ${sessionfile.filename} (${sessionfile.bytes} byte${sessionfile.bytes === 1 ? "" : "s"}) and the detection named the ${sessionfile.kind} kind; the import path takes the file from here.`, { ...session ? { sessionid: session.id } : {} });
|
|
23792
|
+
return { session: sessionfile };
|
|
23793
|
+
}
|
|
23794
|
+
if (input.tour !== void 0) {
|
|
23795
|
+
const stops = featuretourordered(featuretourstops());
|
|
23796
|
+
if (input.tour.replay === true) {
|
|
23797
|
+
const replayed = await handlerequest({ kind: "surface", onboarding: { replay: true } }, {});
|
|
23798
|
+
void replayed;
|
|
23799
|
+
await audit("tour", `The user replayed the featuretour with ${stops.length} stop${stops.length === 1 ? "" : "s"} across the popup, the sidepanel and the dashboardpage, including the datagrid, the compareviewer and the pickeroverlay stops.`, { ...session ? { sessionid: session.id } : {} });
|
|
23800
|
+
return { stops };
|
|
23801
|
+
}
|
|
23802
|
+
return { stops };
|
|
23803
|
+
}
|
|
23804
|
+
if (input.a11y !== void 0) {
|
|
23805
|
+
if (input.a11y.localized !== void 0) {
|
|
23806
|
+
const surface = surfaceof(input.a11y.localized.surface, "popup");
|
|
23807
|
+
const language = input.a11y.localized.language ?? settings?.uilanguage ?? "en";
|
|
23808
|
+
return { labels: a11ylabelslocalizedfor(surface, localebundles(), language), surface, language };
|
|
23809
|
+
}
|
|
23810
|
+
return { labels: a11ylabelsfor(surfaceof(input.a11y.labels?.surface, "popup")) };
|
|
23811
|
+
}
|
|
23812
|
+
if (input.chip !== void 0) {
|
|
23813
|
+
if (input.chip.open !== void 0) {
|
|
23814
|
+
const chip = pagechipof({ stepid: input.chip.open.stepid?.trim() ?? "", selector: input.chip.open.selector?.trim() ?? "", origin: session?.origin ?? "", at: now });
|
|
23815
|
+
await audit("pagechip", `The pagechip ${chip.id} anchored to ${chip.selector} renders the inline confirmation of the gated step ${chip.stepid} on the page.`, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {}, stepid: chip.stepid });
|
|
23816
|
+
return { chip };
|
|
23817
|
+
}
|
|
23818
|
+
if (input.chip.resolve !== void 0) {
|
|
23819
|
+
const resolution = input.chip.resolve.resolution === "approve" ? "approve" : input.chip.resolve.resolution === "reject" ? "reject" : void 0;
|
|
23820
|
+
if (resolution === void 0) throw new Error("The pagechip resolution needs its approve or reject decision.");
|
|
23821
|
+
const surface = surfaceof(input.chip.resolve.surface, "page");
|
|
23822
|
+
const chip = pagechipof({ stepid: input.chip.resolve.stepid?.trim() ?? "", selector: input.chip.resolve.selector?.trim() ?? "", origin: input.chip.resolve.origin?.trim() || session?.origin || "", at: now });
|
|
23823
|
+
const resolved = pagechipresolve(chip, resolution, surface, now);
|
|
23824
|
+
await appendrunevent("review", resolved.logevent.summary, session, chip.origin, chip.stepid);
|
|
23825
|
+
await audit("pagechip", resolved.logevent.summary, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {}, stepid: chip.stepid });
|
|
23826
|
+
await broadcastsurfaceframe({ channel: "runstate", surface, summary: `The step ${chip.stepid} resolved with a ${resolution} from the pagechip.` });
|
|
23827
|
+
return { chip: resolved.chip, logevent: resolved.logevent };
|
|
23828
|
+
}
|
|
23829
|
+
}
|
|
23830
|
+
if (input.toast !== void 0) {
|
|
23831
|
+
if (input.toast.step !== void 0) {
|
|
23832
|
+
const toast = stetoastof({ stepid: input.toast.step.stepid?.trim() ?? "", kind: input.toast.step.kind ?? "", durationms: input.toast.step.durationms ?? 0, at: now });
|
|
23833
|
+
const livecount = settings?.toastlivecount;
|
|
23834
|
+
const stacked = stetoaststackafter([], toast, livecount);
|
|
23835
|
+
await broadcastsurfaceframe({ channel: "logstream", surface: "background", summary: `The step ${toast.stepid} (${toast.kind}) completed in ${toast.durationms} milliseconds.` });
|
|
23836
|
+
await audit("toast", `The step ${toast.stepid} of the kind ${toast.kind} completed in ${toast.durationms} milliseconds; the steteoast confirms it${livecount !== void 0 ? ` inside the user live count of ${livecount}` : ""}.`, { ...session ? { sessionid: session.id } : {}, stepid: toast.stepid });
|
|
23837
|
+
return { toast, live: stacked.live, history: stetoasthistory(stacked.history) };
|
|
23838
|
+
}
|
|
23839
|
+
return { hint: "The steteoast stack keeps its bounded live count inside the surface module while the full history stays queryable." };
|
|
23840
|
+
}
|
|
23841
|
+
if (input.settings !== void 0) {
|
|
23842
|
+
const current = settings ?? {};
|
|
23843
|
+
if (input.settings.recenttraydepth !== void 0 && (!Number.isInteger(input.settings.recenttraydepth) || input.settings.recenttraydepth <= 0)) throw new Error("The recenttray depth stays a positive whole number of runs the user chose; no engine cap exists.");
|
|
23844
|
+
if (input.settings.toastlivecount !== void 0 && (!Number.isInteger(input.settings.toastlivecount) || input.settings.toastlivecount <= 0)) throw new Error("The steteoast live count stays a positive whole number the user chose; no engine cap exists.");
|
|
23845
|
+
const next = {
|
|
23846
|
+
...current,
|
|
23847
|
+
...input.settings.recenttraydepth !== void 0 ? { recenttraydepth: input.settings.recenttraydepth } : {},
|
|
23848
|
+
...input.settings.notifyconsent !== void 0 ? { notifyconsent: input.settings.notifyconsent } : {},
|
|
23849
|
+
...input.settings.notifyenabled !== void 0 ? { notifyenabled: input.settings.notifyenabled } : {},
|
|
23850
|
+
...input.settings.themepreference === "dark" || input.settings.themepreference === "light" || input.settings.themepreference === "system" ? { themepreference: input.settings.themepreference } : {},
|
|
23851
|
+
...input.settings.uilanguage !== void 0 ? { uilanguage: input.settings.uilanguage } : {},
|
|
23852
|
+
...input.settings.toastlivecount !== void 0 ? { toastlivecount: input.settings.toastlivecount } : {}
|
|
23853
|
+
};
|
|
23854
|
+
await memory.setsettings(next);
|
|
23855
|
+
await audit("configure", `The user set the interface finishing options${input.settings.recenttraydepth !== void 0 ? ` with the recenttray depth of ${input.settings.recenttraydepth}` : ""}${input.settings.themepreference !== void 0 ? ` and the ${input.settings.themepreference} theme preference` : ""}${input.settings.uilanguage !== void 0 ? ` and the ${input.settings.uilanguage} interface language` : ""}${input.settings.notifyconsent !== void 0 ? ` and the notification content consent ${input.settings.notifyconsent ? "granted" : "withheld"}` : ""}${input.settings.toastlivecount !== void 0 ? ` and the steteoast live count of ${input.settings.toastlivecount}` : ""}; every write takes effect without reloading the extension.`, { ...session ? { sessionid: session.id } : {} });
|
|
23856
|
+
await broadcastsurfaceframe({ channel: "settings", surface: "optionspage", summary: "The interface finishing options changed and take effect without a reload." });
|
|
23857
|
+
return { settings: next };
|
|
23858
|
+
}
|
|
23859
|
+
throw new Error("The views command carries no datagrid, export, quickaction, shortcut, omnibox, badge, notify, recent, picker, halo, tips, shotpanel, compare, siteprofile, theme, locale, importexport, dropimport, tour, a11y, chip, toast or settings action.");
|
|
23860
|
+
}
|
|
22031
23861
|
async function handlerequest(message, sender) {
|
|
22032
23862
|
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() });
|
|
22033
23863
|
const inboundgate = origincheckgate({ verdict: originverdict });
|
|
@@ -22163,7 +23993,7 @@ async function handlerequest(message, sender) {
|
|
|
22163
23993
|
const livetab = session ? await chrome.tabs.get(session.tabid).catch(() => void 0) : void 0;
|
|
22164
23994
|
const waitprofile = session ? waitprofiles.find((record2) => record2.origin === session.origin) : void 0;
|
|
22165
23995
|
const livestate = { phase: livetab?.status === "loading" ? "loading" : "complete", ...navrecords[0] ? { finalurl: navrecords[0].finalurl, redirects: navrecords[0].chain } : {} };
|
|
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 } : {} } };
|
|
23996
|
+
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 } : {}, ...runsettings?.recenttraydepth !== void 0 ? { recenttraydepth: runsettings.recenttraydepth } : {}, ...runsettings?.notifyconsent !== void 0 ? { notifyconsent: runsettings.notifyconsent } : {}, ...runsettings?.notifyenabled !== void 0 ? { notifyenabled: runsettings.notifyenabled } : {}, ...runsettings?.themepreference !== void 0 ? { themepreference: runsettings.themepreference } : {}, ...runsettings?.uilanguage !== void 0 ? { uilanguage: runsettings.uilanguage } : {}, ...runsettings?.toastlivecount !== void 0 ? { toastlivecount: runsettings.toastlivecount } : {} }, 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 } : {} } };
|
|
22167
23997
|
}
|
|
22168
23998
|
case "capabilities":
|
|
22169
23999
|
return refreshcapabilities();
|
|
@@ -25237,6 +27067,10 @@ async function handlerequest(message, sender) {
|
|
|
25237
27067
|
return handlesessionscommand(message);
|
|
25238
27068
|
case "surface":
|
|
25239
27069
|
return handlesurfacecommand(message);
|
|
27070
|
+
case "views":
|
|
27071
|
+
return handlesurfaceviewcommand(message);
|
|
27072
|
+
case "ecosystem":
|
|
27073
|
+
return handleecosystemcommand(message);
|
|
25240
27074
|
case "security": {
|
|
25241
27075
|
const input2 = message;
|
|
25242
27076
|
const now = Date.now();
|
|
@@ -26285,6 +28119,8 @@ chrome.runtime.onStartup.addListener(() => {
|
|
|
26285
28119
|
void detectcrash();
|
|
26286
28120
|
void pauseinterruptedworkflowruns().then(() => restorebackgroundruns()).catch(() => {
|
|
26287
28121
|
});
|
|
28122
|
+
void recoverbackgroundqueue().catch(() => {
|
|
28123
|
+
});
|
|
26288
28124
|
void runwatchdog().catch(() => {
|
|
26289
28125
|
});
|
|
26290
28126
|
});
|