@wenathlan/extension 1.1.48 → 1.1.50

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/dist/index.js CHANGED
@@ -1331,6 +1331,212 @@ function expireprofilerecords(input) {
1331
1331
  };
1332
1332
  }
1333
1333
 
1334
+ // sessions.ts
1335
+ var sessionkinds = ["persiststate", "capturesession", "restoresession", "namedsessions", "diffsessions", "searchsessions", "exportsessions", "importsessions"];
1336
+ var sessionfileversion = 1;
1337
+ var snapshotsections = ["tabs", "scroll", "forms", "storage", "cookies"];
1338
+ var searchfields = ["urls", "titles", "names", "text"];
1339
+ function checksumtext(payload) {
1340
+ let hash = 2166136261;
1341
+ for (let index = 0; index < payload.length; index += 1) {
1342
+ hash ^= payload.charCodeAt(index);
1343
+ hash = Math.imul(hash, 16777619) >>> 0;
1344
+ }
1345
+ return hash.toString(16).padStart(8, "0");
1346
+ }
1347
+ function taskstatechecksum(runid, stepcursor, outputs) {
1348
+ return checksumtext(`${runid}:${stepcursor}:${outputs.length}:${outputs.map((output) => `${output.stepid}:${output.ok}:${output.summary.length}`).join("|")}`);
1349
+ }
1350
+ function taskstateof(input) {
1351
+ return { runid: input.runid, stepcursor: input.stepcursor, outputs: input.outputs, checkpointat: input.checkpointat, checksum: taskstatechecksum(input.runid, input.stepcursor, input.outputs) };
1352
+ }
1353
+ function taskstatevalid(state) {
1354
+ if (!state || typeof state.runid !== "string" || !state.runid.trim()) return false;
1355
+ if (typeof state.stepcursor !== "number" || !Number.isInteger(state.stepcursor) || state.stepcursor < 0) return false;
1356
+ if (typeof state.checkpointat !== "number" || !Number.isFinite(state.checkpointat)) return false;
1357
+ if (!Array.isArray(state.outputs)) return false;
1358
+ return state.checksum === taskstatechecksum(state.runid, state.stepcursor, state.outputs);
1359
+ }
1360
+ function sessiontabof(value) {
1361
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1362
+ const candidate = value;
1363
+ if (typeof candidate.url !== "string" || !candidate.url.trim()) return void 0;
1364
+ if (typeof candidate.title !== "string") return void 0;
1365
+ if (typeof candidate.index !== "number" || !Number.isInteger(candidate.index) || candidate.index < 0) return void 0;
1366
+ const scrollx = typeof candidate.scrollx === "number" && Number.isFinite(candidate.scrollx) ? candidate.scrollx : 0;
1367
+ const scrolly = typeof candidate.scrolly === "number" && Number.isFinite(candidate.scrolly) ? candidate.scrolly : 0;
1368
+ const forms = Array.isArray(candidate.forms) ? candidate.forms.flatMap((entry) => {
1369
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
1370
+ const form = entry;
1371
+ if (typeof form.selector !== "string" || !form.selector.trim()) return [];
1372
+ return [{ selector: form.selector, value: typeof form.value === "string" ? form.value : "" }];
1373
+ }) : [];
1374
+ return { url: candidate.url, title: candidate.title, index: candidate.index, scrollx, scrolly, forms };
1375
+ }
1376
+ function autointervalof(value) {
1377
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1378
+ const candidate = value;
1379
+ if (typeof candidate.period !== "number" || !Number.isFinite(candidate.period) || candidate.period <= 0) return void 0;
1380
+ if (typeof candidate.maxsnapshots !== "number" || !Number.isInteger(candidate.maxsnapshots) || candidate.maxsnapshots < 1) return void 0;
1381
+ if (typeof candidate.expiry !== "number" || !Number.isFinite(candidate.expiry) || candidate.expiry < 0) return void 0;
1382
+ return { period: candidate.period, maxsnapshots: candidate.maxsnapshots, expiry: candidate.expiry };
1383
+ }
1384
+ function snapshotplanof(value) {
1385
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1386
+ const candidate = value;
1387
+ if (candidate.scope !== "tab" && candidate.scope !== "run" && candidate.scope !== "all") return void 0;
1388
+ const sections = Array.isArray(candidate.sections) ? candidate.sections.flatMap((section) => typeof section === "string" && snapshotsections.includes(section) ? [section] : []) : [];
1389
+ if (sections.length === 0) return void 0;
1390
+ if (typeof candidate.captures !== "boolean") return void 0;
1391
+ const auto = candidate.auto === void 0 ? void 0 : autointervalof(candidate.auto);
1392
+ if (candidate.auto !== void 0 && auto === void 0) return void 0;
1393
+ return { scope: candidate.scope, sections, captures: candidate.captures, ...auto !== void 0 ? { auto } : {} };
1394
+ }
1395
+ function restoreplanof(value) {
1396
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1397
+ const candidate = value;
1398
+ if (candidate.tabpolicy !== "reopen" && candidate.tabpolicy !== "skip") return void 0;
1399
+ if (candidate.formpolicy !== "restore" && candidate.formpolicy !== "skip") return void 0;
1400
+ if (candidate.capturepolicy !== "link" && candidate.capturepolicy !== "skip") return void 0;
1401
+ return { tabpolicy: candidate.tabpolicy, formpolicy: candidate.formpolicy, capturepolicy: candidate.capturepolicy };
1402
+ }
1403
+ function searchqueryof(value) {
1404
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1405
+ const candidate = value;
1406
+ const terms = Array.isArray(candidate.terms) ? candidate.terms.flatMap((term) => typeof term === "string" && term.trim() ? [term.trim()] : []) : [];
1407
+ if (terms.length === 0) return void 0;
1408
+ const fields = Array.isArray(candidate.fields) ? candidate.fields.flatMap((field) => typeof field === "string" && searchfields.includes(field) ? [field] : []) : [...searchfields];
1409
+ if (fields.length === 0) return void 0;
1410
+ const from = typeof candidate.from === "number" && Number.isFinite(candidate.from) ? candidate.from : void 0;
1411
+ const to = typeof candidate.to === "number" && Number.isFinite(candidate.to) ? candidate.to : void 0;
1412
+ if (from !== void 0 && to !== void 0 && from > to) return void 0;
1413
+ return { terms, fields, ...from !== void 0 ? { from } : {}, ...to !== void 0 ? { to } : {} };
1414
+ }
1415
+ function sessionfolderof(value) {
1416
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1417
+ const candidate = value;
1418
+ if (typeof candidate.name !== "string" || !candidate.name.trim()) return void 0;
1419
+ const parent = typeof candidate.parent === "string" && candidate.parent.trim() ? candidate.parent : void 0;
1420
+ const tags = Array.isArray(candidate.tags) ? candidate.tags.flatMap((tag) => typeof tag === "string" && tag.trim() ? [tag] : []) : [];
1421
+ return { name: candidate.name, ...parent !== void 0 ? { parent } : {}, tags };
1422
+ }
1423
+ function newsessionrecord(input) {
1424
+ return { id: input.id, name: input.name, createdat: input.createdat, tabs: input.tabs, captures: input.captures, storage: input.storage, cookies: input.cookies, ...input.folder !== void 0 ? { folder: input.folder } : {}, tags: input.tags ?? [], ...input.auto === true ? { auto: true } : {} };
1425
+ }
1426
+ function diffsessionrecords(left, right) {
1427
+ const changes = [];
1428
+ const leftbyindex = new Map(left.tabs.map((tab) => [tab.index, tab]));
1429
+ const rightbyindex = new Map(right.tabs.map((tab) => [tab.index, tab]));
1430
+ for (const tab of right.tabs) {
1431
+ const prior = leftbyindex.get(tab.index);
1432
+ if (!prior) {
1433
+ changes.push({ class: "added", subject: "tab", detail: `Tab ${tab.index} added: ${tab.url}` });
1434
+ continue;
1435
+ }
1436
+ if (prior.url !== tab.url) changes.push({ class: "changed", subject: "url", detail: `Tab ${tab.index} moved from ${prior.url} to ${tab.url}` });
1437
+ if (prior.title !== tab.title) changes.push({ class: "changed", subject: "tab", detail: `Tab ${tab.index} title changed from "${prior.title}" to "${tab.title}"` });
1438
+ const priorforms = new Map(prior.forms.map((form) => [form.selector, form.value]));
1439
+ for (const form of tab.forms) {
1440
+ const before = priorforms.get(form.selector);
1441
+ if (before === void 0) {
1442
+ changes.push({ class: "added", subject: "form", detail: `Form field ${form.selector} of tab ${tab.index} added with a value` });
1443
+ continue;
1444
+ }
1445
+ if (before !== form.value) changes.push({ class: "changed", subject: "form", detail: `Form field ${form.selector} of tab ${tab.index} changed its captured value` });
1446
+ }
1447
+ for (const form of prior.forms) if (!tab.forms.some((entry) => entry.selector === form.selector)) changes.push({ class: "removed", subject: "form", detail: `Form field ${form.selector} of tab ${tab.index} removed` });
1448
+ }
1449
+ for (const tab of left.tabs) if (!rightbyindex.has(tab.index)) changes.push({ class: "removed", subject: "tab", detail: `Tab ${tab.index} removed: ${tab.url}` });
1450
+ const leftstorage = new Map(left.storage.map((entry) => [entry.origin, entry]));
1451
+ for (const entry of right.storage) {
1452
+ const prior = leftstorage.get(entry.origin);
1453
+ if (!prior) {
1454
+ changes.push({ class: "added", subject: "storage", detail: `Local storage of ${entry.origin} captured with ${entry.keys.length} keys` });
1455
+ continue;
1456
+ }
1457
+ if (prior.keys.join("|") !== entry.keys.join("|") || prior.values.join("|") !== entry.values.join("|")) changes.push({ class: "changed", subject: "storage", detail: `Local storage of ${entry.origin} changed its captured keys or values` });
1458
+ }
1459
+ for (const entry of left.storage) if (!right.storage.some((candidate) => candidate.origin === entry.origin)) changes.push({ class: "removed", subject: "storage", detail: `Local storage of ${entry.origin} left the capture` });
1460
+ return changes;
1461
+ }
1462
+ function newsessiondiff(input) {
1463
+ return { id: input.id, leftid: input.left.id, rightid: input.right.id, changes: diffsessionrecords(input.left, input.right), at: input.at };
1464
+ }
1465
+ function searchsessionrecords(query, records) {
1466
+ const matches = [];
1467
+ for (const record2 of records) {
1468
+ if (query.from !== void 0 && record2.createdat < query.from) continue;
1469
+ if (query.to !== void 0 && record2.createdat > query.to) continue;
1470
+ const haystacks = [
1471
+ { field: "urls", text: record2.tabs.map((tab) => tab.url).join(" ") },
1472
+ { field: "titles", text: record2.tabs.map((tab) => tab.title).join(" ") },
1473
+ { field: "names", text: [record2.name, record2.folder ?? "", ...record2.tags].join(" ") },
1474
+ { field: "text", text: record2.tabs.flatMap((tab) => tab.forms.map((form) => form.value)).join(" ") }
1475
+ ];
1476
+ for (const haystack of haystacks) {
1477
+ if (!query.fields.includes(haystack.field)) continue;
1478
+ const lower = haystack.text.toLowerCase();
1479
+ for (const term of query.terms) {
1480
+ const at = lower.indexOf(term.toLowerCase());
1481
+ if (at < 0) continue;
1482
+ const start = Math.max(0, at - 30);
1483
+ matches.push({ sessionid: record2.id, field: haystack.field, term, at: record2.createdat, excerpt: haystack.text.slice(start, start + 80).trim() });
1484
+ }
1485
+ }
1486
+ }
1487
+ return matches;
1488
+ }
1489
+ function exportsessionfile(records, now) {
1490
+ const recordids = records.map((record2) => record2.id);
1491
+ const payload = JSON.stringify(records);
1492
+ return { formatversion: sessionfileversion, records, recordids, bytesize: payload.length, checksum: checksumtext(`${sessionfileversion}:${recordids.join(",")}:${payload.length}`), exportedat: now };
1493
+ }
1494
+ function importsessionfile(value) {
1495
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1496
+ const candidate = value;
1497
+ if (candidate.formatversion !== sessionfileversion) return void 0;
1498
+ const records = Array.isArray(candidate.records) ? candidate.records.flatMap((record2) => sessionrecordvalid(record2) ? [record2] : []) : [];
1499
+ if (records.length === 0) return void 0;
1500
+ if (!Array.isArray(candidate.recordids) || candidate.recordids.length !== records.length || !candidate.recordids.every((id, index) => id === records[index]?.id)) return void 0;
1501
+ const bytesize = typeof candidate.bytesize === "number" && Number.isFinite(candidate.bytesize) ? candidate.bytesize : -1;
1502
+ if (bytesize < 0) return void 0;
1503
+ const checksum = typeof candidate.checksum === "string" ? candidate.checksum : "";
1504
+ if (checksum !== checksumtext(`${sessionfileversion}:${candidate.recordids.join(",")}:${bytesize}`)) return void 0;
1505
+ return { formatversion: sessionfileversion, records, recordids: candidate.recordids, bytesize, checksum, exportedat: typeof candidate.exportedat === "number" && Number.isFinite(candidate.exportedat) ? candidate.exportedat : 0 };
1506
+ }
1507
+ function sessionrecordvalid(value) {
1508
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1509
+ const candidate = value;
1510
+ if (typeof candidate.id !== "string" || !candidate.id.trim()) return false;
1511
+ if (typeof candidate.name !== "string" || !candidate.name.trim()) return false;
1512
+ if (typeof candidate.createdat !== "number" || !Number.isFinite(candidate.createdat)) return false;
1513
+ if (!Array.isArray(candidate.tabs) || !candidate.tabs.every((tab) => sessiontabof(tab) !== void 0)) return false;
1514
+ if (!Array.isArray(candidate.captures) || !candidate.captures.every((id) => typeof id === "string")) return false;
1515
+ if (!Array.isArray(candidate.tags) || !candidate.tags.every((tag) => typeof tag === "string")) return false;
1516
+ return true;
1517
+ }
1518
+ function expiresessions(records, retention, now) {
1519
+ if (retention === void 0 || !Number.isFinite(retention)) return records;
1520
+ return records.map((record2) => {
1521
+ if (record2.sectionsexpired || now - record2.createdat < retention) return record2;
1522
+ return { id: record2.id, name: record2.name, createdat: record2.createdat, tabs: [], captures: record2.captures, storage: [], cookies: [], ...record2.folder !== void 0 ? { folder: record2.folder } : {}, tags: record2.tags, ...record2.auto === true ? { auto: true } : {}, ...record2.restoredat !== void 0 ? { restoredat: record2.restoredat } : {}, sectionsexpired: true };
1523
+ });
1524
+ }
1525
+ function filteredsessions(records, filter) {
1526
+ return records.filter((record2) => {
1527
+ if (filter.name !== void 0 && !record2.name.toLowerCase().includes(filter.name.toLowerCase())) return false;
1528
+ if (filter.folder !== void 0 && record2.folder !== filter.folder) return false;
1529
+ if (filter.from !== void 0 && record2.createdat < filter.from) return false;
1530
+ if (filter.to !== void 0 && record2.createdat > filter.to) return false;
1531
+ return true;
1532
+ });
1533
+ }
1534
+ function crashinterrupted(state, plansteps, now) {
1535
+ if (!state) return void 0;
1536
+ if (state.stepcursor >= plansteps) return state;
1537
+ return { ...state, interrupted: true, crashat: state.crashat ?? now };
1538
+ }
1539
+
1334
1540
  // memory.ts
