@wenathlan/extension 1.1.33 → 1.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,17 +2,19 @@
2
2
 
3
3
  Devthink is a **consent-first browser-agent bridge** distributed as a TypeScript library and a Chromium Manifest V3 extension. It turns a user-provided browser objective into a bounded, reviewable plan. The user must start the active-tab session and approve the plan before any page action reaches the browser.
4
4
 
5
- Version: **1.1.33**. License: **GPL-3.0-only**. The repository is `wenathlan/extension`; the npm-compatible scoped package identifier is `@wenathlan/extension`.
5
+ Version: **1.1.34**. License: **GPL-3.0-only**. The repository is `wenathlan/extension`; the npm-compatible scoped package identifier is `@wenathlan/extension`.
6
6
 
7
7
  ## What it does
8
8
 
9
- | Capability | Behavior in 1.1.33 |
9
+ | Capability | Behavior in 1.1.34 |
10
10
  | --- | --- |
11
11
  | Active-tab session | The user starts a short-lived session for one HTTPS tab and one origin; the session records its origin grants. |
12
12
  | Page observation | The extension captures the complete semantic inventory: every interactive element, every form control, every select option and the full page text. |
13
13
  | Plan proposal | A local plan can be created immediately; an optional user-configured HTTPS endpoint can return a typed plan proposal of any length. |
14
14
  | Review gate | Every remote proposal starts in `pending`; it cannot reach the page bridge before explicit approval. |
15
- | Browser tools | Reviewed plans cover ninety action kinds: pointer paths and coordinate clicks, text, aria, name, xpath, index and point target resolution, timed typing, key holds, sliders, dates, colors, shadow dom piercing, iframe entry, dialog answering, retry rules, a complete read vocabulary with clickable maps and verification reads, page mutation under review, and browser-level tab, window, zoom, snapshot and download commands. |
15
+ | Browser tools | Reviewed plans cover one hundred eighteen action kinds: pointer paths and coordinate clicks, text, aria, name, xpath, index and point target resolution, timed typing, key holds, sliders, dates, colors, shadow dom piercing, iframe entry, dialog answering, retry rules, a complete read vocabulary with clickable maps and verification reads, page mutation under review, and browser-level tab, window, zoom, snapshot and download commands. |
16
+ | Observation depth | The agent sees the whole page read only: the accessibility tree beside the dom snapshot, reader views, visible text, outlines, selections, open graph and embedded json state, plus detected lists, tables, pagination, infinite scroll, virtualization, lazy images, sticky overlays and scroll locks offered as plan suggestions. |
17
+ | Watch vocabulary | `watchmutate`, `watchfocus` and `watchbanner` observe the page across a reviewed lifetime window with batched event records, `waitquiet` waits for network quiet under a reviewed threshold, `diffsnapshots` diffs two stored observation versions into added, removed and changed rows, and `deriveselector` ranks stable selector candidates with stability scores; registrations persist across service worker restarts. |
16
18
  | Target resolution | Steps address elements through reviewed `targetref` modes: css selector, visible text, aria role and name, accessible name, xpath, clickable map index or viewport point; ambiguous matches are refused with candidate lists and every resolution returns a matched element summary for review. |
17
19
  | Optional capabilities | `tabs`, `downloads`, `clipboardRead` and `clipboardWrite` are optional permissions; browser kinds check their capability, the review panel can request a grant and every grant is audited. |
18
20
  | Unlimited by choice | Wait durations, plan size, plan expiry, audit retention and outcome retention carry no code ceilings; every bound is a user choice. |
@@ -46,10 +48,10 @@ The endpoint field intentionally has no default URL. Enter a URL such as `https:
46
48
 
47
49
  ```json
48
50
  {
49
- "version": "1.1.33",
51
+ "version": "1.1.34",
50
52
  "objective": "User supplied objective",
51
53
  "session": { "id": "uuid", "tabid": 1, "origin": "https://example.com" },
52
- "observation": { "schemaversion": 2, "url": "https://example.com/path", "interactive": [] },
54
+ "observation": { "schemaversion": 3, "url": "https://example.com/path", "interactive": [] },
53
55
  "capabilities": { "tabs": false, "downloads": false, "clipboardread": false, "clipboardwrite": false }
54
56
  }