1335
1541
  var sessionmemory = class {
1336
1542
  constructor(adapter) {
@@ -2778,6 +2984,170 @@ var sessionmemory = class {
2778
2984
  async getlocationconsents() {
2779
2985
  return await this.adapter.get("locationconsents") ?? [];
2780
2986
  }
2987
+ /** Returns the persisted task state checkpoint of one run so the run resumes after a service worker restart. */
2988
+ async gettaskstate(runid) {
2989
+ return this.adapter.get(`taskstate${runid}`);
2990
+ }
2991
+ /** Persists one task state checkpoint per run with its corruption checksum. */
2992
+ async settaskstate(state) {
2993
+ return this.adapter.set(`taskstate${state.runid}`, state);
2994
+ }
2995
+ /** Returns the session event history with timestamps, newest first. */
2996
+ async getsessionevents() {
2997
+ return await this.adapter.get("sessionevents") ?? [];
2998
+ }
2999
+ /** Records one session event of the run with its timestamp and detail. */
3000
+ async addsessionevent(event) {
3001
+ const records = await this.getsessionevents();
3002
+ await this.adapter.set("sessionevents", [event, ...records]);
3003
+ }
3004
+ /** Returns every saved session record with its sections, newest first. */
3005
+ async getsessionrecords() {
3006
+ return await this.adapter.get("sessionrecords") ?? [];
3007
+ }
3008
+ /** Adds one saved session record to the library. */
3009
+ async addsessionrecord(record2) {
3010
+ const records = await this.getsessionrecords();
3011
+ await this.adapter.set("sessionrecords", [record2, ...records]);
3012
+ }
3013
+ /** Replaces one saved session record by its id after a filing or restore touches it. */
3014
+ async updatesessionrecord(record2) {
3015
+ const records = await this.getsessionrecords();
3016
+ await this.adapter.set("sessionrecords", records.map((item) => item.id === record2.id ? record2 : item));
3017
+ }
3018
+ /** Lists saved sessions filtered by name substring, folder and time window; the filter stays a user choice with no result cap. */
3019
+ async listsessions(filter) {
3020
+ return filteredsessions(await this.getsessionrecords(), filter);
3021
+ }
3022
+ /** Returns one saved session with every section; an expired record carries its metadata only. */
3023
+ async getsessionrecord(id) {
3024
+ return (await this.getsessionrecords()).find((record2) => record2.id === id);
3025
+ }
3026
+ /** Runs the reviewed search query across every stored session and returns the matches with their session ids and time windows. */
3027
+ async searchmemory(query) {
3028
+ return searchsessionrecords(query, await this.getsessionrecords());
3029
+ }
3030
+ /** Returns the folder tree of the session library. */
3031
+ async getsessionfolders() {
3032
+ return await this.adapter.get("sessionfolders") ?? [];
3033
+ }
3034
+ /** Replaces the folder tree after a reviewed filing adds or moves one folder. */
3035
+ async setsessionfolders(folders) {
3036
+ return this.adapter.set("sessionfolders", folders);
3037
+ }
3038
+ /** Returns every stored session diff result, newest first. */
3039
+ async getsessiondiffs() {
3040
+ return await this.adapter.get("sessiondiffs") ?? [];
3041
+ }
3042
+ /** Stores one session diff result for later review. */
3043
+ async addsessiondiff(diff) {
3044
+ const records = await this.getsessiondiffs();
3045
+ await this.adapter.set("sessiondiffs", [diff, ...records]);
3046
+ }
3047
+ /** Returns the persisted auto snapshot state with the reviewed interval, the last snapshot time and the snapshot count. */
3048
+ async getautosnapshot() {
3049
+ return await this.adapter.get("autosnapshot") ?? void 0;
3050
+ }
3051
+ /** Stores the auto snapshot state of the reviewed interval. */
3052
+ async setautosnapshot(state) {
3053
+ return this.adapter.set("autosnapshot", state);
3054
+ }
3055
+ /** Clears the auto snapshot interval so on demand captures stay the only source of records. */
3056
+ async clearautosnapshot() {
3057
+ return this.adapter.set("autosnapshot", null);
3058
+ }
3059
+ /** Expires the heavy sections of saved sessions after the reviewed retention window while the record metadata survives. */
3060
+ async applysessionexpiry(retention, now) {
3061
+ const records = expiresessions(await this.getsessionrecords(), retention, now);
3062
+ await this.adapter.set("sessionrecords", records);
3063
+ return records;
3064
+ }
3065
+ /** Returns the crash marker of a run interrupted by a browser restart. */
3066
+ async getcrashflag() {
3067
+ return await this.adapter.get("crashed") ?? false;
3068
+ }
3069
+ /** Sets the crash marker so the sessions view offers the crash restore inside the consent model. */
3070
+ async setcrashflag(value) {
3071
+ return this.adapter.set("crashed", value);
3072
+ }
3073
+ /** Stores one composed workflow record version with its timestamp; re-composing the same version replaces it while older versions survive for the audit trail. */
3074
+ async addworkflowrecord(record2) {
3075
+ const records = await this.getworkflowrecordversions();
3076
+ const remaining = records.filter((entry) => !(entry.id === record2.id && entry.version === record2.version));
3077
+ await this.adapter.set("workflowrecords", [record2, ...remaining]);
3078
+ }
3079
+ /** Returns every stored workflow record version, newest first. */
3080
+ async getworkflowrecordversions() {
3081
+ return await this.adapter.get("workflowrecords") ?? [];
3082
+ }
3083
+ /** Returns the latest stored version of one workflow record. */
3084
+ async getworkflowrecord(id) {
3085
+ return (await this.getworkflowrecordversions()).find((entry) => entry.id === id);
3086
+ }
3087
+ /** Lists the saved workflow records, the latest version of each, newest first. */
3088
+ async listworkflows() {
3089
+ const seen = /* @__PURE__ */ new Set();
3090
+ const latest = [];
3091
+ for (const entry of await this.getworkflowrecordversions()) {
3092
+ if (seen.has(entry.id)) continue;
3093
+ seen.add(entry.id);
3094
+ latest.push(entry);
3095
+ }
3096
+ return latest;
3097
+ }
3098
+ /** Stores one workflow run with its state transition; a run replace keeps the full runlog of the same id. */
3099
+ async setworkflowrun(run) {
3100
+ const runs = await this.listworkflowruns();
3101
+ const remaining = runs.filter((entry) => entry.id !== run.id);
3102
+ await this.adapter.set("workflowruns", [run, ...remaining]);
3103
+ }
3104
+ /** Returns every stored workflow run, newest first. */
3105
+ async listworkflowruns() {
3106
+ return await this.adapter.get("workflowruns") ?? [];
3107
+ }
3108
+ /** Returns one run with its full step outcome list so the panel shows the timeline after and during a run. */
3109
+ async getrun(id) {
3110
+ const run = (await this.listworkflowruns()).find((entry) => entry.id === id);
3111
+ if (!run) return void 0;
3112
+ return { run, log: await this.getrunlog(id) };
3113
+ }
3114
+ /** Records one runlog entry of a run; the runlog retention window is a user setting and an absent window keeps every entry. */
3115
+ async addrunlogentry(runid, entry) {
3116
+ const entries = await this.getrunlog(runid);
3117
+ const combined = [...entries, entry];
3118
+ const retention = (await this.getsettings())?.runlogretention;
3119
+ await this.adapter.set(`runlog${runid}`, retention === void 0 ? combined : combined.slice(-retention));
3120
+ }
3121
+ /** Returns the runlog of one run, oldest first. */
3122
+ async getrunlog(runid) {
3123
+ return await this.adapter.get(`runlog${runid}`) ?? [];
3124
+ }
3125
+ /** Stores the variable values per scope of one run for inspection after the run. */
3126
+ async setrunscopes(runid, scopes) {
3127
+ return this.adapter.set(`runscopes${runid}`, scopes);
3128
+ }
3129
+ /** Returns the variable scopes of one run, oldest first. */
3130
+ async getrunscopes(runid) {
3131
+ return await this.adapter.get(`runscopes${runid}`) ?? [];
3132
+ }
3133
+ /** Records one provenance entry of a run: an expression result or a regex capture with its name, value and time. */
3134
+ async addworkflowprovenance(runid, entry) {
3135
+ const entries = await this.getworkflowprovenance(runid);
3136
+ await this.adapter.set(`workflowprovenance${runid}`, [...entries, entry]);
3137
+ }
3138
+ /** Returns every provenance entry of one run, oldest first. */
3139
+ async getworkflowprovenance(runid) {
3140
+ return await this.adapter.get(`workflowprovenance${runid}`) ?? [];
3141
+ }
3142
+ /** Stores one shareable step template under its unique name. */
3143
+ async addsteptemplate(template) {
3144
+ const templates = (await this.getsteptemplates()).filter((entry) => entry.name !== template.name);
3145
+ await this.adapter.set("steptemplates", [template, ...templates]);
3146
+ }
3147
+ /** Returns every stored step template, newest first. */
3148
+ async getsteptemplates() {
3149
+ return await this.adapter.get("steptemplates") ?? [];
3150
+ }
2781
3151
  };
2782
3152
  function mediakindof(record2) {
2783
3153
  if ("pages" in record2) return "pdf";
@@ -3314,6 +3684,511 @@ function extractvalues(body, paths) {
3314
3684
  return fields.map((field) => ({ path: field.path, ...field.value !== void 0 ? { value: field.value } : {}, ...field.missing ? { missing: true } : {} }));
3315
3685
  }
3316
3686
 
3687
+ // workflow.ts
3688
+ var workflowkinds = ["composeworkflow", "savetemplate", "runworkflow", "dryrun", "delay", "waitelement", "compute", "extractvars"];
3689
+ function workflowstepof(value) {
3690
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
3691
+ const candidate = value;
3692
+ if (typeof candidate.id !== "string" || !candidate.id.trim()) return void 0;
3693
+ if (typeof candidate.kind !== "string" || !/^[a-z]+$/.test(candidate.kind)) return void 0;
3694
+ if (typeof candidate.label !== "string" || !candidate.label.trim()) return void 0;
3695
+ if (candidate.target !== void 0 && (typeof candidate.target !== "string" || !candidate.target)) return void 0;
3696
+ if (candidate.value !== void 0 && typeof candidate.value !== "string") return void 0;
3697
+ if (candidate.options !== void 0 && typeof candidate.options !== "string") return void 0;
3698
+ const bindings = Array.isArray(candidate.bindings) ? candidate.bindings.flatMap((binding) => bindingof(binding) !== void 0 ? [bindingof(binding)] : []) : void 0;
3699
+ if (candidate.bindings !== void 0 && bindings === void 0) return void 0;
3700
+ if (Array.isArray(candidate.bindings) && bindings !== void 0 && bindings.length !== candidate.bindings.length) return void 0;
3701
+ const expression = candidate.expression === void 0 ? void 0 : expressionof(candidate.expression);
3702
+ if (candidate.expression !== void 0 && expression === void 0) return void 0;
3703
+ const extract = candidate.extract === void 0 ? void 0 : regexruleof(candidate.extract);
3704
+ if (candidate.extract !== void 0 && extract === void 0) return void 0;
3705
+ return { id: candidate.id, kind: candidate.kind, label: candidate.label, ...candidate.target !== void 0 ? { target: candidate.target } : {}, ...candidate.value !== void 0 ? { value: candidate.value } : {}, ...candidate.options !== void 0 ? { options: candidate.options } : {}, ...bindings !== void 0 && bindings.length > 0 ? { bindings } : {}, ...expression !== void 0 ? { expression } : {}, ...extract !== void 0 ? { extract } : {} };
3706
+ }
3707
+ function blockinvocationof(value) {
3708
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
3709
+ const candidate = value;
3710
+ if (typeof candidate.block !== "string" || !candidate.block.trim()) return void 0;
3711
+ if (typeof candidate.label !== "string" || !candidate.label.trim()) return void 0;
3712
+ return { block: candidate.block, label: candidate.label };
3713
+ }
3714
+ function workflowblockof(value) {
3715
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
3716
+ const candidate = value;
3717
+ if (typeof candidate.name !== "string" || !/^[a-z][a-z0-9]*$/.test(candidate.name)) return void 0;
3718
+ if (typeof candidate.label !== "string" || !candidate.label.trim()) return void 0;
3719
+ if (!Array.isArray(candidate.steps)) return void 0;
3720
+ const steps = [];
3721
+ for (const entry of candidate.steps) {
3722
+ const step = workflowstepof(entry);
3723
+ if (step) {
3724
+ steps.push(step);
3725
+ continue;
3726
+ }
3727
+ const invocation = blockinvocationof(entry);
3728
+ if (invocation) {
3729
+ steps.push(invocation);
3730
+ continue;
3731
+ }
3732
+ return void 0;
3733
+ }
3734
+ return { name: candidate.name, label: candidate.label, steps };
3735
+ }
3736
+ function steptemplateof(value) {
3737
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
3738
+ const candidate = value;
3739
+ if (typeof candidate.id !== "string" || !candidate.id.trim()) return void 0;
3740
+ if (typeof candidate.name !== "string" || !candidate.name.trim()) return void 0;
3741
+ if (typeof candidate.origin !== "string" || !candidate.origin.trim()) return void 0;
3742
+ const step = workflowstepof(candidate.step);
3743
+ if (!step) return void 0;
3744
+ if (typeof candidate.sharedat !== "number" || !Number.isFinite(candidate.sharedat)) return void 0;
3745
+ return { id: candidate.id, name: candidate.name, origin: candidate.origin, step, sharedat: candidate.sharedat };
3746
+ }
3747
+ function bindingof(value) {
3748
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
3749
+ const candidate = value;
3750
+ if (typeof candidate.variable !== "string" || !/^[a-z][a-z0-9]*$/.test(candidate.variable)) return void 0;
3751
+ if (!variablekinds.includes(candidate.kind)) return void 0;
3752
+ if (typeof candidate.stepid !== "string" || !candidate.stepid.trim()) return void 0;
3753
+ if (candidate.path !== void 0 && (typeof candidate.path !== "string" || !candidate.path.trim())) return void 0;
3754
+ return { variable: candidate.variable, kind: candidate.kind, stepid: candidate.stepid, ...candidate.path !== void 0 ? { path: candidate.path } : {} };
3755
+ }
3756
+ var variablekinds = ["string", "number", "boolean", "list", "element"];
3757
+ var expressionoperators = ["add", "subtract", "multiply", "divide", "modulo", "equal", "notequal", "less", "greater", "lessequal", "greaterequal", "and", "or", "not", "concat", "contains", "length"];
3758
+ function expressionof(value) {
3759
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
3760
+ const candidate = value;
3761
+ const left = operandof(candidate.left);
3762
+ if (!left) return void 0;
3763
+ const right = candidate.right === void 0 ? void 0 : operandof(candidate.right);
3764
+ if (candidate.right !== void 0 && right === void 0) return void 0;
3765
+ if (typeof candidate.operator !== "string" || !expressionoperators.includes(candidate.operator)) return void 0;
3766
+ if (typeof candidate.result !== "string" || !/^[a-z][a-z0-9]*$/.test(candidate.result)) return void 0;
3767
+ if (!variablekinds.includes(candidate.resultkind)) return void 0;
3768
+ return { left, ...right !== void 0 ? { right } : {}, operator: candidate.operator, result: candidate.result, resultkind: candidate.resultkind };
3769
+ }
3770
+ function operandof(value) {
3771
+ if (value === void 0) return void 0;
3772
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return { literal: value };
3773
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
3774
+ const candidate = value;
3775
+ if (typeof candidate.ref === "string" && /^[a-z][a-z0-9]*$/.test(candidate.ref)) return { ref: candidate.ref };
3776
+ if (typeof candidate.literal === "string" || typeof candidate.literal === "number" || typeof candidate.literal === "boolean") return { literal: candidate.literal };
3777
+ return void 0;
3778
+ }
3779
+ function regexruleof(value) {
3780
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
3781
+ const candidate = value;
3782
+ if (typeof candidate.pattern !== "string" || !candidate.pattern.trim()) return void 0;
3783
+ if (typeof candidate.flags !== "string" || !/^[dgimsuvy]*$/.test(candidate.flags)) return void 0;
3784
+ const groups = Array.isArray(candidate.groups) ? candidate.groups.flatMap((group) => typeof group === "string" && /^[a-z][a-z0-9]*$/.test(group) ? [group] : []) : [];
3785
+ if (candidate.groups !== void 0 && groups.length !== candidate.groups.length) return void 0;
3786
+ return { pattern: candidate.pattern, flags: candidate.flags, groups };
3787
+ }
3788
+ function expandblocks(steps, blocks) {
3789
+ const byname = new Map(blocks.map((block) => [block.name, block]));
3790
+ const expanded = [];
3791
+ const visit = (entries, path, inside) => {
3792
+ for (const entry of entries) {
3793
+ if ("kind" in entry && "label" in entry && !("block" in entry)) {
3794
+ expanded.push(inside === void 0 ? entry : { ...entry, block: inside });
3795
+ continue;
3796
+ }
3797
+ const invocation = blockinvocationof(entry);
3798
+ if (!invocation) throw new Error("The step list entry is neither a reviewed step nor a block invocation.");
3799
+ if (path.includes(invocation.block)) throw new Error(`The block ${invocation.block} recurs inside itself and cannot expand.`);
3800
+ const block = byname.get(invocation.block);
3801
+ if (!block) throw new Error(`The block ${invocation.block} is not defined in the workflow.`);
3802
+ visit(block.steps, [...path, invocation.block], invocation.block);
3803
+ }
3804
+ };
3805
+ visit(steps, [], void 0);
3806
+ if (expanded.length === 0) throw new Error("A workflow needs at least one executable step after block expansion.");
3807
+ return expanded;
3808
+ }
3809
+ function composeworkflow(input) {
3810
+ if (typeof input.name !== "string" || !input.name.trim()) throw new Error("The workflow name must be a non-empty string.");
3811
+ if (typeof input.version !== "number" || !Number.isInteger(input.version) || input.version < 1) throw new Error("The workflow version must be a positive integer.");
3812
+ if (!Array.isArray(input.origins) || input.origins.length === 0) throw new Error("A workflow needs at least one granted HTTPS origin.");
3813
+ const origins = input.origins.map((origin) => {
3814
+ try {
3815
+ return new URL(origin).origin;
3816
+ } catch {
3817
+ throw new Error(`The workflow origin ${origin} is not a valid url.`);
3818
+ }
3819
+ });
3820
+ if (origins.some((origin) => !origin.startsWith("https://"))) throw new Error("Workflow origins must use HTTPS.");
3821
+ const blocks = input.blocks ?? [];
3822
+ if (blocks.some((block, index) => blocks.findIndex((other) => other.name === block.name) !== index)) throw new Error("Workflow block names must stay unique.");
3823
+ for (const entry of input.steps) {
3824
+ if ("kind" in entry && "label" in entry && !("block" in entry)) {
3825
+ if (input.kindallowed && !input.kindallowed(entry.kind)) throw new Error(`The workflow step kind ${entry.kind} is not a reviewed action kind.`);
3826
+ }
3827
+ }
3828
+ for (const block of blocks) for (const entry of block.steps) {
3829
+ if ("kind" in entry && "label" in entry && !("block" in entry) && input.kindallowed && !input.kindallowed(entry.kind)) throw new Error(`The workflow step kind ${entry.kind} inside block ${block.name} is not a reviewed action kind.`);
3830
+ }
3831
+ const steps = expandblocks(input.steps, blocks);
3832
+ for (const step of steps) {
3833
+ if (input.kindallowed && !input.kindallowed(step.kind)) throw new Error(`The workflow step kind ${step.kind} is not a reviewed action kind.`);
3834
+ if (step.bindings) for (const binding of step.bindings) {
3835
+ if (!steps.some((other) => other.id === binding.stepid)) throw new Error(`The binding of ${binding.variable} references the unknown step ${binding.stepid}.`);
3836
+ }
3837
+ }
3838
+ const riskof = input.riskof ?? (() => "sensitive");
3839
+ const risk = steps.some((step) => riskof(step.kind) === "sensitive") ? "sensitive" : steps.some((step) => riskof(step.kind) === "interaction") ? "interaction" : "read";
3840
+ const record2 = { id: input.id ?? crypto.randomUUID(), name: input.name, version: input.version, origins: [...new Set(origins)], steps, blocks, risk, createdat: input.now };
3841
+ return deepfreeze(record2);
3842
+ }
3843
+ function deepfreeze(record2) {
3844
+ for (const step of record2.steps) Object.freeze(step);
3845
+ for (const block of record2.blocks) for (const entry of block.steps) if ("kind" in entry && "label" in entry && !("block" in entry)) Object.freeze(entry);
3846
+ Object.freeze(record2.blocks);
3847
+ Object.freeze(record2.steps);
3848
+ return Object.freeze(record2);
3849
+ }
3850
+ function validateworkflow(record2, options) {
3851
+ if (record2.steps.length === 0) return { allowed: false, reason: "A workflow needs at least one reviewed step." };
3852
+ const defined = new Set(options?.inputs ?? []);
3853
+ const byid = new Map(record2.steps.map((step, index) => [step.id, { step, index }]));
3854
+ for (let index = 0; index < record2.steps.length; index += 1) {
3855
+ const step = record2.steps[index];
3856
+ if (options?.kindallowed && !options.kindallowed(step.kind)) return { allowed: false, reason: `The workflow step kind ${step.kind} is not a reviewed action kind.` };
3857
+ if (step.bindings) for (const binding of step.bindings) {
3858
+ const source = byid.get(binding.stepid);
3859
+ if (!source) return { allowed: false, reason: `The binding of ${binding.variable} references the unknown step ${binding.stepid}.` };
3860
+ if (source.index >= index) return { allowed: false, reason: `The binding of ${binding.variable} must link an earlier step than ${step.id}.` };
3861
+ defined.add(binding.variable);
3862
+ }
3863
+ if (step.expression) {
3864
+ for (const operand of [step.expression.left, step.expression.right]) {
3865
+ if (operand?.ref && !defined.has(operand.ref)) return { allowed: false, reason: `The expression of step ${step.id} references the undefined variable ${operand.ref}.` };
3866
+ }
3867
+ defined.add(step.expression.result);
3868
+ }
3869
+ if (step.extract) for (const group of step.extract.groups) defined.add(group);
3870
+ }
3871
+ return { allowed: true };
3872
+ }
3873
+ function pushscope(scopes, name, parent) {
3874
+ return [...scopes, { name, variables: [], ...parent !== void 0 ? { parent } : {} }];
3875
+ }
3876
+ function popscope(scopes) {
3877
+ if (scopes.length === 0) return scopes;
3878
+ return scopes.slice(0, -1);
3879
+ }
3880
+ function resolvevariable(scopes, name) {
3881
+ for (let index = scopes.length - 1; index >= 0; index -= 1) {
3882
+ const scope = scopes[index];
3883
+ const found = scope.variables.find((variable) => variable.name === name);
3884
+ if (found) return found;
3885
+ if (scope.parent === void 0) continue;
3886
+ const parentindex = scopes.findIndex((candidate) => candidate.name === scope.parent);
3887
+ if (parentindex >= 0 && parentindex < index) {
3888
+ const inherited = resolvevariable([scopes[parentindex]], name);
3889
+ if (inherited) return inherited;
3890
+ }
3891
+ }
3892
+ return void 0;
3893
+ }
3894
+ function setvariable(scopes, name, kind, value, now) {
3895
+ if (scopes.length === 0) scopes = [{ name: "root", variables: [] }];
3896
+ const target = scopes[scopes.length - 1];
3897
+ const variables = [...target.variables.filter((variable) => variable.name !== name), { name, kind, value, setat: now }];
3898
+ return [...scopes.slice(0, -1), { ...target, variables }];
3899
+ }
3900
+ function coercevariable(value, kind) {
3901
+ if (kind === "number") {
3902
+ const parsed = typeof value === "number" ? value : typeof value === "string" && value.trim() !== "" ? Number(value) : NaN;
3903
+ if (!Number.isFinite(parsed)) throw new Error("The bound value is not a finite number.");
3904
+ return parsed;
3905
+ }
3906
+ if (kind === "boolean") {
3907
+ if (typeof value === "boolean") return value;
3908
+ if (value === "true") return true;
3909
+ if (value === "false") return false;
3910
+ throw new Error("The bound value is not a boolean.");
3911
+ }
3912
+ if (kind === "list") {
3913
+ if (Array.isArray(value)) return value.map((item) => String(item));
3914
+ if (typeof value === "string") return value.length === 0 ? [] : value.split(",");
3915
+ throw new Error("The bound value is not a list.");
3916
+ }
3917
+ if (kind === "element") {
3918
+ if (typeof value === "string" && value.trim()) return value;
3919
+ throw new Error("The bound value is not an element reference.");
3920
+ }
3921
+ if (typeof value === "string") return value;
3922
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
3923
+ throw new Error("The bound value is not a string.");
3924
+ }
3925
+ function outcomedetail(outcome, path) {
3926
+ if (!path) return outcome.summary;
3927
+ let current = outcome.details ?? {};
3928
+ for (const segment of path.split(".")) {
3929
+ if (!current || typeof current !== "object" || Array.isArray(current)) return void 0;
3930
+ current = current[segment];
3931
+ }
3932
+ return current;
3933
+ }
3934
+ function bindvariables(scopes, bindings, outputs, now) {
3935
+ let current = scopes;
3936
+ const produced = [];
3937
+ for (const binding of bindings) {
3938
+ const outcome = outputs[binding.stepid];
3939
+ if (!outcome) continue;
3940
+ const raw = outcomedetail(outcome, binding.path);
3941
+ if (raw === void 0) throw new Error(`The binding of ${binding.variable} found no value at ${binding.path ?? "the summary"} of step ${binding.stepid}.`);
3942
+ current = setvariable(current, binding.variable, binding.kind, coercevariable(raw, binding.kind), now);
3943
+ produced.push(binding.variable);
3944
+ }
3945
+ return { scopes: current, produced };
3946
+ }
3947
+ function operandvalue(operand, scopes) {
3948
+ if (operand.ref !== void 0) {
3949
+ const resolved = resolvevariable(scopes, operand.ref);
3950
+ if (!resolved) throw new Error(`The expression references the undefined variable ${operand.ref}.`);
3951
+ return resolved.value;
3952
+ }
3953
+ if (operand.literal === void 0) throw new Error("The expression operand needs a variable reference or a literal.");
3954
+ return operand.literal;
3955
+ }
3956
+ function expressioneval(expression, scopes) {
3957
+ const left = operandvalue(expression.left, scopes);
3958
+ const right = expression.right === void 0 ? void 0 : operandvalue(expression.right, scopes);
3959
+ const operand = (value) => {
3960
+ if (Array.isArray(value)) throw new Error("The expression operand is a list and needs the contains or length operator.");
3961
+ if (value === void 0) throw new Error("The expression operand is missing.");
3962
+ return value;
3963
+ };
3964
+ const numbervalue = (value) => {
3965
+ const primitive = operand(value);
3966
+ if (typeof primitive === "number") return primitive;
3967
+ if (typeof primitive === "string" && primitive.trim() !== "") {
3968
+ const parsed = Number(primitive);
3969
+ if (Number.isFinite(parsed)) return parsed;
3970
+ }
3971
+ throw new Error("The arithmetic operand is not a number.");
3972
+ };
3973
+ const booleanvalue = (value) => {
3974
+ const primitive = operand(value);
3975
+ if (typeof primitive === "boolean") return primitive;
3976
+ throw new Error("The logic operand is not a boolean.");
3977
+ };
3978
+ const stringvalue = (value) => {
3979
+ const primitive = operand(value);
3980
+ if (typeof primitive === "string") return primitive;
3981
+ if (typeof primitive === "number" || typeof primitive === "boolean") return String(primitive);
3982
+ throw new Error("The text operand is not a string.");
3983
+ };
3984
+ switch (expression.operator) {
3985
+ case "add":
3986
+ return numbervalue(left) + numbervalue(right);
3987
+ case "subtract":
3988
+ return numbervalue(left) - numbervalue(right);
3989
+ case "multiply":
3990
+ return numbervalue(left) * numbervalue(right);
3991
+ case "divide": {
3992
+ const divisor = numbervalue(right);
3993
+ if (divisor === 0) throw new Error("The expression divides by zero.");
3994
+ return numbervalue(left) / divisor;
3995
+ }
3996
+ case "modulo": {
3997
+ const divisor = numbervalue(right);
3998
+ if (divisor === 0) throw new Error("The expression divides by zero.");
3999
+ return numbervalue(left) % divisor;
4000
+ }
4001
+ case "equal":
4002
+ return left === right;
4003
+ case "notequal":
4004
+ return left !== right;
4005
+ case "less":
4006
+ return numbervalue(left) < numbervalue(right);
4007
+ case "greater":
4008
+ return numbervalue(left) > numbervalue(right);
4009
+ case "lessequal":
4010
+ return numbervalue(left) <= numbervalue(right);
4011
+ case "greaterequal":
4012
+ return numbervalue(left) >= numbervalue(right);
4013
+ case "and":
4014
+ return booleanvalue(left) && booleanvalue(right);
4015
+ case "or":
4016
+ return booleanvalue(left) || booleanvalue(right);
4017
+ case "not":
4018
+ return !booleanvalue(left);
4019
+ case "concat":
4020
+ return `${stringvalue(left)}${stringvalue(right)}`;
4021
+ case "contains": {
4022
+ if (Array.isArray(left)) return left.includes(stringvalue(right));
4023
+ return stringvalue(left).includes(stringvalue(right));
4024
+ }
4025
+ case "length": {
4026
+ if (Array.isArray(left)) return left.length;
4027
+ return stringvalue(left).length;
4028
+ }
4029
+ default:
4030
+ throw new Error("The reviewed expression operator is unknown.");
4031
+ }
4032
+ }
4033
+ function regexextract(rule, text2, now) {
4034
+ const pattern = new RegExp(rule.pattern, rule.flags);
4035
+ const match = pattern.exec(text2);
4036
+ if (!match) return { matched: false, variables: [] };
4037
+ const variables = [];
4038
+ for (const group of rule.groups) {
4039
+ const value = match.groups?.[group];
4040
+ variables.push({ name: group, kind: "string", value: typeof value === "string" ? value : "", setat: now });
4041
+ }
4042
+ return { matched: true, variables };
4043
+ }
4044
+ function waitelementplan(wait) {
4045
+ if (wait.timeout <= 0 || wait.poll <= 0) return { probes: 1, lastwait: 0 };
4046
+ const probes = Math.floor(wait.timeout / wait.poll) + 1;
4047
+ return { probes, lastwait: wait.timeout % wait.poll };
4048
+ }
4049
+ function delayjitter(delay, seed) {
4050
+ if (delay.jitter <= 0) return Math.max(0, delay.base);
4051
+ const sample = seededrandom(seed);
4052
+ return Math.max(0, delay.base - delay.jitter / 2 + sample * delay.jitter);
4053
+ }
4054
+ function seededrandom(seed) {
4055
+ let state = seed >>> 0;
4056
+ state ^= state >>> 16;
4057
+ state = Math.imul(state, 2246822507);
4058
+ state ^= state >>> 13;
4059
+ state = Math.imul(state, 3266489909);
4060
+ state ^= state >>> 16;
4061
+ state = state >>> 0 || 1;
4062
+ state ^= state << 13;
4063
+ state >>>= 0;
4064
+ state ^= state >> 17;
4065
+ state ^= state << 5;
4066
+ state >>>= 0;
4067
+ return state / 4294967296;
4068
+ }
4069
+ function newworkflowrun(input) {
4070
+ return { id: input.id ?? crypto.randomUUID(), workflowid: input.workflowid, state: "pending", cursor: 0, startedat: input.now, ...input.dryrun === true ? { dryrun: true } : {} };
4071
+ }
4072
+ function pauserun(run, now) {
4073
+ if (run.state !== "running") throw new Error("Only a running workflow can pause.");
4074
+ return { ...run, state: "paused", pausedat: now };
4075
+ }
4076
+ function cancelrun(run, reason, now) {
4077
+ if (run.state === "done" || run.state === "cancelled") return run;
4078
+ return { ...run, state: "cancelled", cancelreason: reason, endedat: now };
4079
+ }
4080
+ function interpolate(text2, scopes) {
4081
+ const consumed = [];
4082
+ const resolved = text2.replace(/\$\{([a-z][a-z0-9]*)\}/g, (_whole, name) => {
4083
+ const variable = resolvevariable(scopes, name);
4084
+ if (!variable) throw new Error(`The step references the undefined variable ${name}.`);
4085
+ consumed.push(name);
4086
+ return Array.isArray(variable.value) ? variable.value.join(",") : String(variable.value);
4087
+ });
4088
+ return { text: resolved, consumed };
4089
+ }
4090
+ function runlogof(step, state, startedat, duration, summary, extra) {
4091
+ return { stepid: step.id, label: step.label, state, startedat, duration, summary, ...extra.block !== void 0 ? { block: extra.block } : {}, ...extra.consumed !== void 0 && extra.consumed.length > 0 ? { consumed: extra.consumed } : {}, ...extra.produced !== void 0 && extra.produced.length > 0 ? { produced: extra.produced } : {}, ...extra.checkpoint === true ? { checkpoint: true } : {}, ...extra.details !== void 0 ? { details: extra.details } : {} };
4092
+ }
4093
+ async function runstep(input) {
4094
+ const startedat = input.now;
4095
+ let scopes = input.scopes;
4096
+ const consumed = [];
4097
+ if (input.step.bindings) {
4098
+ const bound = bindvariables(scopes, input.step.bindings.filter((binding) => input.outputs[binding.stepid] !== void 0), input.outputs, input.now);
4099
+ scopes = bound.scopes;
4100
+ }
4101
+ let produced = [];
4102
+ try {
4103
+ if (input.step.expression) {
4104
+ const value2 = expressioneval(input.step.expression, scopes);
4105
+ scopes = setvariable(scopes, input.step.expression.result, input.step.expression.resultkind, coercevariable(value2, input.step.expression.resultkind), input.now);
4106
+ produced = [...produced, input.step.expression.result];
4107
+ }
4108
+ let stepvalue = input.step.value;
4109
+ if (input.step.extract) {
4110
+ const text2 = stepvalue ?? "";
4111
+ const interpolated = interpolate(text2, scopes);
4112
+ consumed.push(...interpolated.consumed);
4113
+ const extraction = regexextract(input.step.extract, interpolated.text, input.now);
4114
+ if (extraction.matched) {
4115
+ for (const variable of extraction.variables) scopes = setvariable(scopes, variable.name, "string", variable.value, input.now);
4116
+ produced = [...produced, ...extraction.variables.map((variable) => variable.name)];
4117
+ }
4118
+ stepvalue = interpolated.text;
4119
+ }
4120
+ const target = input.step.target !== void 0 ? interpolate(input.step.target, scopes) : void 0;
4121
+ if (target) consumed.push(...target.consumed);
4122
+ const value = stepvalue !== void 0 ? interpolate(stepvalue, scopes) : void 0;
4123
+ if (value) consumed.push(...value.consumed);
4124
+ const options = input.step.options !== void 0 ? interpolate(input.step.options, scopes) : void 0;
4125
+ if (options) consumed.push(...options.consumed);
4126
+ const dispatchable = { ...input.step, ...target !== void 0 ? { target: target.text } : {}, ...value !== void 0 ? { value: value.text } : {}, ...options !== void 0 ? { options: options.text } : {} };
4127
+ const output = await input.execute(dispatchable, { scopes, ...input.block !== void 0 ? { block: input.block } : {} });
4128
+ if (input.step.bindings) {
4129
+ const bound = bindvariables(scopes, input.step.bindings, { ...input.outputs, [input.step.id]: { stepid: input.step.id, ok: output.ok, summary: output.summary, ...output.details !== void 0 ? { details: output.details } : {}, at: input.now } }, input.now);
4130
+ scopes = bound.scopes;
4131
+ produced = [.../* @__PURE__ */ new Set([...produced, ...bound.produced])];
4132
+ }
4133
+ const duration = Date.now() - startedat;
4134
+ return { scopes, log: runlogof(input.step, output.ok ? "done" : "failed", startedat, duration, output.summary, { ...input.block !== void 0 ? { block: input.block } : {}, ...consumed.length > 0 ? { consumed } : {}, ...produced.length > 0 ? { produced } : {}, ...output.details !== void 0 ? { details: output.details } : {}, ...output.ok ? { checkpoint: true } : {} }), output };
4135
+ } catch (error) {
4136
+ const duration = Date.now() - startedat;
4137
+ const summary = error instanceof Error ? error.message : String(error);
4138
+ return { scopes, log: runlogof(input.step, "failed", startedat, duration, summary, { ...input.block !== void 0 ? { block: input.block } : {}, ...consumed.length > 0 ? { consumed } : {} }), output: { ok: false, summary } };
4139
+ }
4140
+ }
4141
+ async function runworkflow(input) {
4142
+ if (input.gates && !input.gates.sessionactive) throw new Error("The workflow refuses to run outside an approved session.");
4143
+ if (input.gates && !input.gates.planapproved) throw new Error("The workflow refuses to run without the approved plan review.");
4144
+ if (input.gates) for (const origin of input.record.origins) {
4145
+ if (!input.gates.origingranted(origin)) throw new Error(`The workflow origin ${origin} falls outside the session grants.`);
4146
+ }
4147
+ if (input.run.state === "done" || input.run.state === "failed" || input.run.state === "cancelled") throw new Error(`The workflow run is already ${input.run.state}.`);
4148
+ const { pausedat, ...resumed } = input.run;
4149
+ void pausedat;
4150
+ let run = input.run.state === "paused" ? { ...resumed, state: "running" } : { ...input.run, state: "running" };
4151
+ let scopes = input.scopes ?? [{ name: "root", variables: [] }];
4152
+ const log = [...input.log ?? []];
4153
+ const outputs = { ...input.outputs ?? {} };
4154
+ let activeblock;
4155
+ for (let index = run.cursor; index < input.record.steps.length; index += 1) {
4156
+ const step = input.record.steps[index];
4157
+ if (step.block !== void 0 && step.block !== activeblock) {
4158
+ scopes = pushscope(scopes, step.block, scopes[scopes.length - 1].name);
4159
+ activeblock = step.block;
4160
+ } else if (step.block === void 0 && activeblock !== void 0) {
4161
+ while (scopes.length > 1) scopes = popscope(scopes);
4162
+ activeblock = void 0;
4163
+ }
4164
+ const executed = await runstep({ step, scopes, outputs, execute: input.execute, now: Date.now(), ...step.block !== void 0 ? { block: step.block } : {} });
4165
+ scopes = executed.scopes;
4166
+ log.push(executed.log);
4167
+ outputs[step.id] = { stepid: step.id, ok: executed.output.ok, summary: executed.output.summary, ...executed.output.details !== void 0 ? { details: executed.output.details } : {}, at: Date.now() };
4168
+ if (!executed.output.ok) {
4169
+ run = { ...run, state: "failed", endedat: Date.now(), failreason: executed.output.summary };
4170
+ return { run, scopes, log, outputs };
4171
+ }
4172
+ run = { ...run, cursor: index + 1 };
4173
+ if (input.oncheckpoint) await input.oncheckpoint({ run, scopes, log });
4174
+ }
4175
+ run = { ...run, state: "done", endedat: Date.now() };
4176
+ return { run, scopes, log, outputs };
4177
+ }
4178
+ function dryrunworkflow(input) {
4179
+ const run = { ...input.run, state: "running", ...input.run.dryrun === true ? { dryrun: true } : { dryrun: true } };
4180
+ let scopes = input.scopes ?? [{ name: "root", variables: [] }];
4181
+ const log = [...input.log ?? []];
4182
+ for (let index = run.cursor; index < input.record.steps.length; index += 1) {
4183
+ const step = input.record.steps[index];
4184
+ const summary = input.projection(step);
4185
+ const entry = summary === void 0 ? runlogof(step, "refused", input.now, 0, `The ${step.kind} step has no read only projection and the dry run refuses it.`, { ...step.block !== void 0 ? { block: step.block } : {} }) : runlogof(step, "done", input.now, 0, summary, { ...step.block !== void 0 ? { block: step.block } : {} });
4186
+ log.push(entry);
4187
+ scopes = setvariable(scopes, `${step.id}outcome`, "boolean", entry.state === "done", input.now);
4188
+ }
4189
+ return { run: { ...run, state: "done", cursor: input.record.steps.length, endedat: input.now }, scopes, log };
4190
+ }
4191
+
3317
4192
  // runtimeline.ts
3318
4193
  var timelinekinds = ["watchconsole", "watcherrors", "watchtasks"];
3319
4194
  var loglevels = ["error", "warn", "info", "log", "debug", "trace"];
@@ -3724,9 +4599,9 @@ function polldecision(input) {
3724
4599
  }
3725
4600
 
3726
4601
  // policy.ts
3727
- var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts", "recordscreen", "captureaudio", "downloadimages", "callrest", "callgraphql", "sendmessage", "blockrequest", "mockresponse", "rewriteheaders", "setcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles", "attachcdp", "detachcdp", "cdpcmd", "overridescript", "heapshot", "profilecpu", "capturesourcemaps", "emulatedevice", "emulatenetwork", "emulatelocate", "setuseragent", "overridepermission"]);
4602
+ var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts", "recordscreen", "captureaudio", "downloadimages", "callrest", "callgraphql", "sendmessage", "blockrequest", "mockresponse", "rewriteheaders", "setcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles", "attachcdp", "detachcdp", "cdpcmd", "overridescript", "heapshot", "profilecpu", "capturesourcemaps", "emulatedevice", "emulatenetwork", "emulatelocate", "setuseragent", "overridepermission", "restoresession", "exportsessions", "importsessions", "runworkflow"]);
3728
4603
  var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction", "capturebodies", "setbreakpoint", "stepcode", "watchexpr"]);
3729
- var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml", "opensocket", "waitmessage", "watchrequests", "readheaders", "mapapi", "subscribesse", "longpoll", "extractapi", "readcookies", "watchconsole", "watcherrors", "watchtasks", "watchcdp", "measureflow", "trackmemory", "watchshifts", "traceload", "annotatetrace", "replaytrace", "blackboxscripts"]);
4604
+ var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml", "opensocket", "waitmessage", "watchrequests", "readheaders", "mapapi", "subscribesse", "longpoll", "extractapi", "readcookies", "watchconsole", "watcherrors", "watchtasks", "watchcdp", "measureflow", "trackmemory", "watchshifts", "traceload", "annotatetrace", "replaytrace", "blackboxscripts", "persiststate", "capturesession", "namedsessions", "diffsessions", "searchsessions", "composeworkflow", "savetemplate", "dryrun", "delay", "waitelement", "compute", "extractvars"]);
3730
4605
  var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
3731
4606
  var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
3732
4607
  var targetactions = /* @__PURE__ */ new Set(["inspect", "focus", "click", "type", "scroll", "select", "hover", "clickdeep", "rightclick", "doubleclick", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "highlight", "setattribute", "removeattribute", "waitfor", "shiftclick", "typetime", "appendtext", "setvalue", "typeedit", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "expanddetails", "verifyvisible", "verifyenabled", "pierceshadow", "deriveselector", "fingerprintsection", "submitform", "retryform", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcode", "consentpassword", "scrapetable", "paginateextract", "shotelement", "captureframe", "shotcanvas"]);
@@ -3746,6 +4621,8 @@ var debugactions = /* @__PURE__ */ new Set(["watchconsole", "watcherrors", "watc
3746
4621
  var cdpactions = /* @__PURE__ */ new Set(["attachcdp", "detachcdp", "cdpcmd", "watchcdp", "setbreakpoint", "stepcode", "watchexpr", "overridescript"]);
3747
4622
  var profileractions = /* @__PURE__ */ new Set(["measureflow", "heapshot", "trackmemory", "profilecpu", "watchshifts", "traceload", "annotatetrace", "replaytrace", "capturesourcemaps"]);
3748
4623
  var emulationactions = /* @__PURE__ */ new Set(["emulatedevice", "emulatenetwork", "emulatelocate", "setuseragent", "overridepermission", "blackboxscripts"]);
4624
+ var sessionactions = /* @__PURE__ */ new Set(["persiststate", "capturesession", "restoresession", "namedsessions", "diffsessions", "searchsessions", "exportsessions", "importsessions"]);
4625
+ var workflowactions = /* @__PURE__ */ new Set(["composeworkflow", "savetemplate", "runworkflow", "dryrun", "delay", "waitelement", "compute", "extractvars"]);
3749
4626
  var credentialheaders = /* @__PURE__ */ new Set(["authorization", "proxy-authorization", "cookie", "cookie2", "set-cookie", "api-key", "x-api-key", "x-auth-token", "x-session-token", "proxy-authorization"]);
3750
4627
  var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
3751
4628
  var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
@@ -3761,6 +4638,12 @@ function hostpattern(origin) {
3761
4638
  if (parsed.protocol !== "https:") throw new Error("Only HTTPS origins can be granted.");
3762
4639
  return `${parsed.origin}/*`;
3763
4640
  }
4641
+ function issessionkind(kind) {
4642
+ return sessionactions.has(kind);
4643
+ }
4644
+ function isworkflowkind(kind) {
4645
+ return workflowactions.has(kind);
4646
+ }
3764
4647
  function iswatchkind(kind) {
3765
4648
  return watchactions.has(kind);
3766
4649
  }
@@ -5135,6 +6018,253 @@ function permissionnamevalid(name) {
5135
6018
  if (!browserpermissions.includes(name)) return { allowed: false, reason: `The permission ${name} stays outside the reviewed browser permission set: ${browserpermissions.join(", ")}.` };
5136
6019
  return { allowed: true };
5137
6020
  }
6021
+ function validatesessiongrammar(step, options) {
6022
+ const kind = step.kind;
6023
+ if (kind === "persiststate") {
6024
+ if (options.resume !== void 0 && typeof options.resume !== "boolean") return { allowed: false, reason: "The reviewed resume flag must be a boolean." };
6025
+ return { allowed: true };
6026
+ }
6027
+ if (kind === "capturesession") {
6028
+ const plan = snapshotplanof(options.snapshot);
6029
+ if (!plan) return { allowed: false, reason: "The session capture needs a reviewed snapshot plan with its scope, a non-empty section list of the reviewed grammar (tabs, scroll, forms, storage, cookies) and the capture link flag." };
6030
+ if (plan.auto !== void 0) {
6031
+ const interval = autointervalof(options.snapshot.auto);
6032
+ if (interval === void 0) return { allowed: false, reason: "The reviewed auto snapshot interval needs a positive period, a positive maximum snapshot count and a zero or positive expiry window with no code ceiling." };
6033
+ }
6034
+ return { allowed: true };
6035
+ }
6036
+ if (kind === "restoresession") {
6037
+ if (typeof options.sessionid !== "string" || !options.sessionid.trim()) return { allowed: false, reason: "The session restore needs the reviewed session id of the saved record." };
6038
+ if (restoreplanof(options.restore) === void 0) return { allowed: false, reason: "The session restore needs a reviewed restore plan with its tab, form and capture policies." };
6039
+ if (options.reviewed !== true) return { allowed: false, reason: "Every session restore needs the explicit restore review with its tabs, form state and captures listed before it reopens anything." };
6040
+ return { allowed: true };
6041
+ }
6042
+ if (kind === "namedsessions") {
6043
+ if (typeof options.sessionid !== "string" || !options.sessionid.trim()) return { allowed: false, reason: "The session filing needs the reviewed session id of the saved record." };
6044
+ if (typeof options.name !== "string" || !options.name.trim()) return { allowed: false, reason: "The session filing needs a reviewed non-empty session name." };
6045
+ if (options.folder !== void 0 && (typeof options.folder !== "string" || !options.folder.trim())) return { allowed: false, reason: "The reviewed folder name must be a non-empty string." };
6046
+ if (options.tags !== void 0 && (!Array.isArray(options.tags) || !options.tags.every((tag) => typeof tag === "string" && tag.trim()))) return { allowed: false, reason: "The reviewed tag list must be a list of non-empty strings." };
6047
+ return { allowed: true };
6048
+ }
6049
+ if (kind === "diffsessions") {
6050
+ if (typeof options.left !== "string" || !options.left.trim() || typeof options.right !== "string" || !options.right.trim()) return { allowed: false, reason: "The session diff needs the reviewed ids of both saved sessions." };
6051
+ return { allowed: true };
6052
+ }
6053
+ if (kind === "searchsessions") {
6054
+ if (searchqueryof(options.query) === void 0) return { allowed: false, reason: "The session search needs a reviewed query with a non-empty term list, fields of the reviewed grammar (urls, titles, names, text) and an optional time window." };
6055
+ return { allowed: true };
6056
+ }
6057
+ if (kind === "exportsessions") {
6058
+ if (options.reviewed !== true) return { allowed: false, reason: "Session exports need the explicit export review before any session file leaves the device." };
6059
+ if (options.ids !== void 0 && (!Array.isArray(options.ids) || options.ids.length === 0 || !options.ids.every((id) => typeof id === "string" && id.trim()))) return { allowed: false, reason: "The reviewed export id list must be a non-empty list of saved session ids." };
6060
+ return { allowed: true };
6061
+ }
6062
+ if (kind === "importsessions") {
6063
+ if (options.reviewed !== true) return { allowed: false, reason: "Session imports need the explicit full record review before any record joins the library." };
6064
+ if (importsessionfile(options.file) === void 0) return { allowed: false, reason: "The session import needs a reviewed file of the known format version with an intact checksum." };
6065
+ return { allowed: true };
6066
+ }
6067
+ return { allowed: true };
6068
+ }
6069
+ function restorereviewgranted(step) {
6070
+ let options = {};
6071
+ try {
6072
+ options = parseoptions(step);
6073
+ } catch {
6074
+ options = {};
6075
+ }
6076
+ if (restoreplanof(options.restore) === void 0) return { allowed: false, reason: "Every session restore needs a reviewed restore plan with its tab, form and capture policies." };
6077
+ if (options.reviewed !== true) return { allowed: false, reason: "The session restore needs the explicit restore review of its tabs, form state and captures before it reopens anything." };
6078
+ return { allowed: true };
6079
+ }
6080
+ function sessionrestoregate(input) {
6081
+ const gate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now: input.now, action: "run the session memory step" });
6082
+ if (!gate.allowed) return gate;
6083
+ if (!input.plan || input.plan.state !== "approved") return { allowed: false, reason: "Session memory steps need an approved plan before they run." };
6084
+ if (input.step.kind === "restoresession") return restorereviewgranted(input.step);
6085
+ return { allowed: true };
6086
+ }
6087
+ function restoreoriginsgranted(urls, grants) {
6088
+ const covered = new Set(grants);
6089
+ const skippedorigins = [];
6090
+ for (const url of urls) {
6091
+ let origin = "";
6092
+ try {
6093
+ origin = new URL(url).origin;
6094
+ } catch {
6095
+ origin = "";
6096
+ }
6097
+ if (!origin || !covered.has(origin)) skippedorigins.push(origin || url);
6098
+ }
6099
+ return { allowed: skippedorigins.length === 0, skippedorigins: [...new Set(skippedorigins)] };
6100
+ }
6101
+ function sessionnameunique(name, records, recordid) {
6102
+ if (records.some((record2) => record2.name === name && record2.id !== recordid)) return { allowed: false, reason: `The session name ${name} already exists in the library; review a unique name.` };
6103
+ return { allowed: true };
6104
+ }
6105
+ function sessionfolderunique(name, folders) {
6106
+ if (folders.some((folder) => folder.name === name)) return { allowed: false, reason: `The folder name ${name} already exists in the library; review a unique folder name.` };
6107
+ return { allowed: true };
6108
+ }
6109
+ function snapshotretentionwindow(settings) {
6110
+ return settings?.sessionretention;
6111
+ }
6112
+ function validateworkflowgrammar(step, options) {
6113
+ const kind = step.kind;
6114
+ if (kind === "composeworkflow") {
6115
+ const payload = options.workflow;
6116
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return { allowed: false, reason: "The workflow composition needs the reviewed workflow payload with its name, version, origins, steps and blocks." };
6117
+ const candidate = payload;
6118
+ if (typeof candidate.name !== "string" || !candidate.name.trim()) return { allowed: false, reason: "The workflow composition needs a reviewed non-empty name." };
6119
+ if (typeof candidate.version !== "number" || !Number.isInteger(candidate.version) || candidate.version < 1) return { allowed: false, reason: "The workflow version must be a positive integer." };
6120
+ if (!Array.isArray(candidate.origins) || candidate.origins.length === 0 || !candidate.origins.every((origin) => typeof origin === "string" && origin.startsWith("https://"))) return { allowed: false, reason: "The workflow needs at least one granted HTTPS origin so every step stays inside the grants." };
6121
+ if (!Array.isArray(candidate.steps) || candidate.steps.length === 0 || !candidate.steps.every((entry) => workflowstepof(entry) !== void 0 || entry && typeof entry === "object" && typeof entry.block === "string")) return { allowed: false, reason: "The workflow needs a non-empty reviewed step list of the workflow step grammar or block invocations." };
6122
+ const blocks = Array.isArray(candidate.blocks) ? candidate.blocks.flatMap((block) => {
6123
+ const parsed = workflowblockof(block);
6124
+ return parsed !== void 0 ? [parsed] : [];
6125
+ }) : [];
6126
+ if (Array.isArray(candidate.blocks) && blocks.length !== candidate.blocks.length) return { allowed: false, reason: "The reviewed block list must carry unique lowercase names, labels and valid child steps." };
6127
+ try {
6128
+ const record2 = composeworkflow({ name: candidate.name, version: candidate.version, origins: candidate.origins, steps: candidate.steps.map((entry) => "block" in entry ? { block: entry.block, label: typeof entry.label === "string" ? entry.label : entry.block } : workflowstepof(entry)), blocks, now: 0, kindallowed: (candidatekind) => {
6129
+ try {
6130
+ actionrisk(candidatekind);
6131
+ return true;
6132
+ } catch {
6133
+ return false;
6134
+ }
6135
+ }, riskof: (candidatekind) => actionrisk(candidatekind) });
6136
+ const inputs = Array.isArray(candidate.inputs) ? candidate.inputs.flatMap((name) => typeof name === "string" ? [name] : []) : void 0;
6137
+ const checked = validateworkflow(record2, { kindallowed: (workflowkind) => {
6138
+ try {
6139
+ actionrisk(workflowkind);
6140
+ return true;
6141
+ } catch {
6142
+ return false;
6143
+ }
6144
+ }, ...inputs !== void 0 ? { inputs } : {} });
6145
+ if (!checked.allowed) return checked;
6146
+ } catch (error) {
6147
+ return { allowed: false, reason: error instanceof Error ? error.message : "The workflow payload failed its composition validation." };
6148
+ }
6149
+ return { allowed: true };
6150
+ }
6151
+ if (kind === "savetemplate") {
6152
+ const payload = options.template && typeof options.template === "object" && !Array.isArray(options.template) ? options.template : {};
6153
+ const template = steptemplateof({ id: "templatereview", origin: "https://example.com", sharedat: 0, ...payload });
6154
+ if (!template) return { allowed: false, reason: "The step template needs a reviewed name and a valid workflow step it shares across workflows." };
6155
+ return { allowed: true };
6156
+ }
6157
+ if (kind === "runworkflow") {
6158
+ if (typeof options.workflowid !== "string" || !options.workflowid.trim()) return { allowed: false, reason: "The workflow run needs the reviewed id of the composed workflow." };
6159
+ if (options.reviewed !== true) return { allowed: false, reason: "Every real workflow run needs the explicit run review with its expanded step list shown before the first step executes." };
6160
+ if (options.variables !== void 0 && (!options.variables || typeof options.variables !== "object" || Array.isArray(options.variables) || !Object.values(options.variables).every((value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean"))) return { allowed: false, reason: "The reviewed run variables must be an object of string, number or boolean values." };
6161
+ return { allowed: true };
6162
+ }
6163
+ if (kind === "dryrun") {
6164
+ if (typeof options.workflowid !== "string" || !options.workflowid.trim()) return { allowed: false, reason: "The dry run needs the reviewed id of the composed workflow." };
6165
+ return { allowed: true };
6166
+ }
6167
+ if (kind === "delay") {
6168
+ const delay = options.delay;
6169
+ if (!delay || typeof delay !== "object" || Array.isArray(delay)) return { allowed: false, reason: "The delay needs a reviewed base and jitter window in options." };
6170
+ const reviewed = delay;
6171
+ if (typeof reviewed.base !== "number" || !Number.isFinite(reviewed.base) || reviewed.base < 0) return { allowed: false, reason: "The reviewed delay base must be zero or a positive number of milliseconds." };
6172
+ if (typeof reviewed.jitter !== "number" || !Number.isFinite(reviewed.jitter) || reviewed.jitter < 0) return { allowed: false, reason: "The reviewed delay jitter window must be zero or a positive number of milliseconds with no code ceiling." };
6173
+ return { allowed: true };
6174
+ }
6175
+ if (kind === "waitelement") {
6176
+ const wait = options.wait;
6177
+ if (!wait || typeof wait !== "object" || Array.isArray(wait)) return { allowed: false, reason: "The element wait needs a reviewed selector, timeout and poll interval in options." };
6178
+ const reviewed = wait;
6179
+ if (typeof reviewed.selector !== "string" || !reviewed.selector.trim()) return { allowed: false, reason: "The element wait needs a reviewed non-empty selector." };
6180
+ if (typeof reviewed.timeout !== "number" || !Number.isFinite(reviewed.timeout) || reviewed.timeout < 0) return { allowed: false, reason: "The reviewed element wait timeout must be zero or a positive number of milliseconds with no code ceiling." };
6181
+ if (typeof reviewed.poll !== "number" || !Number.isFinite(reviewed.poll) || reviewed.poll < 0) return { allowed: false, reason: "The reviewed element wait poll interval must be zero or a positive number of milliseconds with no code ceiling." };
6182
+ return { allowed: true };
6183
+ }
6184
+ if (kind === "compute") {
6185
+ const expression = expressionof(options.expression);
6186
+ if (!expression) return { allowed: false, reason: `The expression step needs a reviewed expression with operands, an operator of the reviewed set (${expressionoperators.join(", ")}) and a result variable of a reviewed kind.` };
6187
+ const operatorcheck = validatexpressionoperators(expression);
6188
+ if (!operatorcheck.allowed) return operatorcheck;
6189
+ return { allowed: true };
6190
+ }
6191
+ if (kind === "extractvars") {
6192
+ const rule = regexruleof(options.rule);
6193
+ if (!rule) return { allowed: false, reason: "The variable extraction needs a reviewed regex rule with its pattern, flags and named capture groups." };
6194
+ const shapecheck = validateregexrule(rule.pattern);
6195
+ if (!shapecheck.allowed) return shapecheck;
6196
+ if (typeof options.text !== "string") return { allowed: false, reason: "The variable extraction needs the reviewed text the regex rule applies to." };
6197
+ return { allowed: true };
6198
+ }
6199
+ return { allowed: true };
6200
+ }
6201
+ function validateregexrule(pattern) {
6202
+ try {
6203
+ new RegExp(pattern);
6204
+ } catch {
6205
+ return { allowed: false, reason: "The reviewed regex pattern does not compile." };
6206
+ }
6207
+ const nestedquantifier = /\((?:[^()\\]|\\.)*[+*}]\)[+*{]/.test(pattern) || /\(\)[+*{]/.test(pattern);
6208
+ if (nestedquantifier) return { allowed: false, reason: "The reviewed regex pattern nests an unbounded quantifier inside a quantified group and is refused because adversarial text could explode the backtracking." };
6209
+ const unboundedrepeat = /\{\d+,\}/.test(pattern);
6210
+ if (unboundedrepeat && /\([^)]*\{\d+,\}[^)]*\)[+*{]/.test(pattern)) return { allowed: false, reason: "The reviewed regex pattern repeats an unbounded group and is refused because adversarial text could explode the backtracking." };
6211
+ return { allowed: true };
6212
+ }
6213
+ function validatexpressionoperators(expression) {
6214
+ const numeric = /* @__PURE__ */ new Set(["add", "subtract", "multiply", "divide", "modulo"]);
6215
+ const logic = /* @__PURE__ */ new Set(["and", "or", "not"]);
6216
+ const comparison = /* @__PURE__ */ new Set(["less", "greater", "lessequal", "greaterequal"]);
6217
+ const text2 = /* @__PURE__ */ new Set(["concat", "contains"]);
6218
+ const operator = expression.operator;
6219
+ if (numeric.has(operator)) {
6220
+ for (const operand of [expression.left, expression.right]) {
6221
+ if (operand === void 0) continue;
6222
+ if (operand.literal !== void 0 && typeof operand.literal === "boolean") return { allowed: false, reason: `The ${operator} operator needs numeric operands; boolean literals are refused.` };
6223
+ }
6224
+ if (expression.resultkind !== "number" && expression.resultkind !== "string") return { allowed: false, reason: `The ${operator} operator needs a number result kind.` };
6225
+ }
6226
+ if (logic.has(operator)) {
6227
+ for (const operand of [expression.left, expression.right]) {
6228
+ if (operand === void 0) continue;
6229
+ if (operand.literal !== void 0 && typeof operand.literal !== "boolean") return { allowed: false, reason: `The ${operator} operator needs boolean operands; non boolean literals are refused.` };
6230
+ }
6231
+ if (expression.resultkind !== "boolean") return { allowed: false, reason: `The ${operator} operator needs a boolean result kind.` };
6232
+ if (operator === "not" && expression.right !== void 0) return { allowed: false, reason: "The not operator takes one operand only." };
6233
+ }
6234
+ if (comparison.has(operator) && expression.resultkind !== "boolean") return { allowed: false, reason: `The ${operator} operator needs a boolean result kind.` };
6235
+ if (text2.has(operator) && expression.resultkind !== "boolean" && expression.resultkind !== "string") return { allowed: false, reason: `The ${operator} operator needs a string or boolean result kind.` };
6236
+ if (operator === "contains" && expression.resultkind !== "boolean") return { allowed: false, reason: "The contains operator needs a boolean result kind." };
6237
+ if (operator === "length") {
6238
+ if (expression.right !== void 0) return { allowed: false, reason: "The length operator takes one operand only." };
6239
+ if (expression.resultkind !== "number") return { allowed: false, reason: "The length operator needs a number result kind." };
6240
+ }
6241
+ if ((operator === "equal" || operator === "notequal") && !(/* @__PURE__ */ new Set(["boolean", "string", "number"])).has(expression.resultkind)) return { allowed: false, reason: "The equality operator needs a primitive result kind." };
6242
+ return { allowed: true };
6243
+ }
6244
+ function workflowgate(input) {
6245
+ const gate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now: input.now, action: "run the workflow step" });
6246
+ if (!gate.allowed) return gate;
6247
+ if (!input.plan || input.plan.state !== "approved") return { allowed: false, reason: "Workflow steps need the approved plan review before they run." };
6248
+ if (input.step.kind === "runworkflow") {
6249
+ let runoptions = {};
6250
+ try {
6251
+ runoptions = parseoptions(input.step);
6252
+ } catch {
6253
+ runoptions = {};
6254
+ }
6255
+ if (runoptions.reviewed !== true) return { allowed: false, reason: "Every real workflow run needs the explicit run review with its expanded step list shown before the first step executes." };
6256
+ }
6257
+ return { allowed: true };
6258
+ }
6259
+ function dryrunprojection(step) {
6260
+ const risk = resolvedrisk({ id: step.id, kind: step.kind, summary: step.label, risk: "read", ...step.target !== void 0 ? { target: step.target } : {}, ...step.value !== void 0 ? { value: step.value } : {}, ...step.options !== void 0 ? { options: step.options } : {} });
6261
+ if (risk !== "read") return void 0;
6262
+ if (step.kind === "delay") return `The delay step would sleep its reviewed base inside the jitter window.`;
6263
+ if (step.kind === "waitelement") return `The element wait step would poll ${step.target ?? "the reviewed selector"} until appearance or the reviewed timeout.`;
6264
+ if (step.kind === "compute") return `The compute step would evaluate its reviewed expression into the result variable.`;
6265
+ if (step.kind === "extractvars") return `The variable extraction step would apply its reviewed regex rule and store the named captures.`;
6266
+ return `The ${step.kind} step would run read only and mutate nothing.`;
6267
+ }
5138
6268
  function permissionstatevalid(state) {
5139
6269
  if (!permissionstates.includes(state)) return { allowed: false, reason: `The reviewed permission state must be one of ${permissionstates.join(", ")}.` };
5140
6270
  return { allowed: true };
@@ -5700,6 +6830,14 @@ function validatestep(step, origin) {
5700
6830
  const emulationcheck = validateemulationgrammar(step, options);
5701
6831
  if (!emulationcheck.allowed) return emulationcheck;
5702
6832
  }
6833
+ if (issessionkind(step.kind)) {
6834
+ const sessioncheck = validatesessiongrammar(step, options);
6835
+ if (!sessioncheck.allowed) return sessioncheck;
6836
+ }
6837
+ if (isworkflowkind(step.kind)) {
6838
+ const workflowcheck = validateworkflowgrammar(step, options);
6839
+ if (!workflowcheck.allowed) return workflowcheck;
6840
+ }
5703
6841
  if (step.kind === "tabcreate") {
5704
6842
  if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
5705
6843
  if (options.window !== void 0 && (typeof options.window !== "number" || !Number.isInteger(options.window) || options.window < 0)) return { allowed: false, reason: "The reviewed target window id must be a non-negative integer." };
@@ -5873,6 +7011,27 @@ function canexecute(input) {
5873
7011
  const emugatecheck = emugate({ session: input.session, plan: input.plan, step: input.step, tabid: input.tabid, origin: input.origin, now });
5874
7012
  if (!emugatecheck.allowed) return emugatecheck;
5875
7013
  }
7014
+ if (issessionkind(input.step.kind)) {
7015
+ const sessiongatecheck = sessionrestoregate({ session: input.session, plan: input.plan, step: input.step, tabid: input.tabid, origin: input.origin, now });
7016
+ if (!sessiongatecheck.allowed) return sessiongatecheck;
7017
+ if (input.step.kind === "restoresession") {
7018
+ let restoreoptions = {};
7019
+ try {
7020
+ restoreoptions = parseoptions(input.step);
7021
+ } catch {
7022
+ restoreoptions = {};
7023
+ }
7024
+ for (const url of Array.isArray(restoreoptions.origins) ? restoreoptions.origins : []) {
7025
+ if (typeof url !== "string" || !url) continue;
7026
+ const origingate = origincheck(input.session, url);
7027
+ if (!origingate.allowed) return { allowed: false, reason: `The session restore reopens ${url} outside the session origin grants; review the restore record or grant the origin.` };
7028
+ }
7029
+ }
7030
+ }
7031
+ if (isworkflowkind(input.step.kind)) {
7032
+ const workflowgatecheck = workflowgate({ session: input.session, plan: input.plan, step: input.step, tabid: input.tabid, origin: input.origin, now });
7033
+ if (!workflowgatecheck.allowed) return workflowgatecheck;
7034
+ }
5876
7035
  if (iscontrolkind(input.step.kind)) {
5877
7036
  const controlgate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: "control the network" });
5878
7037
  if (!controlgate.allowed) return controlgate;
@@ -5956,7 +7115,7 @@ function canexecute(input) {
5956
7115
  }
5957
7116
 
5958
7117
  // version.ts
5959
- var packageversion = "1.1.48";
7118
+ var packageversion = "1.1.50";
5960
7119
 
5961
7120
  // types.ts
5962
7121
  var protocolversion = packageversion;
@@ -6150,6 +7309,51 @@ function parseproposal(value, origin, grants) {
6150
7309
  }
6151
7310
  if (step.kind === "overridepermission" && permissiongrantof(emulationoptions.permission) === void 0) throw new Error("Permission overrides of unknown permission names are refused.");
6152
7311
  }
7312
+ if (issessionkind(step.kind)) {
7313
+ let sessionoptions = {};
7314
+ try {
7315
+ sessionoptions = parseoptions(step);
7316
+ } catch {
7317
+ sessionoptions = {};
7318
+ }
7319
+ if (step.kind === "restoresession") {
7320
+ for (const url of Array.isArray(sessionoptions.origins) ? sessionoptions.origins : []) {
7321
+ if (typeof url !== "string" || !url) continue;
7322
+ const granted = covered.some((pattern) => {
7323
+ try {
7324
+ return new URL(url).origin === new URL(pattern).origin;
7325
+ } catch {
7326
+ return false;
7327
+ }
7328
+ });
7329
+ if (!granted) throw new Error(`The session restore reopens ${url} outside the grants.`);
7330
+ }
7331
+ }
7332
+ if (step.kind === "importsessions" && importsessionfile(sessionoptions.file) === void 0) throw new Error("Session import files of unknown format versions are refused.");
7333
+ }
7334
+ if (isworkflowkind(step.kind)) {
7335
+ let workflowoptions = {};
7336
+ try {
7337
+ workflowoptions = parseoptions(step);
7338
+ } catch {
7339
+ workflowoptions = {};
7340
+ }
7341
+ if (step.kind === "composeworkflow") {
7342
+ const payload = workflowoptions.workflow && typeof workflowoptions.workflow === "object" && !Array.isArray(workflowoptions.workflow) ? workflowoptions.workflow : void 0;
7343
+ const origins = payload && Array.isArray(payload.origins) ? payload.origins.filter((originvalue) => typeof originvalue === "string") : [];
7344
+ for (const workfloworigin of origins) {
7345
+ const granted = covered.some((pattern) => {
7346
+ try {
7347
+ return new URL(workfloworigin).origin === new URL(pattern).origin;
7348
+ } catch {
7349
+ return false;
7350
+ }
7351
+ });
7352
+ if (!granted) throw new Error(`The workflow origin ${workfloworigin} stays outside the grants.`);
7353
+ }
7354
+ }
7355
+ if (step.kind === "runworkflow" && workflowoptions.reviewed !== true) throw new Error("Workflow runs without the explicit run review of the expanded step list are refused.");
7356
+ }
6153
7357
  const evaluation = validatestep(step, origin);
6154
7358
  if (!evaluation.allowed) throw new Error(evaluation.reason);
6155
7359
  const target = outboundtarget(step);
@@ -6209,6 +7413,70 @@ function parseproposal(value, origin, grants) {
6209
7413
  };
6210
7414
  return { version: protocolversion, plan };
6211
7415
  }
7416
+ function parseworkflowproposal(value, origin, grants, dryrun) {
7417
+ const root = record(value);
7418
+ if (root.version !== protocolversion) throw new Error("Unsupported protocol version.");
7419
+ const covered = grants !== void 0 && grants.length > 0 ? grants : [origin];
7420
+ const candidate = record(root.workflow);
7421
+ const name = text(candidate.name, "workflow name");
7422
+ const version = typeof candidate.version === "number" && Number.isInteger(candidate.version) && candidate.version >= 1 ? candidate.version : void 0;
7423
+ if (version === void 0) throw new Error("The workflow version must be a positive integer.");
7424
+ const origins = Array.isArray(candidate.origins) ? candidate.origins : [];
7425
+ if (origins.length === 0 || !origins.every((workfloworigin) => typeof workfloworigin === "string" && workfloworigin.startsWith("https://"))) throw new Error("The workflow needs at least one granted HTTPS origin.");
7426
+ for (const workfloworigin of origins) {
7427
+ const granted = covered.some((pattern) => {
7428
+ try {
7429
+ return new URL(workfloworigin).origin === new URL(pattern).origin;
7430
+ } catch {
7431
+ return false;
7432
+ }
7433
+ });
7434
+ if (!granted) throw new Error(`The workflow origin ${workfloworigin} stays outside the grants.`);
7435
+ }
7436
+ const steps = Array.isArray(candidate.steps) ? candidate.steps : [];
7437
+ if (steps.length === 0) throw new Error("A workflow proposal needs at least one step or block invocation.");
7438
+ const blocks = Array.isArray(candidate.blocks) ? candidate.blocks.flatMap((block) => workflowblockof(block) !== void 0 ? [workflowblockof(block)] : []) : [];
7439
+ if (Array.isArray(candidate.blocks) && blocks.length !== candidate.blocks.length) throw new Error("The reviewed block list must carry unique lowercase names, labels and valid child steps.");
7440
+ const composed = composeworkflow({
7441
+ name,
7442
+ version,
7443
+ origins,
7444
+ steps: steps.map((entry) => {
7445
+ const step = workflowstepof(entry);
7446
+ if (step) return step;
7447
+ const invocation = blockinvocationof(entry);
7448
+ if (invocation) return invocation;
7449
+ throw new Error("Every workflow entry must be a reviewed step or a block invocation.");
7450
+ }),
7451
+ blocks,
7452
+ now: Date.now(),
7453
+ kindallowed: (kind) => {
7454
+ try {
7455
+ actionrisk(kind);
7456
+ return true;
7457
+ } catch {
7458
+ return false;
7459
+ }
7460
+ },
7461
+ riskof: (kind) => actionrisk(kind)
7462
+ });
7463
+ const inputs = Array.isArray(candidate.inputs) ? candidate.inputs.flatMap((inputname) => typeof inputname === "string" ? [inputname] : []) : void 0;
7464
+ const checked = validateworkflow(composed, { kindallowed: (kind) => {
7465
+ try {
7466
+ actionrisk(kind);
7467
+ return true;
7468
+ } catch {
7469
+ return false;
7470
+ }
7471
+ }, ...inputs !== void 0 ? { inputs } : {} });
7472
+ if (!checked.allowed) throw new Error(checked.reason ?? "The workflow proposal failed its validation.");
7473
+ return { version: protocolversion, workflow: composed, ...dryrun === true ? { dryrun: true } : {} };
7474
+ }
7475
+ function workflowoutcome(input) {
7476
+ const selected = input.stepid !== void 0 ? input.entries.filter((entry) => entry.stepid === input.stepid) : input.entries;
7477
+ const steps = selected.map((entry) => ({ stepid: entry.stepid, label: entry.label, state: entry.state, duration: entry.duration, summary: entry.summary, ...entry.block !== void 0 ? { block: entry.block } : {}, ...entry.produced !== void 0 ? { produced: entry.produced } : {}, ...entry.consumed !== void 0 ? { consumed: entry.consumed } : {}, ...entry.checkpoint === true ? { checkpoint: true } : {} }));
7478
+ return { version: protocolversion, runid: input.run.id, workflowid: input.run.workflowid, state: input.run.state, ...input.run.dryrun === true ? { dryrun: true } : {}, steps };
7479
+ }
6212
7480
  function stepof(kind, candidate, index) {
6213
7481
  return { id: typeof candidate.id === "string" ? candidate.id : `candidate${index + 1}`, kind, summary: typeof candidate.summary === "string" ? candidate.summary : "", risk: "read", ...typeof candidate.options === "string" ? { options: candidate.options } : {} };
6214
7482
  }
@@ -6224,7 +7492,7 @@ function requestbody(input) {
6224
7492
  return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation, capabilities: input.capabilities });
6225
7493
  }
6226
7494
  function outcomeresponse(input) {
6227
- return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {}, ...input.timeline ? { timeline: input.timeline } : {}, ...input.cdp ? { cdp: input.cdp } : {}, ...input.profile ? { profile: input.profile } : {}, ...input.emulation ? { emulation: input.emulation } : {} });
7495
+ return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {}, ...input.timeline ? { timeline: input.timeline } : {}, ...input.cdp ? { cdp: input.cdp } : {}, ...input.profile ? { profile: input.profile } : {}, ...input.emulation ? { emulation: input.emulation } : {}, ...input.session ? { session: input.session } : {}, ...input.workflow ? { workflow: { runid: input.workflow.runid, state: input.workflow.state, ...input.workflow.dryrun === true ? { dryrun: true } : {}, produced: input.workflow.produced, consumed: input.workflow.consumed } } : {} });
6228
7496
  }