55
57
  ```
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from "./memory.js";
2
- export { actionrisk as deriveactionrisk, canexecute, hostpattern, normalizeendpoint, resolutionverdict, validatestep, validatetargetref } from "./policy.js";
2
+ export { actionrisk as deriveactionrisk, canexecute, hostpattern, iswatchkind, normalizeendpoint, observationmodeof, resolutionverdict, validatestep, validatetargetref } from "./policy.js";
3
3
  export * from "./protocol.js";
4
4
  export * from "./types.js";
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,UAAU,IAAI,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC7J,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,UAAU,IAAI,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC7L,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -128,6 +128,116 @@ var sessionmemory = class {
128
128
  async setdialogpolicy(policy) {
129
129
  return this.adapter.set("dialogpolicy", policy);
130
130
  }
131
+ /** Stores one observation capture under its version so every observation version stays available. */
132
+ async setobservation(record2) {
133
+ return this.adapter.set(`observation${record2.version}`, record2);
134
+ }
135
+ /** Returns one stored observation version. */
136
+ async getobservation(version) {
137
+ return this.adapter.get(`observation${version}`);
138
+ }
139
+ /** Returns the observation retention window; an absent setting keeps every capture. */
140
+ async observationretention() {
141
+ return (await this.getsettings())?.observationretention;
142
+ }
143
+ /** Stores one accessibility tree capture; retention is a user setting and an absent setting keeps every tree. */
144
+ async adda11ytree(capture) {
145
+ const records = await this.geta11ytrees();
146
+ const combined = [capture, ...records];
147
+ const retention = await this.observationretention();
148
+ await this.adapter.set("a11ytrees", retention === void 0 ? combined : combined.slice(0, retention));
149
+ }
150
+ /** Returns every stored accessibility tree capture, newest first. */
151
+ async geta11ytrees() {
152
+ return await this.adapter.get("a11ytrees") ?? [];
153
+ }
154
+ /** Stores one reader article capture; retention is a user setting and an absent setting keeps every article. */
155
+ async addreaderarticle(capture) {
156
+ const records = await this.getreaderarticles();
157
+ const combined = [capture, ...records];
158
+ const retention = await this.observationretention();
159
+ await this.adapter.set("readerarticles", retention === void 0 ? combined : combined.slice(0, retention));
160
+ }
161
+ /** Returns every stored reader article capture, newest first. */
162
+ async getreaderarticles() {
163
+ return await this.adapter.get("readerarticles") ?? [];
164
+ }
165
+ /** Records one dom mutation observed inside a reviewed watch. */
166
+ async addmutationevent(event) {
167
+ const records = await this.getmutationevents();
168
+ await this.adapter.set("mutationevents", [event, ...records]);
169
+ }
170
+ /** Returns the mutation event stream of every reviewed watch. */
171
+ async getmutationevents() {
172
+ return await this.adapter.get("mutationevents") ?? [];
173
+ }
174
+ /** Records one focus change observed inside a reviewed watch. */
175
+ async addfocusevent(event) {
176
+ const records = await this.getfocusevents();
177
+ await this.adapter.set("focusevents", [event, ...records]);
178
+ }
179
+ /** Returns the focus event stream of every reviewed watch. */
180
+ async getfocusevents() {
181
+ return await this.adapter.get("focusevents") ?? [];
182
+ }
183
+ /** Records one consent banner observed by a reviewed banner watch. */
184
+ async addbanner(event) {
185
+ const records = await this.getbanners();
186
+ await this.adapter.set("banners", [event, ...records]);
187
+ }
188
+ /** Returns every consent banner report observed so far. */
189
+ async getbanners() {
190
+ return await this.adapter.get("banners") ?? [];
191
+ }
192
+ /** Records one snapshot diff between two observation versions. */
193
+ async adddiff(diff) {
194
+ const records = await this.getdiffs();
195
+ await this.adapter.set("diffs", [diff, ...records]);
196
+ }
197
+ /** Returns every stored snapshot diff, newest first. */
198
+ async getdiffs() {
199
+ return await this.adapter.get("diffs") ?? [];
200
+ }
201
+ /** Records one derived selector with its stability score for reuse. */
202
+ async addselector(selector) {
203
+ const records = await this.getselectors();
204
+ await this.adapter.set("selectors", [selector, ...records]);
205
+ }
206
+ /** Returns every stored derived selector with its stability score, newest first. */
207
+ async getselectors() {
208
+ return await this.adapter.get("selectors") ?? [];
209
+ }
210
+ /** Records one detected template class or section fingerprint for its origin. */
211
+ async addtemplate(profile) {
212
+ const records = await this.gettemplates();
213
+ await this.adapter.set("templates", [profile, ...records]);
214
+ }
215
+ /** Returns every stored template class and section fingerprint, newest first. */
216
+ async gettemplates() {
217
+ return await this.adapter.get("templates") ?? [];
218
+ }
219
+ /** Records one watch registration so it survives service worker restarts. */
220
+ async addwatch(watch) {
221
+ const records = await this.getwatches();
222
+ await this.adapter.set("watches", [watch, ...records]);
223
+ }
224
+ /** Returns every watch registration, newest first, including closed windows. */
225
+ async getwatches() {
226
+ return await this.adapter.get("watches") ?? [];
227
+ }
228
+ /** Closes one watch registration by watch id once its reviewed lifetime window ends. */
229
+ async closewatch(watchid, closedat) {
230
+ const records = await this.getwatches();
231
+ await this.adapter.set("watches", records.map((watch) => watch.watchid === watchid && watch.closedat === void 0 ? { ...watch, closedat } : watch));
232
+ }
233
+ /** Returns the live page signals of language, template, scroll lock and banner state. */
234
+ async getsignals() {
235
+ return this.adapter.get("signals");
236
+ }
237
+ /** Replaces the live page signals after an observation step refreshes them. */
238
+ async setsignals(signals) {
239
+ return this.adapter.set("signals", signals);
240
+ }
131
241
  };
132
242
  function randomid() {
133
243
  return crypto.randomUUID();
@@ -136,9 +246,10 @@ function randomid() {
136
246
  // policy.ts
137
247
  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"]);
138
248
  var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction"]);
139
- 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"]);
249
+ 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"]);
140
250
  var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
141
- 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"]);
251
+ var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus"]);
252
+ 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"]);
142
253
  var valueactions = /* @__PURE__ */ new Set(["presskey", "drag", "drop", "upload", "readattribute", "removeattribute", "waittext", "evaluate", "zoomset", "tabactivate", "tabclose", "tabreload", "windowclose", "windowresize", "tabcreate", "windowcreate", "downloadfile", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "chooseradio", "setslider", "setdate", "setcolor"]);
143
254
  function normalizeendpoint(value) {
144
255
  const endpoint = new URL(value.trim());
@@ -151,6 +262,14 @@ function hostpattern(origin) {
151
262
  if (parsed.protocol !== "https:") throw new Error("Only HTTPS origins can be granted.");
152
263
  return `${parsed.origin}/*`;
153
264
  }
265
+ function iswatchkind(kind) {
266
+ return watchactions.has(kind);
267
+ }
268
+ function observationmodeof(kind) {
269
+ if (watchactions.has(kind) || kind === "waitquiet") return "watching";
270
+ if (kind === "diffsnapshots") return "diffing";
271
+ return "passive";
272
+ }
154
273
  function actionrisk(kind) {
155
274
  if (!allowedactions.has(kind)) throw new Error("Unsupported browser action.");
156
275
  if (sensitiveactions.has(kind)) return "sensitive";
@@ -361,6 +480,24 @@ function validatestep(step, origin) {
361
480
  if (!nonnegativeoption(retry, "settle")) return { allowed: false, reason: "The reviewed retry settle window must be zero or a positive number of milliseconds." };
362
481
  if (!nonnegativeoption(retry, "tolerance")) return { allowed: false, reason: "The reviewed retry movement tolerance must be zero or a positive number of pixels." };
363
482
  }
483
+ if (watchactions.has(step.kind)) {
484
+ if (typeof options.lifetime !== "number" || !Number.isFinite(options.lifetime) || options.lifetime <= 0) return { allowed: false, reason: "A reviewed watch lifetime window in milliseconds is required in options." };
485
+ if (options.scopes !== void 0 && (!Array.isArray(options.scopes) || !options.scopes.every((scope) => isnonempty(scope)))) return { allowed: false, reason: "The reviewed watch scopes must be a list of non-empty selectors." };
486
+ if (options.events !== void 0 && (!Array.isArray(options.events) || !options.events.every((event) => isnonempty(event)))) return { allowed: false, reason: "The reviewed watch event kinds must be a list of non-empty strings." };
487
+ if (!nonnegativeoption(options, "poll")) return { allowed: false, reason: "The reviewed watch poll interval must be zero or a positive number of milliseconds." };
488
+ }
489
+ if (step.kind === "waitquiet") {
490
+ const rule = options.quietrule;
491
+ if (!rule || typeof rule !== "object" || Array.isArray(rule)) return { allowed: false, reason: "A reviewed quietrule with an idle threshold is required in options." };
492
+ const quiet = rule;
493
+ if (typeof quiet.idle !== "number" || !Number.isFinite(quiet.idle) || quiet.idle <= 0) return { allowed: false, reason: "The reviewed quiet idle threshold must be a positive number of milliseconds with no code ceiling." };
494
+ if (!nonnegativeoption(quiet, "poll")) return { allowed: false, reason: "The reviewed quiet poll interval must be zero or a positive number of milliseconds." };
495
+ if (!nonnegativeoption(quiet, "timeout")) return { allowed: false, reason: "The reviewed quiet timeout must be zero or a positive number of milliseconds." };
496
+ }
497
+ if (step.kind === "diffsnapshots") {
498
+ const versions = options.versions;
499
+ if (!Array.isArray(versions) || versions.length !== 2 || !versions.every((version) => typeof version === "number" && Number.isInteger(version) && version >= 1)) return { allowed: false, reason: "Two reviewed observation version numbers are required in options." };
500
+ }
364
501
  return { allowed: true };
365
502
  }
366
503
  function sessiongate(input) {
@@ -377,11 +514,12 @@ function canexecute(input) {
377
514
  if (!input.plan || input.plan.state !== "approved") return { allowed: false, reason: "The plan has not received explicit approval." };
378
515
  if (input.plan.expiresat <= now) return { allowed: false, reason: "The approved plan has expired." };
379
516
  if ((input.step.kind === "pierceshadow" || input.step.kind === "enterframe") && !origingranted(input.session, input.origin)) return { allowed: false, reason: "The shadow or frame step is outside the session origin grants." };
517
+ if (input.step.kind === "readjson" && !origingranted(input.session, input.origin)) return { allowed: false, reason: "The json state read is outside the session origin grants." };
380
518
  return validatestep(input.step, input.origin);
381
519
  }
382
520
 
383
521
  // version.ts
384
- var packageversion = "1.1.33";
522
+ var packageversion = "1.1.34";
385
523
 
386
524
  // types.ts
387
525
  var protocolversion = packageversion;
@@ -448,20 +586,49 @@ function mapresponse(input) {
448
586
  function heldkeysreport(input) {
449
587
  return { version: protocolversion, tabid: input.tabid, heldkeys: input.holds };
450
588
  }
589
+ function observationresponse(input) {
590
+ return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, observation: input.observation });
591
+ }
592
+ function eventresponse(input) {
593
+ return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, events: input.events });
594
+ }
595
+ function diffresponse(input) {
596
+ return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, diff: input.diff });
597
+ }
598
+ function signalsreport(input) {
599
+ const signals = input.signals;
600
+ return {
601
+ version: protocolversion,
602
+ ...signals && signals.language !== void 0 ? { language: signals.language } : {},
603
+ ...signals && signals.template !== void 0 ? { template: signals.template } : {},
604
+ ...signals && signals.scrolllocked !== void 0 ? { scrolllocked: signals.scrolllocked } : {},
605
+ ...signals && signals.banner !== void 0 ? { banner: signals.banner } : {}
606
+ };
607
+ }
608
+ function selectorresponse(input) {
609
+ return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, candidates: input.candidates });
610
+ }
451
611
  export {
452
612
  canexecute,
453
613
  actionrisk as deriveactionrisk,
614
+ diffresponse,
615
+ eventresponse,
454
616
  heldkeysreport,
455
617
  hostpattern,
618
+ iswatchkind,
456
619
  mapresponse,
457
620
  normalizeendpoint,
621
+ observationmodeof,
622
+ observationresponse,
458
623
  outcomeresponse,
459
624
  parseproposal,
460
625
  protocolversion,
461
626
  randomid,
462
627
  requestbody,
463
628
  resolutionverdict,
629
+ selectorresponse,
464
630
  sessionmemory,
631
+ signalsreport,
465
632
  validatestep,
466
633
  validatetargetref
467
634
  };