6229
7497
  function mapresponse(input) {
6230
7498
  return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, map: input.map });
@@ -6370,6 +7638,12 @@ function emulationreport(input) {
6370
7638
  });
6371
7639
  return { version: protocolversion, ...input.state !== void 0 ? { state: input.state } : {}, layers: input.state?.layers ?? [], devices: input.devices, networks: input.networks, locations: input.locations, agents: input.agents, blackbox: input.blackbox, permissions: input.permissions, consents };
6372
7640
  }
7641
+ function sessionreport(input) {
7642
+ return { version: protocolversion, records: input.records, events: input.events, folders: input.folders, diffs: input.diffs, ...input.auto !== void 0 ? { auto: input.auto } : {}, ...input.crashed === true ? { crashed: true } : {} };
7643
+ }
7644
+ function workflowreport(input) {
7645
+ return { version: protocolversion, workflows: input.workflows, runs: input.runs, templates: input.templates, log: input.log ?? [], scopes: input.scopes ?? [], provenance: input.provenance ?? [] };
7646
+ }
6373
7647
  export {
6374
7648
  activelayers,
6375
7649
  agentgrammarvalid,
@@ -6391,12 +7665,15 @@ export {
6391
7665
  authconsentgranted,
6392
7666
  authorizeurl,
6393
7667
  authreport,
7668
+ autointervalof,
7669
+ bindvariables,
6394
7670
  blackboxedurls,
6395
7671
  blackboxmatches,
6396
7672
  blackboxruleof,
6397
7673
  blendrows,
6398
7674
  blockgate,
6399
7675
  blockingduration,
7676
+ blockinvocationof,
6400
7677
  blockruleof,
6401
7678
  bodyfilterof,
6402
7679
  bodymatches,
@@ -6411,6 +7688,7 @@ export {
6411
7688
  callgraphql,
6412
7689
  callrest,
6413
7690
  callsreport,
7691
+ cancelrun,
6414
7692
  canexecute,
6415
7693
  capturebody,
6416
7694
  capturecode,
@@ -6436,6 +7714,7 @@ export {
6436
7714
  channelorigin,
6437
7715
  closechannel,
6438
7716
  collectmessages,
7717
+ composeworkflow,
6439
7718
  consolecapture,
6440
7719
  consoleconsentcovers,
6441
7720
  consolediff,
@@ -6449,6 +7728,7 @@ export {
6449
7728
  cookierecordof,
6450
7729
  correlationid,
6451
7730
  cpusnap,
7731
+ crashinterrupted,
6452
7732
  croprect,
6453
7733
  crossesviewport,
6454
7734
  cursorfrom,
@@ -6457,12 +7737,16 @@ export {
6457
7737
  debuggerconsentcovers,
6458
7738
  debugwaitbudgetallowed,
6459
7739
  dedupeimages,
7740
+ delayjitter,
6460
7741
  actionrisk as deriveactionrisk,
6461
7742
  detachcdpsession,
6462
7743
  devicepresetof,
6463
7744
  diffresponse,
6464
7745
  diffreviewgrade,
7746
+ diffsessionrecords,
6465
7747
  downloadreport,
7748
+ dryrunprojection,
7749
+ dryrunworkflow,
6466
7750
  emugate,
6467
7751
  emulationkinds,
6468
7752
  emulationreport,
@@ -6473,15 +7757,22 @@ export {
6473
7757
  errorreportresponse,
6474
7758
  eventresponse,
6475
7759
  exchangesreport,
7760
+ expandblocks,
6476
7761
  expirelayers,
6477
7762
  expireprofilerecords,
7763
+ expiresessions,
6478
7764
  exportpresetlibrary,
7765
+ exportsessionfile,
7766
+ expressioneval,
7767
+ expressionof,
7768
+ expressionoperators,
6479
7769
  extractionreport,
6480
7770
  extractvalues,
6481
7771
  failureclass,
6482
7772
  familyofkind,
6483
7773
  fetchoptionsof,
6484
7774
  fetchrequestof,
7775
+ filteredsessions,
6485
7776
  filterentries,
6486
7777
  filterexchanges,
6487
7778
  finishrecording,
@@ -6509,6 +7800,7 @@ export {
6509
7800
  imagematches,
6510
7801
  imagenames,
6511
7802
  importpresetlibrary,
7803
+ importsessionfile,
6512
7804
  iscdpkind,
6513
7805
  iscontrolkind,
6514
7806
  isdebugkind,
@@ -6516,8 +7808,10 @@ export {
6516
7808
  isformkind,
6517
7809
  isnetwatchkind,
6518
7810
  isprofilekind,
7811
+ issessionkind,
6519
7812
  issocketkind,
6520
7813
  iswatchkind,
7814
+ isworkflowkind,
6521
7815
  jsonpathrulesof,
6522
7816
  lapseframes,
6523
7817
  lapseplanof,
@@ -6556,6 +7850,9 @@ export {
6556
7850
  newlayer,
6557
7851
  newmockspec,
6558
7852
  newrecording,
7853
+ newsessiondiff,
7854
+ newsessionrecord,
7855
+ newworkflowrun,
6559
7856
  normalizeendpoint,
6560
7857
  oauthflowof,
6561
7858
  observationmodeof,
@@ -6570,9 +7867,11 @@ export {
6570
7867
  parseproposal,
6571
7868
  parsessetext,
6572
7869
  parsetokens,
7870
+ parseworkflowproposal,
6573
7871
  passwordconsentgranted,
6574
7872
  patternorigin,
6575
7873
  pauseretentionwindow,
7874
+ pauserun,
6576
7875
  payloadshapeof,
6577
7876
  payloadvalid,
6578
7877
  payloadwithdefaults,
@@ -6589,6 +7888,7 @@ export {
6589
7888
  pollcursorof,
6590
7889
  polldecision,
6591
7890
  pollurl,
7891
+ popscope,
6592
7892
  privatemime,
6593
7893
  profilegrantgranted,
6594
7894
  profilereport,
@@ -6599,6 +7899,7 @@ export {
6599
7899
  proxygate,
6600
7900
  proxyrouteof,
6601
7901
  publishmessage,
7902
+ pushscope,
6602
7903
  quarantinereport,
6603
7904
  randomid,
6604
7905
  rankapis,
@@ -6613,6 +7914,8 @@ export {
6613
7914
  recordwatchvalue,
6614
7915
  redactconsoletext,
6615
7916
  redactedcookies,
7917
+ regexextract,
7918
+ regexruleof,
6616
7919
  regionsteps,
6617
7920
  rejectioncapture,
6618
7921
  replaytrace,
@@ -6620,7 +7923,11 @@ export {
6620
7923
  requestbody,
6621
7924
  resolutionverdict,
6622
7925
  resolvedrisk,
7926
+ resolvevariable,
6623
7927
  resourcefacts,
7928
+ restoreoriginsgranted,
7929
+ restoreplanof,
7930
+ restorereviewgranted,
6624
7931
  retryafterof,
6625
7932
  revertalllayers,
6626
7933
  revertlayer,
@@ -6630,18 +7937,34 @@ export {
6630
7937
  rewritesourcelocation,
6631
7938
  rotatelogs,
6632
7939
  rotationruleof,
7940
+ runstep,
7941
+ runworkflow,
6633
7942
  safetyresponse,
6634
7943
  scaledrect,
6635
7944
  seamweights,
7945
+ searchfields,
7946
+ searchqueryof,
7947
+ searchsessionrecords,
6636
7948
  selectorresponse,
6637
7949
  sendcdpcommand,
6638
7950
  sendfetch,
6639
7951
  sequenceintegrity,
6640
7952
  serializearg,
6641
7953
  serializecdpcommand,
7954
+ sessionfileversion,
7955
+ sessionfolderof,
7956
+ sessionfolderunique,
7957
+ sessionkinds,
6642
7958
  sessionmemory,
7959
+ sessionnameunique,
7960
+ sessionreport,
7961
+ sessionrestoregate,
7962
+ sessiontabof,
6643
7963
  shiftentryof,
6644
7964
  signalsreport,
7965
+ snapshotplanof,
7966
+ snapshotretentionwindow,
7967
+ snapshotsections,
6645
7968
  socketgate,
6646
7969
  socketkinds,
6647
7970
  sourcemapconsentcovers,
@@ -6653,6 +7976,7 @@ export {
6653
7976
  stackgate,
6654
7977
  statusclassof,
6655
7978
  stepmodeof,
7979
+ steptemplateof,
6656
7980
  stepwindows,
6657
7981
  streamsummaries,
6658
7982
  streamwindowof,
@@ -6660,6 +7984,9 @@ export {
6660
7984
  subscriptionoptionsof,
6661
7985
  tabreportresponse,
6662
7986
  targetgate,
7987
+ taskstatechecksum,
7988
+ taskstateof,
7989
+ taskstatevalid,
6663
7990
  teardowncdpsession,
6664
7991
  teardownplanof,
6665
7992
  templateurl,
@@ -6683,13 +8010,22 @@ export {
6683
8010
  validatebreakpointcondition,
6684
8011
  validatefieldmatch,
6685
8012
  validateformrecord,
8013
+ validateregexrule,
6686
8014
  validatestep,
6687
8015
  validatetargetref,
6688
8016
  validatevaluegen,
8017
+ validateworkflow,
8018
+ waitelementplan,
6689
8019
  watchcdpevents,
6690
8020
  watcherdetached,
6691
8021
  watchexpressionof,
6692
8022
  watchgate,
6693
- wizardreport
8023
+ wizardreport,
8024
+ workflowblockof,
8025
+ workflowgate,
8026
+ workflowkinds,
8027
+ workflowoutcome,
8028
+ workflowreport,
8029
+ workflowstepof
6694
8030
  };
6695
8031
  //# sourceMappingURL=index.js.map