@akshayram1/omnibrowser-agent 0.2.32 → 0.3.0

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/lib.js CHANGED
@@ -11,6 +11,7 @@ var __export = (target, all) => {
11
11
  // src/shared/parse-action.ts
12
12
  var parse_action_exports = {};
13
13
  __export(parse_action_exports, {
14
+ PARSE_FAILURE_PATTERN: () => PARSE_FAILURE_PATTERN,
14
15
  parseAction: () => parseAction,
15
16
  parsePlannerResult: () => parsePlannerResult
16
17
  });
@@ -66,6 +67,28 @@ function parseAction(raw) {
66
67
  if (typeof obj.type !== "string" || !VALID_TYPES.has(obj.type)) {
67
68
  return { type: "done", reason: `Unknown or missing action type: ${String(obj.type)}` };
68
69
  }
70
+ const t = obj.type;
71
+ if ((t === "click" || t === "type" || t === "extract" || t === "focus") && typeof obj.selector !== "string") {
72
+ return { type: "done", reason: `Missing required field 'selector' for action type: ${t}` };
73
+ }
74
+ if (t === "extract" && typeof obj.label !== "string") {
75
+ return { type: "done", reason: `Missing required field 'label' for action type: extract` };
76
+ }
77
+ if (t === "type" && typeof obj.text !== "string") {
78
+ return { type: "done", reason: `Missing required field 'text' for action type: type` };
79
+ }
80
+ if (t === "navigate" && typeof obj.url !== "string") {
81
+ return { type: "done", reason: `Missing required field 'url' for action type: navigate` };
82
+ }
83
+ if (t === "scroll" && typeof obj.deltaY !== "number") {
84
+ return { type: "done", reason: `Missing required field 'deltaY' for action type: scroll` };
85
+ }
86
+ if (t === "wait" && typeof obj.ms !== "number") {
87
+ return { type: "done", reason: `Missing required field 'ms' for action type: wait` };
88
+ }
89
+ if (t === "done" && typeof obj.reason !== "string") {
90
+ return { type: "done", reason: `Missing required field 'reason' for action type: done` };
91
+ }
69
92
  return obj;
70
93
  }
71
94
  function parsePlannerResult(raw) {
@@ -96,10 +119,11 @@ function parsePlannerResult(raw) {
96
119
  }
97
120
  return { action: parseAction(jsonStr) };
98
121
  }
99
- var VALID_TYPES;
122
+ var PARSE_FAILURE_PATTERN, VALID_TYPES;
100
123
  var init_parse_action = __esm({
101
124
  "src/shared/parse-action.ts"() {
102
125
  "use strict";
126
+ PARSE_FAILURE_PATTERN = /(No JSON|JSON parse error|Parsed value is not an object|Unknown or missing action type|Missing required field)/;
103
127
  VALID_TYPES = /* @__PURE__ */ new Set([
104
128
  "click",
105
129
  "type",
@@ -150,6 +174,9 @@ async function executeAction(action) {
150
174
  }
151
175
  case "type": {
152
176
  const input = mustFind(action.selector);
177
+ if (input.value === action.text) {
178
+ return `Already contains correct value in ${action.selector}`;
179
+ }
153
180
  input.focus();
154
181
  if (action.clearFirst) {
155
182
  input.value = "";
@@ -253,8 +280,8 @@ function cssPath(element) {
253
280
  return parts.join(" > ");
254
281
  }
255
282
  function isVisible(el) {
256
- if (el.offsetParent === null && el.tagName !== "BODY") return false;
257
283
  const style = window.getComputedStyle(el);
284
+ if (el.offsetParent === null && el.tagName !== "BODY" && style.position !== "fixed") return false;
258
285
  if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") return false;
259
286
  const rect = el.getBoundingClientRect();
260
287
  return rect.width > 0 || rect.height > 0;
@@ -263,6 +290,14 @@ function isInViewport(el) {
263
290
  const rect = el.getBoundingClientRect();
264
291
  return rect.bottom > 0 && rect.top < window.innerHeight && rect.right > 0 && rect.left < window.innerWidth;
265
292
  }
293
+ function isActiveElement(el) {
294
+ if (el.classList.contains("active")) return true;
295
+ if (el.getAttribute("aria-selected") === "true") return true;
296
+ const ariaCurrent = el.getAttribute("aria-current");
297
+ if (ariaCurrent && ariaCurrent !== "false") return true;
298
+ if (el.getAttribute("aria-pressed") === "true") return true;
299
+ return false;
300
+ }
266
301
  function getAssociatedLabel(el) {
267
302
  if (el.id) {
268
303
  const label = document.querySelector(`label[for="${CSS.escape(el.id)}"]`);
@@ -284,7 +319,7 @@ function getAssociatedLabel(el) {
284
319
  function collectSnapshot() {
285
320
  const allNodes = Array.from(
286
321
  document.querySelectorAll(CANDIDATE_SELECTOR)
287
- ).filter(isVisible);
322
+ ).filter(isVisible).filter((el) => !el.closest("[data-agent-exclude]"));
288
323
  const inView = allNodes.filter(isInViewport);
289
324
  const offScreen = allNodes.filter((el) => !isInViewport(el));
290
325
  const nodes = [...inView, ...offScreen].slice(0, MAX_CANDIDATES);
@@ -296,7 +331,8 @@ function collectSnapshot() {
296
331
  role: node.getAttribute("role") ?? node.tagName.toLowerCase(),
297
332
  text: (node.innerText || node.getAttribute("name") || "").trim().slice(0, 120),
298
333
  placeholder: placeholder || void 0,
299
- label: associatedLabel || void 0
334
+ label: associatedLabel || void 0,
335
+ active: isActiveElement(node) || void 0
300
336
  };
301
337
  });
302
338
  const textPreview = document.body.innerText.replace(/\s+/g, " ").trim().slice(0, 1500);
@@ -375,14 +411,15 @@ function toPlannerResult(raw) {
375
411
  return { action: raw };
376
412
  }
377
413
  async function parsePlannerText(raw) {
378
- const parser = await Promise.resolve().then(() => (init_parse_action(), parse_action_exports));
379
- return parser.parsePlannerResult(raw);
414
+ const { parsePlannerResult: parsePlannerResult2, PARSE_FAILURE_PATTERN: PARSE_FAILURE_PATTERN2 } = await Promise.resolve().then(() => (init_parse_action(), parse_action_exports));
415
+ const result = parsePlannerResult2(raw);
416
+ const parseFailed = result.action.type === "done" && PARSE_FAILURE_PATTERN2.test(result.action.reason);
417
+ return { result, parseFailed };
380
418
  }
381
419
  async function normalizeBridgeResponse(raw) {
382
420
  if (typeof raw === "string") {
383
- const parsed = await parsePlannerText(raw);
384
- const parseFailed = parsed.action.type === "done" && /(No JSON|JSON parse error|Parsed value is not an object|Unknown or missing action type)/.test(parsed.action.reason);
385
- return { result: parsed, parseFailed, rawText: raw };
421
+ const { result, parseFailed } = await parsePlannerText(raw);
422
+ return { result, parseFailed, rawText: raw };
386
423
  }
387
424
  return { result: toPlannerResult(raw), parseFailed: false };
388
425
  }
@@ -425,9 +462,16 @@ var RISKY_KEYWORDS = /\b(delete|remove|pay|purchase|submit|confirm|checkout|tran
425
462
  function elementTextRisky(text) {
426
463
  return text != null && RISKY_KEYWORDS.test(text);
427
464
  }
428
- function assessRisk(action) {
465
+ function candidateText(selector, candidates) {
466
+ const match = candidates?.find((c) => c.selector === selector);
467
+ return match ? [match.label, match.text, match.placeholder].filter(Boolean).join(" ") || void 0 : void 0;
468
+ }
469
+ function assessRisk(action, candidates) {
429
470
  switch (action.type) {
430
471
  case "navigate": {
472
+ if (action.url.startsWith("#") || action.url.startsWith("/") || action.url.startsWith("./") || action.url.startsWith("../")) {
473
+ return "safe";
474
+ }
431
475
  try {
432
476
  const next = new URL(action.url);
433
477
  if (!["http:", "https:"].includes(next.protocol)) {
@@ -438,10 +482,14 @@ function assessRisk(action) {
438
482
  }
439
483
  return "safe";
440
484
  }
441
- case "click":
442
- return elementTextRisky(action.label) ? "review" : "safe";
443
- case "type":
444
- return elementTextRisky(action.label) ? "review" : "safe";
485
+ case "click": {
486
+ const text = action.label ?? candidateText(action.selector, candidates) ?? action.selector;
487
+ return elementTextRisky(text) ? "review" : "safe";
488
+ }
489
+ case "type": {
490
+ const text = action.label ?? candidateText(action.selector, candidates) ?? action.selector;
491
+ return elementTextRisky(text) ? "review" : "safe";
492
+ }
445
493
  case "focus":
446
494
  case "scroll":
447
495
  case "wait":
@@ -481,7 +529,14 @@ var DEFAULT_SYSTEM_PROMPT = [
481
529
  "",
482
530
  "IMPORTANT: You MUST use selectors exactly as listed in the candidates. NEVER invent or guess selectors.",
483
531
  "If you cannot find a matching candidate for a target element, use the closest match from the candidates list.",
484
- "When previous step failed, recover by trying a different candidate selector or fallback strategy."
532
+ "When previous step failed, recover by trying a different candidate selector or fallback strategy.",
533
+ "NEVER use navigate for in-page tab switches or buttons \u2014 use click with the button's selector instead.",
534
+ "",
535
+ "Loop prevention rules (CRITICAL):",
536
+ "- If a candidate shows 'state: active', it is already selected/active \u2014 do NOT click it again.",
537
+ "- Check the History before acting. If the same selector was already clicked or typed in a recent step, do NOT repeat it \u2014 proceed to the next logical step or return done.",
538
+ "- If the goal is already achieved (value is set, element clicked, task complete), return done immediately.",
539
+ "- NEVER click a navigation tab or button more than once per goal unless the page changed to a different section."
485
540
  ].join("\n");
486
541
  function formatCandidate(candidate, index) {
487
542
  const parts = [
@@ -495,6 +550,9 @@ function formatCandidate(candidate, index) {
495
550
  if (candidate.placeholder) {
496
551
  parts.push(`placeholder: ${JSON.stringify(candidate.placeholder)}`);
497
552
  }
553
+ if (candidate.active) {
554
+ parts.push(`state: active`);
555
+ }
498
556
  return `[${index + 1}] ${parts.join(" | ")}`;
499
557
  }
500
558
  function buildSystemPrompt(customPrompt) {
@@ -545,7 +603,7 @@ function isParseFailure(result) {
545
603
  return false;
546
604
  }
547
605
  const reason = result.action.reason;
548
- return /(No JSON|JSON parse error|Parsed value is not an object|Unknown or missing action type)/.test(reason);
606
+ return PARSE_FAILURE_PATTERN.test(reason);
549
607
  }
550
608
  function contentToText(content) {
551
609
  if (typeof content === "string") {
@@ -627,6 +685,14 @@ function createWebLLMBridge(engine) {
627
685
  init_parse_action();
628
686
  var DEFAULT_PLANNER = { kind: "heuristic" };
629
687
  var MAX_CONSECUTIVE_ERRORS = 2;
688
+ var LOOP_WINDOW = 8;
689
+ var LOOP_THRESHOLD = 3;
690
+ function actionSignature(action) {
691
+ if (action.type === "click" || action.type === "type" || action.type === "focus") {
692
+ return `${action.type}:${action.selector}`;
693
+ }
694
+ return null;
695
+ }
630
696
  var BrowserAgent = class {
631
697
  session;
632
698
  maxSteps;
@@ -634,6 +700,7 @@ var BrowserAgent = class {
634
700
  events;
635
701
  isStopped = false;
636
702
  signal;
703
+ recentActionSigs = [];
637
704
  constructor(config, events = {}) {
638
705
  this.session = {
639
706
  id: crypto.randomUUID(),
@@ -660,6 +727,7 @@ var BrowserAgent = class {
660
727
  }
661
728
  async start() {
662
729
  this.isStopped = false;
730
+ this.recentActionSigs = [];
663
731
  this.session.isRunning = true;
664
732
  this.events.onStart?.(this.getSession());
665
733
  return this.runLoop();
@@ -764,15 +832,31 @@ var BrowserAgent = class {
764
832
  if (plannerResult.memory !== void 0) {
765
833
  this.session.memory = plannerResult.memory;
766
834
  }
767
- return this.processAction(plannerResult);
835
+ return this.processAction(plannerResult, snapshot.candidates);
768
836
  } catch (error) {
769
837
  return { status: "error", message: String(error) };
770
838
  }
771
839
  }
772
- async processAction(plannerResult) {
840
+ async processAction(plannerResult, candidates) {
773
841
  const { action } = plannerResult;
774
842
  const reflection = plannerResult.evaluation !== void 0 || plannerResult.memory !== void 0 || plannerResult.nextGoal !== void 0 ? { evaluation: plannerResult.evaluation, memory: plannerResult.memory, nextGoal: plannerResult.nextGoal } : void 0;
775
- const risk = assessRisk(action);
843
+ const sig = actionSignature(action);
844
+ if (sig) {
845
+ this.recentActionSigs.push(sig);
846
+ if (this.recentActionSigs.length > LOOP_WINDOW) {
847
+ this.recentActionSigs.shift();
848
+ }
849
+ const repeatCount = this.recentActionSigs.filter((s) => s === sig).length;
850
+ if (repeatCount >= LOOP_THRESHOLD) {
851
+ return {
852
+ status: "done",
853
+ action,
854
+ message: "Loop detected \u2014 goal appears to be complete.",
855
+ reflection
856
+ };
857
+ }
858
+ }
859
+ const risk = assessRisk(action, candidates);
776
860
  if (risk === "blocked") {
777
861
  return { status: "blocked", action, message: `Blocked action: ${JSON.stringify(action)}`, reflection };
778
862
  }
package/dist/lib.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/shared/parse-action.ts", "../src/core/executor.ts", "../src/core/observer.ts", "../src/core/planner.ts", "../src/shared/safety.ts", "../src/core/webllm-bridge.ts", "../src/core/prompt.ts", "../src/lib/index.ts"],
4
- "sourcesContent": ["import type { AgentAction, PlannerResult } from \"./contracts\";\n\nconst VALID_TYPES = new Set([\n \"click\", \"type\", \"navigate\", \"extract\", \"scroll\", \"focus\", \"wait\", \"done\",\n]);\n\n/**\n * Extract the first complete JSON object from text using bracket counting.\n * More robust than regex: handles prose before/after JSON, and repairs\n * truncated output (e.g. when the model hits a token limit mid-JSON).\n */\nfunction extractFirstJsonObject(text: string): string | null {\n const start = text.indexOf(\"{\");\n if (start === -1) return null;\n\n let depth = 0;\n let inString = false;\n let escaped = false;\n\n for (let i = start; i < text.length; i++) {\n const ch = text[i];\n if (escaped) { escaped = false; continue; }\n if (ch === \"\\\\\" && inString) { escaped = true; continue; }\n if (ch === '\"') { inString = !inString; continue; }\n if (inString) continue;\n if (ch === \"{\") depth++;\n else if (ch === \"}\") {\n depth--;\n if (depth === 0) return text.slice(start, i + 1);\n }\n }\n\n // Truncated JSON (hit token limit mid-output) \u2014 close remaining open braces.\n if (depth > 0) {\n return text.slice(start) + \"}\".repeat(depth);\n }\n\n return null;\n}\n\n/**\n * Parse an AgentAction from raw LLM output.\n *\n * Handles bare JSON, markdown fences, and JSON embedded in prose.\n * Returns a \"done\" action if parsing fails, so the caller always gets a valid AgentAction.\n */\nexport function parseAction(raw: string): AgentAction {\n const fenceMatch = raw.match(/```(?:json)?\\s*([\\s\\S]*?)```/);\n const candidate = fenceMatch ? fenceMatch[1].trim() : raw.trim();\n\n const jsonStr = extractFirstJsonObject(candidate);\n if (!jsonStr) {\n return { type: \"done\", reason: `No JSON object found in: ${raw.slice(0, 120)}` };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(jsonStr);\n } catch {\n return { type: \"done\", reason: `JSON parse error for: ${jsonStr.slice(0, 120)}` };\n }\n\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n return { type: \"done\", reason: \"Parsed value is not an object\" };\n }\n\n const obj = parsed as Record<string, unknown>;\n if (typeof obj.type !== \"string\" || !VALID_TYPES.has(obj.type)) {\n return { type: \"done\", reason: `Unknown or missing action type: ${String(obj.type)}` };\n }\n\n return obj as unknown as AgentAction;\n}\n\n/**\n * Parse a full PlannerResult from raw LLM output.\n *\n * Accepts the reflection+action format:\n * { \"evaluation\": \"...\", \"memory\": \"...\", \"nextGoal\": \"...\", \"action\": { ... } }\n * Also supports legacy `next_goal` key for backward compatibility.\n *\n * Also accepts a bare AgentAction for backward compatibility with simple bridges.\n */\nexport function parsePlannerResult(raw: string): PlannerResult {\n const fenceMatch = raw.match(/```(?:json)?\\s*([\\s\\S]*?)```/);\n const candidate = fenceMatch ? fenceMatch[1].trim() : raw.trim();\n\n const jsonStr = extractFirstJsonObject(candidate);\n if (!jsonStr) {\n return { action: { type: \"done\", reason: `No JSON found in: ${raw.slice(0, 120)}` } };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(jsonStr);\n } catch {\n return { action: { type: \"done\", reason: `JSON parse error: ${jsonStr.slice(0, 120)}` } };\n }\n\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n return { action: { type: \"done\", reason: \"Parsed value is not an object\" } };\n }\n\n const obj = parsed as Record<string, unknown>;\n\n // Full reflection format: { evaluation, memory, nextGoal, action }\n if (typeof obj.action === \"object\" && obj.action !== null) {\n const action = parseAction(JSON.stringify(obj.action));\n return {\n action,\n evaluation: typeof obj.evaluation === \"string\" ? obj.evaluation : undefined,\n memory: typeof obj.memory === \"string\" ? obj.memory : undefined,\n nextGoal:\n typeof obj.nextGoal === \"string\"\n ? obj.nextGoal\n : typeof obj.next_goal === \"string\"\n ? obj.next_goal\n : undefined,\n };\n }\n\n // Fallback: bare AgentAction (no reflection fields)\n return { action: parseAction(jsonStr) };\n}\n", "import type { AgentAction } from \"../shared/contracts\";\n\nfunction mustFind(selector: string): HTMLElement {\n const node = document.querySelector(selector);\n if (node instanceof HTMLElement) {\n return node;\n }\n\n // Fallback: if selector looks like a bare nth-of-type or is invalid,\n // try to find the element by tag and common attributes extracted from the selector\n const tagMatch = selector.match(/^(\\w+)/);\n if (tagMatch) {\n const tag = tagMatch[1];\n // Try matching by name, placeholder, or aria-label attributes in the selector\n const attrMatch = selector.match(/\\[(\\w[\\w-]*)=[\"']?([^\\]\"']+)[\"']?\\]/);\n if (attrMatch) {\n const fallback = document.querySelector(`${tag}[${attrMatch[1]}=\"${attrMatch[2]}\"]`);\n if (fallback instanceof HTMLElement) return fallback;\n }\n // Last resort: if only one element of that tag exists, use it\n const allOfTag = document.querySelectorAll(tag);\n if (allOfTag.length === 1 && allOfTag[0] instanceof HTMLElement) {\n return allOfTag[0];\n }\n }\n\n throw new Error(`Selector not found: ${selector}`);\n}\n\nfunction dispatchInputEvents(el: HTMLInputElement | HTMLTextAreaElement): void {\n el.dispatchEvent(new InputEvent(\"input\", { bubbles: true, cancelable: true }));\n el.dispatchEvent(new Event(\"change\", { bubbles: true }));\n}\n\nexport async function executeAction(action: AgentAction): Promise<string> {\n switch (action.type) {\n case \"click\": {\n const el = mustFind(action.selector);\n if ((el as HTMLButtonElement).disabled) {\n throw new Error(`Element is disabled: ${action.selector}`);\n }\n el.click();\n return `Clicked ${action.selector}`;\n }\n case \"type\": {\n const input = mustFind(action.selector) as HTMLInputElement | HTMLTextAreaElement;\n input.focus();\n if (action.clearFirst) {\n input.value = \"\";\n dispatchInputEvents(input);\n }\n input.value = `${input.value}${action.text}`;\n dispatchInputEvents(input);\n if (input.value.indexOf(action.text) === -1) {\n throw new Error(`Type verification failed: value did not update for ${action.selector}`);\n }\n return `Typed into ${action.selector}`;\n }\n case \"navigate\": {\n window.location.href = action.url;\n return `Navigated to ${action.url}`;\n }\n case \"extract\": {\n const value = mustFind(action.selector).innerText.trim();\n if (!value) {\n throw new Error(`Extract returned empty text from ${action.selector}`);\n }\n return `${action.label}: ${value}`;\n }\n case \"scroll\": {\n const target = action.selector ? mustFind(action.selector) : document.documentElement;\n target.scrollBy({ top: action.deltaY, behavior: \"smooth\" });\n return `Scrolled ${action.deltaY > 0 ? \"down\" : \"up\"} ${Math.abs(action.deltaY)}px`;\n }\n case \"focus\": {\n mustFind(action.selector).focus();\n return `Focused ${action.selector}`;\n }\n case \"wait\": {\n await new Promise((resolve) => setTimeout(resolve, action.ms));\n return `Waited ${action.ms}ms`;\n }\n case \"done\": {\n return action.reason;\n }\n default:\n return \"No-op\";\n }\n}\n", "import type { CandidateElement, PageSnapshot } from \"../shared/contracts\";\n\nconst CANDIDATE_SELECTOR =\n \"a,button,input,textarea,select,[role='button'],[role='link'],[contenteditable='true']\";\n\nconst MAX_CANDIDATES = 60;\n\nfunction cssPath(element: Element): string {\n if (!(element instanceof HTMLElement)) {\n return element.tagName.toLowerCase();\n }\n\n if (element.id) {\n return `#${CSS.escape(element.id)}`;\n }\n\n // For form elements, prefer attribute-based selectors that are more stable\n const tag = element.tagName.toLowerCase();\n if (tag === \"input\" || tag === \"textarea\" || tag === \"select\") {\n const name = element.getAttribute(\"name\");\n if (name && document.querySelectorAll(`${tag}[name=${CSS.escape(name)}]`).length === 1) {\n return `${tag}[name=${CSS.escape(name)}]`;\n }\n const type = (element as HTMLInputElement).type;\n const placeholder = element.getAttribute(\"placeholder\");\n if (placeholder && document.querySelectorAll(`${tag}[placeholder=${CSS.escape(placeholder)}]`).length === 1) {\n return `${tag}[placeholder=${CSS.escape(placeholder)}]`;\n }\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel && document.querySelectorAll(`${tag}[aria-label=${CSS.escape(ariaLabel)}]`).length === 1) {\n return `${tag}[aria-label=${CSS.escape(ariaLabel)}]`;\n }\n // Combine name + type for uniqueness\n if (name && type) {\n const combo = `${tag}[name=${CSS.escape(name)}][type=${CSS.escape(type)}]`;\n if (document.querySelectorAll(combo).length === 1) {\n return combo;\n }\n }\n }\n\n // For buttons/links, prefer text-based or aria selectors\n if (tag === \"button\" || tag === \"a\") {\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel && document.querySelectorAll(`${tag}[aria-label=${CSS.escape(ariaLabel)}]`).length === 1) {\n return `${tag}[aria-label=${CSS.escape(ariaLabel)}]`;\n }\n }\n\n const parts: string[] = [];\n let current: HTMLElement | null = element;\n while (current && parts.length < 4) {\n let part = current.tagName.toLowerCase();\n if (current.classList.length > 0) {\n part += `.${Array.from(current.classList).slice(0, 2).map(CSS.escape).join(\".\")}`;\n }\n const parent: HTMLElement | null = current.parentElement;\n if (parent) {\n const siblings = Array.from(parent.children).filter((s: Element) => s.tagName === current!.tagName);\n if (siblings.length > 1) {\n const index = siblings.indexOf(current) + 1;\n part += `:nth-of-type(${index})`;\n }\n }\n parts.unshift(part);\n current = parent;\n }\n return parts.join(\" > \");\n}\n\nfunction isVisible(el: HTMLElement): boolean {\n if (el.offsetParent === null && el.tagName !== \"BODY\") return false;\n const style = window.getComputedStyle(el);\n if (style.display === \"none\" || style.visibility === \"hidden\" || style.opacity === \"0\") return false;\n // Zero-dimension elements are functionally hidden\n const rect = el.getBoundingClientRect();\n return rect.width > 0 || rect.height > 0;\n}\n\nfunction isInViewport(el: HTMLElement): boolean {\n const rect = el.getBoundingClientRect();\n return (\n rect.bottom > 0 &&\n rect.top < window.innerHeight &&\n rect.right > 0 &&\n rect.left < window.innerWidth\n );\n}\n\n/** Resolve the visible label text via for/id, aria-labelledby, aria-label, or wrapping <label>. */\nfunction getAssociatedLabel(el: HTMLElement): string {\n if (el.id) {\n const label = document.querySelector<HTMLLabelElement>(`label[for=\"${CSS.escape(el.id)}\"]`);\n if (label) return label.innerText.trim();\n }\n\n const labelledBy = el.getAttribute(\"aria-labelledby\");\n if (labelledBy) {\n const labelEl = document.getElementById(labelledBy);\n if (labelEl) return labelEl.innerText.trim();\n }\n\n const ariaLabel = el.getAttribute(\"aria-label\");\n if (ariaLabel) return ariaLabel.trim();\n\n const parentLabel = el.closest(\"label\");\n if (parentLabel) {\n return Array.from(parentLabel.childNodes)\n .filter((n) => n.nodeType === Node.TEXT_NODE)\n .map((n) => n.textContent?.trim() ?? \"\")\n .filter(Boolean)\n .join(\" \");\n }\n\n return \"\";\n}\n\nexport function collectSnapshot(): PageSnapshot {\n const allNodes = Array.from(\n document.querySelectorAll<HTMLElement>(CANDIDATE_SELECTOR)\n ).filter(isVisible);\n\n // In-viewport elements first so the model sees the most relevant candidates first\n const inView = allNodes.filter(isInViewport);\n const offScreen = allNodes.filter((el) => !isInViewport(el));\n const nodes = [...inView, ...offScreen].slice(0, MAX_CANDIDATES);\n\n const candidates: CandidateElement[] = nodes.map((node) => {\n const placeholder =\n (node as HTMLInputElement).placeholder?.trim() || node.getAttribute(\"placeholder\")?.trim();\n const associatedLabel = getAssociatedLabel(node);\n return {\n selector: cssPath(node),\n role: node.getAttribute(\"role\") ?? node.tagName.toLowerCase(),\n text: (node.innerText || node.getAttribute(\"name\") || \"\").trim().slice(0, 120),\n placeholder: placeholder || undefined,\n label: associatedLabel || undefined,\n };\n });\n\n const textPreview = document.body.innerText.replace(/\\s+/g, \" \").trim().slice(0, 1500);\n\n return {\n url: window.location.href,\n title: document.title,\n textPreview,\n candidates,\n };\n}\n", "import type { AgentAction, CandidateElement, PlannerConfig, PlannerInput, PlannerResult } from \"../shared/contracts\";\n\ntype WebLLMBridge = {\n // Bridge may return PlannerResult (new), AgentAction (legacy), or raw LLM text.\n plan(input: PlannerInput, modelId?: string): Promise<PlannerResult | AgentAction | string>;\n // Optional retry hook that keeps chat history and asks for corrected JSON.\n retryInvalidJson?(input: PlannerInput, badOutput: string, modelId?: string): Promise<PlannerResult | AgentAction | string>;\n};\n\nconst URL_PATTERN = /(?:go to|navigate to|open)\\s+(https?:\\/\\/\\S+)/i;\nconst SEARCH_PATTERN = /search(?:\\s+for)?\\s+(.+)/i;\nconst FILL_PATTERN = /(?:fill|type|enter)\\s+\"?([^\"]+)\"?\\s+(?:in(?:to)?|for|on)\\s+(.+)/i;\nconst CLICK_PATTERN = /click(?:\\s+(?:on|the))?\\s+(.+)/i;\n\nfunction findByText(candidates: CandidateElement[], text: string): CandidateElement | undefined {\n const lower = text.toLowerCase();\n return candidates.find(\n (c) =>\n c.text.toLowerCase().includes(lower) ||\n (c.placeholder?.toLowerCase().includes(lower) ?? false) ||\n (c.label?.toLowerCase().includes(lower) ?? false)\n );\n}\n\nfunction findInput(candidates: CandidateElement[]): CandidateElement | undefined {\n return candidates.find(\n (c) => c.role === \"input\" || c.role === \"textarea\" || c.selector.includes(\"input\") || c.selector.includes(\"textarea\")\n );\n}\n\nfunction findButton(candidates: CandidateElement[]): CandidateElement | undefined {\n return candidates.find(\n (c) => c.role === \"button\" || c.role === \"a\" || c.selector.includes(\"button\") || c.selector.includes(\"a\")\n );\n}\n\nfunction heuristicPlan(input: PlannerInput): AgentAction {\n const { goal, snapshot, history } = input;\n\n const navMatch = goal.match(URL_PATTERN);\n if (navMatch) {\n return { type: \"navigate\", url: navMatch[1] };\n }\n\n const fillMatch = goal.match(FILL_PATTERN);\n if (fillMatch) {\n const [, text, fieldHint] = fillMatch;\n const target = findByText(snapshot.candidates, fieldHint) ?? findInput(snapshot.candidates);\n if (target) {\n return { type: \"type\", selector: target.selector, text, clearFirst: true, label: target.label || target.text || target.placeholder };\n }\n }\n\n const searchMatch = goal.match(SEARCH_PATTERN);\n if (searchMatch) {\n const input = findInput(snapshot.candidates);\n if (input) {\n return { type: \"type\", selector: input.selector, text: searchMatch[1].trim(), clearFirst: true, label: input.label || input.text || input.placeholder };\n }\n }\n\n const clickMatch = goal.match(CLICK_PATTERN);\n if (clickMatch) {\n const target = findByText(snapshot.candidates, clickMatch[1].trim());\n if (target) {\n return { type: \"click\", selector: target.selector, label: target.text };\n }\n }\n\n const firstInput = findInput(snapshot.candidates);\n const firstButton = findButton(snapshot.candidates);\n\n if (firstInput && !history.some((h) => h.startsWith(\"Typed\"))) {\n const searchTerm = goal.replace(/.*(?:search|find|look up)\\s+/i, \"\").trim();\n return { type: \"type\", selector: firstInput.selector, text: searchTerm, clearFirst: true, label: firstInput.label || firstInput.text || firstInput.placeholder };\n }\n\n if (firstButton && !history.some((h) => h.startsWith(\"Clicked\"))) {\n return { type: \"click\", selector: firstButton.selector, label: firstButton.text };\n }\n\n return { type: \"done\", reason: \"No further heuristic actions available\" };\n}\n\n/** Normalize whatever a bridge returns into a PlannerResult. */\nfunction toPlannerResult(raw: PlannerResult | AgentAction): PlannerResult {\n // New format: has an `action` key that is an object\n if (\"action\" in raw && typeof (raw as PlannerResult).action === \"object\") {\n return raw as PlannerResult;\n }\n // Legacy format: bare AgentAction\n return { action: raw as AgentAction };\n}\n\nasync function parsePlannerText(raw: string): Promise<PlannerResult> {\n const parser = await import(\"../shared/parse-action\");\n return parser.parsePlannerResult(raw);\n}\n\nasync function normalizeBridgeResponse(\n raw: PlannerResult | AgentAction | string\n): Promise<{ result: PlannerResult; parseFailed: boolean; rawText?: string }> {\n if (typeof raw === \"string\") {\n const parsed = await parsePlannerText(raw);\n const parseFailed = parsed.action.type === \"done\" &&\n /(No JSON|JSON parse error|Parsed value is not an object|Unknown or missing action type)/.test(parsed.action.reason);\n return { result: parsed, parseFailed, rawText: raw };\n }\n\n return { result: toPlannerResult(raw), parseFailed: false };\n}\n\nexport async function planNextAction(config: PlannerConfig, input: PlannerInput): Promise<PlannerResult> {\n if (config.kind === \"heuristic\") {\n return { action: heuristicPlan(input) };\n }\n\n const bridge = (window as Window & { __browserAgentWebLLM?: WebLLMBridge }).__browserAgentWebLLM;\n if (!bridge) {\n return {\n action: {\n type: \"done\",\n reason: \"WebLLM bridge is not configured. Use heuristic mode or wire a WebLLM bridge implementation.\"\n }\n };\n }\n\n const plannerInput = { ...input, systemPrompt: config.systemPrompt };\n const firstAttempt = await normalizeBridgeResponse(await bridge.plan(plannerInput, config.modelId));\n\n if (!firstAttempt.parseFailed) {\n return firstAttempt.result;\n }\n\n if (bridge.retryInvalidJson && firstAttempt.rawText) {\n const retryAttempt = await normalizeBridgeResponse(\n await bridge.retryInvalidJson(plannerInput, firstAttempt.rawText, config.modelId)\n );\n\n if (!retryAttempt.parseFailed) {\n return retryAttempt.result;\n }\n }\n\n return {\n action: {\n type: \"done\",\n reason: \"WebLLM output could not be parsed after retry.\"\n }\n };\n}\n", "import type { AgentAction, RiskLevel } from \"./contracts\";\n\nconst RISKY_KEYWORDS = /\\b(delete|remove|pay|purchase|submit|confirm|checkout|transfer|withdraw|send)\\b/i;\n\nfunction elementTextRisky(text?: string): boolean {\n return text != null && RISKY_KEYWORDS.test(text);\n}\n\nexport function assessRisk(action: AgentAction): RiskLevel {\n switch (action.type) {\n case \"navigate\": {\n try {\n const next = new URL(action.url);\n if (![\"http:\", \"https:\"].includes(next.protocol)) {\n return \"blocked\";\n }\n } catch {\n return \"blocked\";\n }\n return \"safe\";\n }\n case \"click\":\n return elementTextRisky(action.label) ? \"review\" : \"safe\";\n case \"type\":\n return elementTextRisky(action.label) ? \"review\" : \"safe\";\n case \"focus\":\n case \"scroll\":\n case \"wait\":\n return \"safe\";\n case \"extract\":\n return \"review\";\n case \"done\":\n return \"safe\";\n default:\n return \"review\";\n }\n}\n", "import type { PlannerInput, PlannerResult } from \"../shared/contracts\";\nimport { parsePlannerResult } from \"../shared/parse-action\";\nimport { buildSystemPrompt, buildUserMessage } from \"./prompt\";\n\nexport const INVALID_JSON_RETRY_MESSAGE = [\n \"Your reply was not valid JSON. Output ONLY a JSON object \u2014 no explanation, no markdown, no extra text.\",\n 'Required format: {\"evaluation\":\"...\",\"memory\":\"...\",\"nextGoal\":\"...\",\"action\":{\"type\":\"click\",\"selector\":\"EXACT_SELECTOR\",\"label\":\"...\"}}',\n].join(\"\\n\");\n\nconst BARE_ACTION_RETRY_MESSAGE = [\n \"Still not valid JSON. Output ONLY the action object. No evaluation, memory, or nextGoal fields.\",\n 'Example: {\"type\":\"type\",\"selector\":\"EXACT_SELECTOR_FROM_LIST\",\"text\":\"value\",\"clearFirst\":true}',\n 'Example: {\"type\":\"click\",\"selector\":\"EXACT_SELECTOR_FROM_LIST\",\"label\":\"button text\"}',\n].join(\"\\n\");\n\ntype ChatRole = \"system\" | \"user\" | \"assistant\";\n\ntype ChatMessage = {\n role: ChatRole;\n content: string;\n};\n\ntype CompletionRequest = {\n messages: ChatMessage[];\n temperature?: number;\n max_tokens?: number;\n model?: string;\n};\n\ntype CompletionResponse = {\n choices?: Array<{\n message?: {\n content?: unknown;\n };\n }>;\n};\n\nexport type WebLLMEngineLike = {\n chat: {\n completions: {\n create(request: CompletionRequest): Promise<CompletionResponse>;\n };\n };\n};\n\nexport type BrowserAgentWebLLMBridge = {\n plan(input: PlannerInput, modelId?: string): Promise<PlannerResult>;\n retryInvalidJson(input: PlannerInput, badOutput: string, modelId?: string): Promise<PlannerResult>;\n};\n\nfunction isParseFailure(result: PlannerResult): boolean {\n if (result.action.type !== \"done\") {\n return false;\n }\n\n const reason = result.action.reason;\n return /(No JSON|JSON parse error|Parsed value is not an object|Unknown or missing action type)/.test(reason);\n}\n\nfunction contentToText(content: unknown): string {\n if (typeof content === \"string\") {\n return content;\n }\n\n if (Array.isArray(content)) {\n const chunks = content\n .map((part) => {\n if (typeof part === \"string\") {\n return part;\n }\n if (typeof part === \"object\" && part !== null && \"text\" in part && typeof part.text === \"string\") {\n return part.text;\n }\n return \"\";\n })\n .filter(Boolean);\n\n return chunks.join(\"\\n\");\n }\n\n return \"\";\n}\n\nfunction failureResult(attempts: number): PlannerResult {\n return {\n action: {\n type: \"done\",\n reason: `WebLLM returned invalid JSON after ${attempts} attempt${attempts === 1 ? \"\" : \"s\"}. Unable to continue.`\n }\n };\n}\n\nasync function createCompletion(engine: WebLLMEngineLike, messages: ChatMessage[], modelId?: string): Promise<string> {\n const response = await engine.chat.completions.create({\n messages,\n temperature: 0,\n max_tokens: 700,\n ...(modelId ? { model: modelId } : {})\n });\n\n return contentToText(response.choices?.[0]?.message?.content).trim();\n}\n\nfunction buildBaseMessages(input: PlannerInput): ChatMessage[] {\n return [\n { role: \"system\", content: buildSystemPrompt(input.systemPrompt) },\n { role: \"user\", content: buildUserMessage(input) }\n ];\n}\n\nasync function retryInvalidJsonWithHistory(\n engine: WebLLMEngineLike,\n baseMessages: ChatMessage[],\n badOutput: string,\n modelId?: string\n): Promise<PlannerResult> {\n // Attempt 2: improved message with format example\n const attempt2Messages: ChatMessage[] = [\n ...baseMessages,\n { role: \"assistant\", content: badOutput },\n { role: \"user\", content: INVALID_JSON_RETRY_MESSAGE }\n ];\n\n const raw2 = await createCompletion(engine, attempt2Messages, modelId);\n const result2 = parsePlannerResult(raw2);\n if (!isParseFailure(result2)) return result2;\n\n // Attempt 3: ask for bare action only (simpler target for small models)\n const attempt3Messages: ChatMessage[] = [\n ...attempt2Messages,\n { role: \"assistant\", content: raw2 },\n { role: \"user\", content: BARE_ACTION_RETRY_MESSAGE }\n ];\n\n const raw3 = await createCompletion(engine, attempt3Messages, modelId);\n const result3 = parsePlannerResult(raw3);\n if (!isParseFailure(result3)) return result3;\n\n return failureResult(3);\n}\n\nexport function createWebLLMBridge(engine: WebLLMEngineLike): BrowserAgentWebLLMBridge {\n return {\n async plan(input: PlannerInput, modelId?: string): Promise<PlannerResult> {\n const baseMessages = buildBaseMessages(input);\n\n // Attempt 1: normal\n const raw1 = await createCompletion(engine, baseMessages, modelId);\n const result1 = parsePlannerResult(raw1);\n if (!isParseFailure(result1)) return result1;\n\n // Attempts 2 & 3 with progressively simpler asks\n return retryInvalidJsonWithHistory(engine, baseMessages, raw1, modelId);\n },\n\n async retryInvalidJson(input: PlannerInput, badOutput: string, modelId?: string): Promise<PlannerResult> {\n const baseMessages = buildBaseMessages(input);\n return retryInvalidJsonWithHistory(engine, baseMessages, badOutput, modelId);\n }\n };\n}\n", "import type { CandidateElement, PlannerInput } from \"../shared/contracts\";\n\nconst MAX_TEXT_PREVIEW_LENGTH = 800;\n\nconst DEFAULT_SYSTEM_PROMPT = [\n \"You are OmniBrowser Agent, an on-page web automation planner.\",\n \"Choose exactly one next action using the provided page snapshot.\",\n \"\",\n \"Output rules:\",\n '- Reply with only one valid JSON object (no markdown fences, no prose).',\n '- JSON shape must be: { \"evaluation\": string, \"memory\": string, \"nextGoal\": string, \"action\": AgentAction }.',\n '- Keep evaluation/memory/nextGoal concise but informative.',\n \"\",\n \"Valid AgentAction shapes:\",\n '- {\"type\":\"click\",\"selector\":\"<css>\",\"label\":\"<optional>\"}',\n '- {\"type\":\"type\",\"selector\":\"<css>\",\"text\":\"<text>\",\"clearFirst\":true|false,\"label\":\"<optional>\"}',\n '- {\"type\":\"navigate\",\"url\":\"https://...\"}',\n '- {\"type\":\"extract\",\"selector\":\"<css>\",\"label\":\"<label>\"}',\n '- {\"type\":\"scroll\",\"selector\":\"<optional css>\",\"deltaY\":number}',\n '- {\"type\":\"focus\",\"selector\":\"<css>\"}',\n '- {\"type\":\"wait\",\"ms\":number}',\n '- {\"type\":\"done\",\"reason\":\"<reason>\"}',\n \"\",\n \"IMPORTANT: You MUST use selectors exactly as listed in the candidates. NEVER invent or guess selectors.\",\n \"If you cannot find a matching candidate for a target element, use the closest match from the candidates list.\",\n \"When previous step failed, recover by trying a different candidate selector or fallback strategy.\"\n].join(\"\\n\");\n\nfunction formatCandidate(candidate: CandidateElement, index: number): string {\n const parts = [\n `${candidate.role || \"element\"}`,\n `selector: ${JSON.stringify(candidate.selector)}`,\n `text: ${JSON.stringify(candidate.text || \"\")}`\n ];\n\n if (candidate.label) {\n parts.push(`label: ${JSON.stringify(candidate.label)}`);\n }\n if (candidate.placeholder) {\n parts.push(`placeholder: ${JSON.stringify(candidate.placeholder)}`);\n }\n\n return `[${index + 1}] ${parts.join(\" | \")}`;\n}\n\nexport function buildSystemPrompt(customPrompt?: string): string {\n if (!customPrompt?.trim()) {\n return DEFAULT_SYSTEM_PROMPT;\n }\n\n return `${DEFAULT_SYSTEM_PROMPT}\\n\\nAdditional instructions:\\n${customPrompt.trim()}`;\n}\n\nexport function buildUserMessage(input: PlannerInput): string {\n const { goal, snapshot, history, memory, lastError } = input;\n const candidates = snapshot.candidates.map(formatCandidate).join(\"\\n\");\n const historyLines = history.length > 0\n ? history.map((step, index) => `${index + 1}. ${step}`).join(\"\\n\")\n : \"(none)\";\n\n return [\n `Goal: ${goal}`,\n \"\",\n \"Page snapshot:\",\n `- URL: ${snapshot.url}`,\n `- Title: ${snapshot.title}`,\n `- Text preview (first ${MAX_TEXT_PREVIEW_LENGTH} chars): ${JSON.stringify(snapshot.textPreview.slice(0, MAX_TEXT_PREVIEW_LENGTH))}`,\n \"\",\n `Interactive candidates (${snapshot.candidates.length}):`,\n candidates || \"(none)\",\n \"\",\n \"History:\",\n historyLines,\n \"\",\n `Working memory: ${memory ? JSON.stringify(memory) : \"(none)\"}`,\n `Last error: ${lastError ? JSON.stringify(lastError) : \"(none)\"}`,\n \"If Last error is present, recover and choose a different valid action.\"\n ].join(\"\\n\");\n}\n", "import { executeAction } from \"../core/executor\";\nimport { collectSnapshot } from \"../core/observer\";\nimport { planNextAction } from \"../core/planner\";\nimport type {\n AgentSession,\n ContentResult,\n LibraryAgentConfig,\n LibraryAgentEvents,\n PlannerConfig,\n PlannerResult\n} from \"../shared/contracts\";\nimport { assessRisk } from \"../shared/safety\";\n\nconst DEFAULT_PLANNER: PlannerConfig = { kind: \"heuristic\" };\n\n/** Max consecutive errors before the agent gives up instead of retrying */\nconst MAX_CONSECUTIVE_ERRORS = 2;\n\nexport class BrowserAgent {\n private session: AgentSession;\n private maxSteps: number;\n private stepDelayMs: number;\n private events: LibraryAgentEvents;\n private isStopped = false;\n private signal?: AbortSignal;\n\n constructor(config: LibraryAgentConfig, events: LibraryAgentEvents = {}) {\n this.session = {\n id: crypto.randomUUID(),\n tabId: null,\n goal: config.goal,\n mode: config.mode ?? \"human-approved\",\n planner: config.planner ?? DEFAULT_PLANNER,\n history: [],\n isRunning: false\n };\n\n this.maxSteps = config.maxSteps ?? 20;\n this.stepDelayMs = config.stepDelayMs ?? 500;\n this.events = events;\n this.signal = config.signal;\n }\n\n getSession(): AgentSession {\n return { ...this.session, history: [...this.session.history] };\n }\n\n get isRunning(): boolean {\n return this.session.isRunning;\n }\n\n get hasPendingAction(): boolean {\n return this.session.pendingAction != null;\n }\n\n async start(): Promise<ContentResult> {\n this.isStopped = false;\n this.session.isRunning = true;\n this.events.onStart?.(this.getSession());\n return this.runLoop();\n }\n\n async resume(): Promise<ContentResult> {\n if (this.session.pendingAction) {\n const approvalResult = await this.approvePendingAction();\n if (approvalResult.status === \"error\") {\n return approvalResult;\n }\n }\n this.session.isRunning = true;\n return this.runLoop();\n }\n\n private async runLoop(): Promise<ContentResult> {\n let consecutiveErrors = 0;\n let lastError: string | undefined;\n\n for (let step = 0; step < this.maxSteps; step += 1) {\n if (this.isStopped || !this.session.isRunning) {\n return { status: \"done\", message: \"Stopped\" };\n }\n\n if (this.signal?.aborted) {\n this.session.isRunning = false;\n return { status: \"done\", message: \"Aborted\" };\n }\n\n const result = await this.tick(lastError);\n this.events.onStep?.(result, this.getSession());\n\n if (result.status === \"error\") {\n consecutiveErrors += 1;\n lastError = result.message;\n this.session.history.push(`Error: ${result.message}`);\n\n if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {\n this.session.isRunning = false;\n this.events.onError?.(new Error(result.message), this.getSession());\n this.events.onDone?.(result, this.getSession());\n return result;\n }\n\n await this.delay(this.stepDelayMs);\n continue;\n }\n\n consecutiveErrors = 0;\n lastError = undefined;\n this.session.history.push(result.message);\n\n if (result.status === \"needs_approval\") {\n this.session.pendingAction = result.action;\n this.session.isRunning = false;\n if (result.action) {\n this.events.onApprovalRequired?.(result.action, this.getSession());\n }\n return result;\n }\n\n if ([\"done\", \"blocked\"].includes(result.status)) {\n this.session.isRunning = false;\n this.events.onDone?.(result, this.getSession());\n return result;\n }\n\n await this.delay(this.stepDelayMs);\n }\n\n const result: ContentResult = { status: \"done\", message: \"Reached max steps\" };\n this.session.history.push(result.message);\n this.session.isRunning = false;\n this.events.onMaxStepsReached?.(this.getSession());\n this.events.onDone?.(result, this.getSession());\n return result;\n }\n\n async approvePendingAction(): Promise<ContentResult> {\n if (!this.session.pendingAction) {\n return { status: \"error\", message: \"No pending action to approve\" };\n }\n\n try {\n const message = await executeAction(this.session.pendingAction);\n const result: ContentResult = {\n status: \"executed\",\n message,\n action: this.session.pendingAction\n };\n this.session.history.push(message);\n this.session.pendingAction = undefined;\n this.session.isRunning = true;\n this.events.onStep?.(result, this.getSession());\n return result;\n } catch (error) {\n this.session.isRunning = false;\n this.events.onError?.(error, this.getSession());\n return { status: \"error\", message: String(error) };\n }\n }\n\n stop(): void {\n this.isStopped = true;\n this.session.isRunning = false;\n }\n\n private async tick(lastError?: string): Promise<ContentResult> {\n try {\n const snapshot = collectSnapshot();\n const plannerResult = await planNextAction(this.session.planner, {\n goal: this.session.goal,\n snapshot,\n history: this.session.history,\n lastError,\n memory: this.session.memory\n });\n\n // Carry working memory forward across steps\n if (plannerResult.memory !== undefined) {\n this.session.memory = plannerResult.memory;\n }\n\n return this.processAction(plannerResult);\n } catch (error) {\n return { status: \"error\", message: String(error) };\n }\n }\n\n private async processAction(plannerResult: PlannerResult): Promise<ContentResult> {\n const { action } = plannerResult;\n const reflection = plannerResult.evaluation !== undefined || plannerResult.memory !== undefined || plannerResult.nextGoal !== undefined\n ? { evaluation: plannerResult.evaluation, memory: plannerResult.memory, nextGoal: plannerResult.nextGoal }\n : undefined;\n\n const risk = assessRisk(action);\n if (risk === \"blocked\") {\n return { status: \"blocked\", action, message: `Blocked action: ${JSON.stringify(action)}`, reflection };\n }\n\n if (this.session.mode === \"human-approved\" && risk === \"review\") {\n return { status: \"needs_approval\", action, message: `Approval needed for ${action.type}`, reflection };\n }\n\n if (action.type === \"done\") {\n return { status: \"done\", action, message: action.reason, reflection };\n }\n\n const message = await executeAction(action);\n return { status: \"executed\", action, message, reflection };\n }\n\n private async delay(ms: number): Promise<void> {\n await new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\nexport function createBrowserAgent(config: LibraryAgentConfig, events?: LibraryAgentEvents): BrowserAgent {\n return new BrowserAgent(config, events);\n}\n\nexport { createWebLLMBridge } from \"../core/webllm-bridge\";\nexport { parseAction, parsePlannerResult } from \"../shared/parse-action\";\nexport type { BrowserAgentWebLLMBridge, WebLLMEngineLike } from \"../core/webllm-bridge\";\n\nexport type {\n AgentAction,\n AgentMode,\n AgentSession,\n ContentResult,\n LibraryAgentConfig,\n LibraryAgentEvents,\n PlannerConfig,\n PlannerInput,\n PlannerKind,\n PlannerResult,\n RiskLevel\n} from \"../shared/contracts\";\n"],
5
- "mappings": "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",
6
- "names": ["input", "result"]
4
+ "sourcesContent": ["import type { AgentAction, PlannerResult } from \"./contracts\";\n\nexport const PARSE_FAILURE_PATTERN = /(No JSON|JSON parse error|Parsed value is not an object|Unknown or missing action type|Missing required field)/;\n\nconst VALID_TYPES = new Set([\n \"click\", \"type\", \"navigate\", \"extract\", \"scroll\", \"focus\", \"wait\", \"done\",\n]);\n\n/**\n * Extract the first complete JSON object from text using bracket counting.\n * More robust than regex: handles prose before/after JSON, and repairs\n * truncated output (e.g. when the model hits a token limit mid-JSON).\n */\nfunction extractFirstJsonObject(text: string): string | null {\n const start = text.indexOf(\"{\");\n if (start === -1) return null;\n\n let depth = 0;\n let inString = false;\n let escaped = false;\n\n for (let i = start; i < text.length; i++) {\n const ch = text[i];\n if (escaped) { escaped = false; continue; }\n if (ch === \"\\\\\" && inString) { escaped = true; continue; }\n if (ch === '\"') { inString = !inString; continue; }\n if (inString) continue;\n if (ch === \"{\") depth++;\n else if (ch === \"}\") {\n depth--;\n if (depth === 0) return text.slice(start, i + 1);\n }\n }\n\n // Truncated JSON (hit token limit mid-output) \u2014 close remaining open braces.\n if (depth > 0) {\n return text.slice(start) + \"}\".repeat(depth);\n }\n\n return null;\n}\n\n/**\n * Parse an AgentAction from raw LLM output.\n *\n * Handles bare JSON, markdown fences, and JSON embedded in prose.\n * Returns a \"done\" action if parsing fails, so the caller always gets a valid AgentAction.\n */\nexport function parseAction(raw: string): AgentAction {\n const fenceMatch = raw.match(/```(?:json)?\\s*([\\s\\S]*?)```/);\n const candidate = fenceMatch ? fenceMatch[1].trim() : raw.trim();\n\n const jsonStr = extractFirstJsonObject(candidate);\n if (!jsonStr) {\n return { type: \"done\", reason: `No JSON object found in: ${raw.slice(0, 120)}` };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(jsonStr);\n } catch {\n return { type: \"done\", reason: `JSON parse error for: ${jsonStr.slice(0, 120)}` };\n }\n\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n return { type: \"done\", reason: \"Parsed value is not an object\" };\n }\n\n const obj = parsed as Record<string, unknown>;\n if (typeof obj.type !== \"string\" || !VALID_TYPES.has(obj.type)) {\n return { type: \"done\", reason: `Unknown or missing action type: ${String(obj.type)}` };\n }\n\n const t = obj.type;\n if ((t === \"click\" || t === \"type\" || t === \"extract\" || t === \"focus\") && typeof obj.selector !== \"string\") {\n return { type: \"done\", reason: `Missing required field 'selector' for action type: ${t}` };\n }\n if (t === \"extract\" && typeof obj.label !== \"string\") {\n return { type: \"done\", reason: `Missing required field 'label' for action type: extract` };\n }\n if (t === \"type\" && typeof obj.text !== \"string\") {\n return { type: \"done\", reason: `Missing required field 'text' for action type: type` };\n }\n if (t === \"navigate\" && typeof obj.url !== \"string\") {\n return { type: \"done\", reason: `Missing required field 'url' for action type: navigate` };\n }\n if (t === \"scroll\" && typeof obj.deltaY !== \"number\") {\n return { type: \"done\", reason: `Missing required field 'deltaY' for action type: scroll` };\n }\n if (t === \"wait\" && typeof obj.ms !== \"number\") {\n return { type: \"done\", reason: `Missing required field 'ms' for action type: wait` };\n }\n if (t === \"done\" && typeof obj.reason !== \"string\") {\n return { type: \"done\", reason: `Missing required field 'reason' for action type: done` };\n }\n\n return obj as unknown as AgentAction;\n}\n\n/**\n * Parse a full PlannerResult from raw LLM output.\n *\n * Accepts the reflection+action format:\n * { \"evaluation\": \"...\", \"memory\": \"...\", \"nextGoal\": \"...\", \"action\": { ... } }\n * Also supports legacy `next_goal` key for backward compatibility.\n *\n * Also accepts a bare AgentAction for backward compatibility with simple bridges.\n */\nexport function parsePlannerResult(raw: string): PlannerResult {\n const fenceMatch = raw.match(/```(?:json)?\\s*([\\s\\S]*?)```/);\n const candidate = fenceMatch ? fenceMatch[1].trim() : raw.trim();\n\n const jsonStr = extractFirstJsonObject(candidate);\n if (!jsonStr) {\n return { action: { type: \"done\", reason: `No JSON found in: ${raw.slice(0, 120)}` } };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(jsonStr);\n } catch {\n return { action: { type: \"done\", reason: `JSON parse error: ${jsonStr.slice(0, 120)}` } };\n }\n\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n return { action: { type: \"done\", reason: \"Parsed value is not an object\" } };\n }\n\n const obj = parsed as Record<string, unknown>;\n\n // Full reflection format: { evaluation, memory, nextGoal, action }\n if (typeof obj.action === \"object\" && obj.action !== null) {\n const action = parseAction(JSON.stringify(obj.action));\n return {\n action,\n evaluation: typeof obj.evaluation === \"string\" ? obj.evaluation : undefined,\n memory: typeof obj.memory === \"string\" ? obj.memory : undefined,\n nextGoal:\n typeof obj.nextGoal === \"string\"\n ? obj.nextGoal\n : typeof obj.next_goal === \"string\"\n ? obj.next_goal\n : undefined,\n };\n }\n\n // Fallback: bare AgentAction (no reflection fields)\n return { action: parseAction(jsonStr) };\n}\n", "import type { AgentAction } from \"../shared/contracts\";\n\nfunction mustFind(selector: string): HTMLElement {\n const node = document.querySelector(selector);\n if (node instanceof HTMLElement) {\n return node;\n }\n\n // Fallback: if selector looks like a bare nth-of-type or is invalid,\n // try to find the element by tag and common attributes extracted from the selector\n const tagMatch = selector.match(/^(\\w+)/);\n if (tagMatch) {\n const tag = tagMatch[1];\n // Try matching by name, placeholder, or aria-label attributes in the selector\n const attrMatch = selector.match(/\\[(\\w[\\w-]*)=[\"']?([^\\]\"']+)[\"']?\\]/);\n if (attrMatch) {\n const fallback = document.querySelector(`${tag}[${attrMatch[1]}=\"${attrMatch[2]}\"]`);\n if (fallback instanceof HTMLElement) return fallback;\n }\n // Last resort: if only one element of that tag exists, use it\n const allOfTag = document.querySelectorAll(tag);\n if (allOfTag.length === 1 && allOfTag[0] instanceof HTMLElement) {\n return allOfTag[0];\n }\n }\n\n throw new Error(`Selector not found: ${selector}`);\n}\n\nfunction dispatchInputEvents(el: HTMLInputElement | HTMLTextAreaElement): void {\n el.dispatchEvent(new InputEvent(\"input\", { bubbles: true, cancelable: true }));\n el.dispatchEvent(new Event(\"change\", { bubbles: true }));\n}\n\nexport async function executeAction(action: AgentAction): Promise<string> {\n switch (action.type) {\n case \"click\": {\n const el = mustFind(action.selector);\n if ((el as HTMLButtonElement).disabled) {\n throw new Error(`Element is disabled: ${action.selector}`);\n }\n el.click();\n return `Clicked ${action.selector}`;\n }\n case \"type\": {\n const input = mustFind(action.selector) as HTMLInputElement | HTMLTextAreaElement;\n if (input.value === action.text) {\n return `Already contains correct value in ${action.selector}`;\n }\n input.focus();\n if (action.clearFirst) {\n input.value = \"\";\n dispatchInputEvents(input);\n }\n input.value = `${input.value}${action.text}`;\n dispatchInputEvents(input);\n if (input.value.indexOf(action.text) === -1) {\n throw new Error(`Type verification failed: value did not update for ${action.selector}`);\n }\n return `Typed into ${action.selector}`;\n }\n case \"navigate\": {\n window.location.href = action.url;\n return `Navigated to ${action.url}`;\n }\n case \"extract\": {\n const value = mustFind(action.selector).innerText.trim();\n if (!value) {\n throw new Error(`Extract returned empty text from ${action.selector}`);\n }\n return `${action.label}: ${value}`;\n }\n case \"scroll\": {\n const target = action.selector ? mustFind(action.selector) : document.documentElement;\n target.scrollBy({ top: action.deltaY, behavior: \"smooth\" });\n return `Scrolled ${action.deltaY > 0 ? \"down\" : \"up\"} ${Math.abs(action.deltaY)}px`;\n }\n case \"focus\": {\n mustFind(action.selector).focus();\n return `Focused ${action.selector}`;\n }\n case \"wait\": {\n await new Promise((resolve) => setTimeout(resolve, action.ms));\n return `Waited ${action.ms}ms`;\n }\n case \"done\": {\n return action.reason;\n }\n default:\n return \"No-op\";\n }\n}\n", "import type { CandidateElement, PageSnapshot } from \"../shared/contracts\";\n\nconst CANDIDATE_SELECTOR =\n \"a,button,input,textarea,select,[role='button'],[role='link'],[contenteditable='true']\";\n\nconst MAX_CANDIDATES = 60;\n\nfunction cssPath(element: Element): string {\n if (!(element instanceof HTMLElement)) {\n return element.tagName.toLowerCase();\n }\n\n if (element.id) {\n return `#${CSS.escape(element.id)}`;\n }\n\n // For form elements, prefer attribute-based selectors that are more stable\n const tag = element.tagName.toLowerCase();\n if (tag === \"input\" || tag === \"textarea\" || tag === \"select\") {\n const name = element.getAttribute(\"name\");\n if (name && document.querySelectorAll(`${tag}[name=${CSS.escape(name)}]`).length === 1) {\n return `${tag}[name=${CSS.escape(name)}]`;\n }\n const type = (element as HTMLInputElement).type;\n const placeholder = element.getAttribute(\"placeholder\");\n if (placeholder && document.querySelectorAll(`${tag}[placeholder=${CSS.escape(placeholder)}]`).length === 1) {\n return `${tag}[placeholder=${CSS.escape(placeholder)}]`;\n }\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel && document.querySelectorAll(`${tag}[aria-label=${CSS.escape(ariaLabel)}]`).length === 1) {\n return `${tag}[aria-label=${CSS.escape(ariaLabel)}]`;\n }\n // Combine name + type for uniqueness\n if (name && type) {\n const combo = `${tag}[name=${CSS.escape(name)}][type=${CSS.escape(type)}]`;\n if (document.querySelectorAll(combo).length === 1) {\n return combo;\n }\n }\n }\n\n // For buttons/links, prefer text-based or aria selectors\n if (tag === \"button\" || tag === \"a\") {\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel && document.querySelectorAll(`${tag}[aria-label=${CSS.escape(ariaLabel)}]`).length === 1) {\n return `${tag}[aria-label=${CSS.escape(ariaLabel)}]`;\n }\n }\n\n const parts: string[] = [];\n let current: HTMLElement | null = element;\n while (current && parts.length < 4) {\n let part = current.tagName.toLowerCase();\n if (current.classList.length > 0) {\n part += `.${Array.from(current.classList).slice(0, 2).map(CSS.escape).join(\".\")}`;\n }\n const parent: HTMLElement | null = current.parentElement;\n if (parent) {\n const siblings = Array.from(parent.children).filter((s: Element) => s.tagName === current!.tagName);\n if (siblings.length > 1) {\n const index = siblings.indexOf(current) + 1;\n part += `:nth-of-type(${index})`;\n }\n }\n parts.unshift(part);\n current = parent;\n }\n return parts.join(\" > \");\n}\n\nfunction isVisible(el: HTMLElement): boolean {\n const style = window.getComputedStyle(el);\n if (el.offsetParent === null && el.tagName !== \"BODY\" && style.position !== \"fixed\") return false;\n if (style.display === \"none\" || style.visibility === \"hidden\" || style.opacity === \"0\") return false;\n // Zero-dimension elements are functionally hidden\n const rect = el.getBoundingClientRect();\n return rect.width > 0 || rect.height > 0;\n}\n\nfunction isInViewport(el: HTMLElement): boolean {\n const rect = el.getBoundingClientRect();\n return (\n rect.bottom > 0 &&\n rect.top < window.innerHeight &&\n rect.right > 0 &&\n rect.left < window.innerWidth\n );\n}\n\nfunction isActiveElement(el: HTMLElement): boolean {\n if (el.classList.contains(\"active\")) return true;\n if (el.getAttribute(\"aria-selected\") === \"true\") return true;\n const ariaCurrent = el.getAttribute(\"aria-current\");\n if (ariaCurrent && ariaCurrent !== \"false\") return true;\n if (el.getAttribute(\"aria-pressed\") === \"true\") return true;\n return false;\n}\n\n/** Resolve the visible label text via for/id, aria-labelledby, aria-label, or wrapping <label>. */\nfunction getAssociatedLabel(el: HTMLElement): string {\n if (el.id) {\n const label = document.querySelector<HTMLLabelElement>(`label[for=\"${CSS.escape(el.id)}\"]`);\n if (label) return label.innerText.trim();\n }\n\n const labelledBy = el.getAttribute(\"aria-labelledby\");\n if (labelledBy) {\n const labelEl = document.getElementById(labelledBy);\n if (labelEl) return labelEl.innerText.trim();\n }\n\n const ariaLabel = el.getAttribute(\"aria-label\");\n if (ariaLabel) return ariaLabel.trim();\n\n const parentLabel = el.closest(\"label\");\n if (parentLabel) {\n return Array.from(parentLabel.childNodes)\n .filter((n) => n.nodeType === Node.TEXT_NODE)\n .map((n) => n.textContent?.trim() ?? \"\")\n .filter(Boolean)\n .join(\" \");\n }\n\n return \"\";\n}\n\nexport function collectSnapshot(): PageSnapshot {\n const allNodes = Array.from(\n document.querySelectorAll<HTMLElement>(CANDIDATE_SELECTOR)\n ).filter(isVisible).filter((el) => !el.closest(\"[data-agent-exclude]\"));\n\n // In-viewport elements first so the model sees the most relevant candidates first\n const inView = allNodes.filter(isInViewport);\n const offScreen = allNodes.filter((el) => !isInViewport(el));\n const nodes = [...inView, ...offScreen].slice(0, MAX_CANDIDATES);\n\n const candidates: CandidateElement[] = nodes.map((node) => {\n const placeholder =\n (node as HTMLInputElement).placeholder?.trim() || node.getAttribute(\"placeholder\")?.trim();\n const associatedLabel = getAssociatedLabel(node);\n return {\n selector: cssPath(node),\n role: node.getAttribute(\"role\") ?? node.tagName.toLowerCase(),\n text: (node.innerText || node.getAttribute(\"name\") || \"\").trim().slice(0, 120),\n placeholder: placeholder || undefined,\n label: associatedLabel || undefined,\n active: isActiveElement(node) || undefined,\n };\n });\n\n const textPreview = document.body.innerText.replace(/\\s+/g, \" \").trim().slice(0, 1500);\n\n return {\n url: window.location.href,\n title: document.title,\n textPreview,\n candidates,\n };\n}\n", "import type { AgentAction, CandidateElement, PlannerConfig, PlannerInput, PlannerResult } from \"../shared/contracts\";\n\ntype WebLLMBridge = {\n // Bridge may return PlannerResult (new), AgentAction (legacy), or raw LLM text.\n plan(input: PlannerInput, modelId?: string): Promise<PlannerResult | AgentAction | string>;\n // Optional retry hook that keeps chat history and asks for corrected JSON.\n retryInvalidJson?(input: PlannerInput, badOutput: string, modelId?: string): Promise<PlannerResult | AgentAction | string>;\n};\n\nconst URL_PATTERN = /(?:go to|navigate to|open)\\s+(https?:\\/\\/\\S+)/i;\nconst SEARCH_PATTERN = /search(?:\\s+for)?\\s+(.+)/i;\nconst FILL_PATTERN = /(?:fill|type|enter)\\s+\"?([^\"]+)\"?\\s+(?:in(?:to)?|for|on)\\s+(.+)/i;\nconst CLICK_PATTERN = /click(?:\\s+(?:on|the))?\\s+(.+)/i;\n\nfunction findByText(candidates: CandidateElement[], text: string): CandidateElement | undefined {\n const lower = text.toLowerCase();\n return candidates.find(\n (c) =>\n c.text.toLowerCase().includes(lower) ||\n (c.placeholder?.toLowerCase().includes(lower) ?? false) ||\n (c.label?.toLowerCase().includes(lower) ?? false)\n );\n}\n\nfunction findInput(candidates: CandidateElement[]): CandidateElement | undefined {\n return candidates.find(\n (c) => c.role === \"input\" || c.role === \"textarea\" || c.selector.includes(\"input\") || c.selector.includes(\"textarea\")\n );\n}\n\nfunction findButton(candidates: CandidateElement[]): CandidateElement | undefined {\n return candidates.find(\n (c) => c.role === \"button\" || c.role === \"a\" || c.selector.includes(\"button\") || c.selector.includes(\"a\")\n );\n}\n\nfunction heuristicPlan(input: PlannerInput): AgentAction {\n const { goal, snapshot, history } = input;\n\n const navMatch = goal.match(URL_PATTERN);\n if (navMatch) {\n return { type: \"navigate\", url: navMatch[1] };\n }\n\n const fillMatch = goal.match(FILL_PATTERN);\n if (fillMatch) {\n const [, text, fieldHint] = fillMatch;\n const target = findByText(snapshot.candidates, fieldHint) ?? findInput(snapshot.candidates);\n if (target) {\n return { type: \"type\", selector: target.selector, text, clearFirst: true, label: target.label || target.text || target.placeholder };\n }\n }\n\n const searchMatch = goal.match(SEARCH_PATTERN);\n if (searchMatch) {\n const input = findInput(snapshot.candidates);\n if (input) {\n return { type: \"type\", selector: input.selector, text: searchMatch[1].trim(), clearFirst: true, label: input.label || input.text || input.placeholder };\n }\n }\n\n const clickMatch = goal.match(CLICK_PATTERN);\n if (clickMatch) {\n const target = findByText(snapshot.candidates, clickMatch[1].trim());\n if (target) {\n return { type: \"click\", selector: target.selector, label: target.text };\n }\n }\n\n const firstInput = findInput(snapshot.candidates);\n const firstButton = findButton(snapshot.candidates);\n\n if (firstInput && !history.some((h) => h.startsWith(\"Typed\"))) {\n const searchTerm = goal.replace(/.*(?:search|find|look up)\\s+/i, \"\").trim();\n return { type: \"type\", selector: firstInput.selector, text: searchTerm, clearFirst: true, label: firstInput.label || firstInput.text || firstInput.placeholder };\n }\n\n if (firstButton && !history.some((h) => h.startsWith(\"Clicked\"))) {\n return { type: \"click\", selector: firstButton.selector, label: firstButton.text };\n }\n\n return { type: \"done\", reason: \"No further heuristic actions available\" };\n}\n\n/** Normalize whatever a bridge returns into a PlannerResult. */\nfunction toPlannerResult(raw: PlannerResult | AgentAction): PlannerResult {\n // New format: has an `action` key that is an object\n if (\"action\" in raw && typeof (raw as PlannerResult).action === \"object\") {\n return raw as PlannerResult;\n }\n // Legacy format: bare AgentAction\n return { action: raw as AgentAction };\n}\n\nasync function parsePlannerText(raw: string): Promise<{ result: PlannerResult; parseFailed: boolean }> {\n const { parsePlannerResult, PARSE_FAILURE_PATTERN } = await import(\"../shared/parse-action\");\n const result = parsePlannerResult(raw);\n const parseFailed = result.action.type === \"done\" && PARSE_FAILURE_PATTERN.test(result.action.reason);\n return { result, parseFailed };\n}\n\n\nasync function normalizeBridgeResponse(\n raw: PlannerResult | AgentAction | string\n): Promise<{ result: PlannerResult; parseFailed: boolean; rawText?: string }> {\n if (typeof raw === \"string\") {\n const { result, parseFailed } = await parsePlannerText(raw);\n return { result, parseFailed, rawText: raw };\n }\n\n return { result: toPlannerResult(raw), parseFailed: false };\n}\n\nexport async function planNextAction(config: PlannerConfig, input: PlannerInput): Promise<PlannerResult> {\n if (config.kind === \"heuristic\") {\n return { action: heuristicPlan(input) };\n }\n\n const bridge = (window as Window & { __browserAgentWebLLM?: WebLLMBridge }).__browserAgentWebLLM;\n if (!bridge) {\n return {\n action: {\n type: \"done\",\n reason: \"WebLLM bridge is not configured. Use heuristic mode or wire a WebLLM bridge implementation.\"\n }\n };\n }\n\n const plannerInput = { ...input, systemPrompt: config.systemPrompt };\n const firstAttempt = await normalizeBridgeResponse(await bridge.plan(plannerInput, config.modelId));\n\n if (!firstAttempt.parseFailed) {\n return firstAttempt.result;\n }\n\n if (bridge.retryInvalidJson && firstAttempt.rawText) {\n const retryAttempt = await normalizeBridgeResponse(\n await bridge.retryInvalidJson(plannerInput, firstAttempt.rawText, config.modelId)\n );\n\n if (!retryAttempt.parseFailed) {\n return retryAttempt.result;\n }\n }\n\n return {\n action: {\n type: \"done\",\n reason: \"WebLLM output could not be parsed after retry.\"\n }\n };\n}\n", "import type { AgentAction, CandidateElement, RiskLevel } from \"./contracts\";\n\nconst RISKY_KEYWORDS = /\\b(delete|remove|pay|purchase|submit|confirm|checkout|transfer|withdraw|send)\\b/i;\n\nfunction elementTextRisky(text?: string): boolean {\n return text != null && RISKY_KEYWORDS.test(text);\n}\n\nfunction candidateText(selector: string, candidates?: CandidateElement[]): string | undefined {\n const match = candidates?.find((c) => c.selector === selector);\n return match ? ([match.label, match.text, match.placeholder].filter(Boolean).join(\" \") || undefined) : undefined;\n}\n\nexport function assessRisk(action: AgentAction, candidates?: CandidateElement[]): RiskLevel {\n switch (action.type) {\n case \"navigate\": {\n // Fragment-only (#anchor) and relative paths are safe on-page navigations.\n if (action.url.startsWith(\"#\") || action.url.startsWith(\"/\") || action.url.startsWith(\"./\") || action.url.startsWith(\"../\")) {\n return \"safe\";\n }\n try {\n const next = new URL(action.url);\n if (![\"http:\", \"https:\"].includes(next.protocol)) {\n return \"blocked\";\n }\n } catch {\n // Unparseable URL that is not a known relative form \u2014 block it.\n return \"blocked\";\n }\n return \"safe\";\n }\n case \"click\": {\n const text = action.label ?? candidateText(action.selector, candidates) ?? action.selector;\n return elementTextRisky(text) ? \"review\" : \"safe\";\n }\n case \"type\": {\n const text = action.label ?? candidateText(action.selector, candidates) ?? action.selector;\n return elementTextRisky(text) ? \"review\" : \"safe\";\n }\n case \"focus\":\n case \"scroll\":\n case \"wait\":\n return \"safe\";\n case \"extract\":\n return \"review\";\n case \"done\":\n return \"safe\";\n default:\n return \"review\";\n }\n}\n", "import type { PlannerInput, PlannerResult } from \"../shared/contracts\";\nimport { parsePlannerResult, PARSE_FAILURE_PATTERN } from \"../shared/parse-action\";\nimport { buildSystemPrompt, buildUserMessage } from \"./prompt\";\n\nexport const INVALID_JSON_RETRY_MESSAGE = [\n \"Your reply was not valid JSON. Output ONLY a JSON object \u2014 no explanation, no markdown, no extra text.\",\n 'Required format: {\"evaluation\":\"...\",\"memory\":\"...\",\"nextGoal\":\"...\",\"action\":{\"type\":\"click\",\"selector\":\"EXACT_SELECTOR\",\"label\":\"...\"}}',\n].join(\"\\n\");\n\nconst BARE_ACTION_RETRY_MESSAGE = [\n \"Still not valid JSON. Output ONLY the action object. No evaluation, memory, or nextGoal fields.\",\n 'Example: {\"type\":\"type\",\"selector\":\"EXACT_SELECTOR_FROM_LIST\",\"text\":\"value\",\"clearFirst\":true}',\n 'Example: {\"type\":\"click\",\"selector\":\"EXACT_SELECTOR_FROM_LIST\",\"label\":\"button text\"}',\n].join(\"\\n\");\n\ntype ChatRole = \"system\" | \"user\" | \"assistant\";\n\ntype ChatMessage = {\n role: ChatRole;\n content: string;\n};\n\ntype CompletionRequest = {\n messages: ChatMessage[];\n temperature?: number;\n max_tokens?: number;\n model?: string;\n};\n\ntype CompletionResponse = {\n choices?: Array<{\n message?: {\n content?: unknown;\n };\n }>;\n};\n\nexport type WebLLMEngineLike = {\n chat: {\n completions: {\n create(request: CompletionRequest): Promise<CompletionResponse>;\n };\n };\n};\n\nexport type BrowserAgentWebLLMBridge = {\n plan(input: PlannerInput, modelId?: string): Promise<PlannerResult>;\n retryInvalidJson(input: PlannerInput, badOutput: string, modelId?: string): Promise<PlannerResult>;\n};\n\nfunction isParseFailure(result: PlannerResult): boolean {\n if (result.action.type !== \"done\") {\n return false;\n }\n\n const reason = result.action.reason;\n return PARSE_FAILURE_PATTERN.test(reason);\n}\n\nfunction contentToText(content: unknown): string {\n if (typeof content === \"string\") {\n return content;\n }\n\n if (Array.isArray(content)) {\n const chunks = content\n .map((part) => {\n if (typeof part === \"string\") {\n return part;\n }\n if (typeof part === \"object\" && part !== null && \"text\" in part && typeof part.text === \"string\") {\n return part.text;\n }\n return \"\";\n })\n .filter(Boolean);\n\n return chunks.join(\"\\n\");\n }\n\n return \"\";\n}\n\nfunction failureResult(attempts: number): PlannerResult {\n return {\n action: {\n type: \"done\",\n reason: `WebLLM returned invalid JSON after ${attempts} attempt${attempts === 1 ? \"\" : \"s\"}. Unable to continue.`\n }\n };\n}\n\nasync function createCompletion(engine: WebLLMEngineLike, messages: ChatMessage[], modelId?: string): Promise<string> {\n const response = await engine.chat.completions.create({\n messages,\n temperature: 0,\n max_tokens: 700,\n ...(modelId ? { model: modelId } : {})\n });\n\n return contentToText(response.choices?.[0]?.message?.content).trim();\n}\n\nfunction buildBaseMessages(input: PlannerInput): ChatMessage[] {\n return [\n { role: \"system\", content: buildSystemPrompt(input.systemPrompt) },\n { role: \"user\", content: buildUserMessage(input) }\n ];\n}\n\nasync function retryInvalidJsonWithHistory(\n engine: WebLLMEngineLike,\n baseMessages: ChatMessage[],\n badOutput: string,\n modelId?: string\n): Promise<PlannerResult> {\n // Attempt 2: improved message with format example\n const attempt2Messages: ChatMessage[] = [\n ...baseMessages,\n { role: \"assistant\", content: badOutput },\n { role: \"user\", content: INVALID_JSON_RETRY_MESSAGE }\n ];\n\n const raw2 = await createCompletion(engine, attempt2Messages, modelId);\n const result2 = parsePlannerResult(raw2);\n if (!isParseFailure(result2)) return result2;\n\n // Attempt 3: ask for bare action only (simpler target for small models)\n const attempt3Messages: ChatMessage[] = [\n ...attempt2Messages,\n { role: \"assistant\", content: raw2 },\n { role: \"user\", content: BARE_ACTION_RETRY_MESSAGE }\n ];\n\n const raw3 = await createCompletion(engine, attempt3Messages, modelId);\n const result3 = parsePlannerResult(raw3);\n if (!isParseFailure(result3)) return result3;\n\n return failureResult(3);\n}\n\nexport function createWebLLMBridge(engine: WebLLMEngineLike): BrowserAgentWebLLMBridge {\n return {\n async plan(input: PlannerInput, modelId?: string): Promise<PlannerResult> {\n const baseMessages = buildBaseMessages(input);\n\n // Attempt 1: normal\n const raw1 = await createCompletion(engine, baseMessages, modelId);\n const result1 = parsePlannerResult(raw1);\n if (!isParseFailure(result1)) return result1;\n\n // Attempts 2 & 3 with progressively simpler asks\n return retryInvalidJsonWithHistory(engine, baseMessages, raw1, modelId);\n },\n\n async retryInvalidJson(input: PlannerInput, badOutput: string, modelId?: string): Promise<PlannerResult> {\n const baseMessages = buildBaseMessages(input);\n return retryInvalidJsonWithHistory(engine, baseMessages, badOutput, modelId);\n }\n };\n}\n", "import type { CandidateElement, PlannerInput } from \"../shared/contracts\";\n\nconst MAX_TEXT_PREVIEW_LENGTH = 800;\n\nconst DEFAULT_SYSTEM_PROMPT = [\n \"You are OmniBrowser Agent, an on-page web automation planner.\",\n \"Choose exactly one next action using the provided page snapshot.\",\n \"\",\n \"Output rules:\",\n '- Reply with only one valid JSON object (no markdown fences, no prose).',\n '- JSON shape must be: { \"evaluation\": string, \"memory\": string, \"nextGoal\": string, \"action\": AgentAction }.',\n '- Keep evaluation/memory/nextGoal concise but informative.',\n \"\",\n \"Valid AgentAction shapes:\",\n '- {\"type\":\"click\",\"selector\":\"<css>\",\"label\":\"<optional>\"}',\n '- {\"type\":\"type\",\"selector\":\"<css>\",\"text\":\"<text>\",\"clearFirst\":true|false,\"label\":\"<optional>\"}',\n '- {\"type\":\"navigate\",\"url\":\"https://...\"}',\n '- {\"type\":\"extract\",\"selector\":\"<css>\",\"label\":\"<label>\"}',\n '- {\"type\":\"scroll\",\"selector\":\"<optional css>\",\"deltaY\":number}',\n '- {\"type\":\"focus\",\"selector\":\"<css>\"}',\n '- {\"type\":\"wait\",\"ms\":number}',\n '- {\"type\":\"done\",\"reason\":\"<reason>\"}',\n \"\",\n \"IMPORTANT: You MUST use selectors exactly as listed in the candidates. NEVER invent or guess selectors.\",\n \"If you cannot find a matching candidate for a target element, use the closest match from the candidates list.\",\n \"When previous step failed, recover by trying a different candidate selector or fallback strategy.\",\n \"NEVER use navigate for in-page tab switches or buttons \u2014 use click with the button's selector instead.\",\n \"\",\n \"Loop prevention rules (CRITICAL):\",\n \"- If a candidate shows 'state: active', it is already selected/active \u2014 do NOT click it again.\",\n \"- Check the History before acting. If the same selector was already clicked or typed in a recent step, do NOT repeat it \u2014 proceed to the next logical step or return done.\",\n \"- If the goal is already achieved (value is set, element clicked, task complete), return done immediately.\",\n \"- NEVER click a navigation tab or button more than once per goal unless the page changed to a different section.\"\n].join(\"\\n\");\n\nfunction formatCandidate(candidate: CandidateElement, index: number): string {\n const parts = [\n `${candidate.role || \"element\"}`,\n `selector: ${JSON.stringify(candidate.selector)}`,\n `text: ${JSON.stringify(candidate.text || \"\")}`\n ];\n\n if (candidate.label) {\n parts.push(`label: ${JSON.stringify(candidate.label)}`);\n }\n if (candidate.placeholder) {\n parts.push(`placeholder: ${JSON.stringify(candidate.placeholder)}`);\n }\n if (candidate.active) {\n parts.push(`state: active`);\n }\n\n return `[${index + 1}] ${parts.join(\" | \")}`;\n}\n\nexport function buildSystemPrompt(customPrompt?: string): string {\n if (!customPrompt?.trim()) {\n return DEFAULT_SYSTEM_PROMPT;\n }\n\n return `${DEFAULT_SYSTEM_PROMPT}\\n\\nAdditional instructions:\\n${customPrompt.trim()}`;\n}\n\nexport function buildUserMessage(input: PlannerInput): string {\n const { goal, snapshot, history, memory, lastError } = input;\n const candidates = snapshot.candidates.map(formatCandidate).join(\"\\n\");\n const historyLines = history.length > 0\n ? history.map((step, index) => `${index + 1}. ${step}`).join(\"\\n\")\n : \"(none)\";\n\n return [\n `Goal: ${goal}`,\n \"\",\n \"Page snapshot:\",\n `- URL: ${snapshot.url}`,\n `- Title: ${snapshot.title}`,\n `- Text preview (first ${MAX_TEXT_PREVIEW_LENGTH} chars): ${JSON.stringify(snapshot.textPreview.slice(0, MAX_TEXT_PREVIEW_LENGTH))}`,\n \"\",\n `Interactive candidates (${snapshot.candidates.length}):`,\n candidates || \"(none)\",\n \"\",\n \"History:\",\n historyLines,\n \"\",\n `Working memory: ${memory ? JSON.stringify(memory) : \"(none)\"}`,\n `Last error: ${lastError ? JSON.stringify(lastError) : \"(none)\"}`,\n \"If Last error is present, recover and choose a different valid action.\"\n ].join(\"\\n\");\n}\n", "import { executeAction } from \"../core/executor\";\nimport { collectSnapshot } from \"../core/observer\";\nimport { planNextAction } from \"../core/planner\";\nimport type {\n AgentAction,\n AgentSession,\n CandidateElement,\n ContentResult,\n LibraryAgentConfig,\n LibraryAgentEvents,\n PlannerConfig,\n PlannerResult\n} from \"../shared/contracts\";\nimport { assessRisk } from \"../shared/safety\";\n\nconst DEFAULT_PLANNER: PlannerConfig = { kind: \"heuristic\" };\n\n/** Max consecutive errors before the agent gives up instead of retrying */\nconst MAX_CONSECUTIVE_ERRORS = 2;\n\n/** Sliding window of recent action signatures used for loop detection */\nconst LOOP_WINDOW = 8;\nconst LOOP_THRESHOLD = 3;\n\nfunction actionSignature(action: AgentAction): string | null {\n if (action.type === \"click\" || action.type === \"type\" || action.type === \"focus\") {\n return `${action.type}:${action.selector}`;\n }\n return null;\n}\n\nexport class BrowserAgent {\n private session: AgentSession;\n private maxSteps: number;\n private stepDelayMs: number;\n private events: LibraryAgentEvents;\n private isStopped = false;\n private signal?: AbortSignal;\n private recentActionSigs: string[] = [];\n\n constructor(config: LibraryAgentConfig, events: LibraryAgentEvents = {}) {\n this.session = {\n id: crypto.randomUUID(),\n tabId: null,\n goal: config.goal,\n mode: config.mode ?? \"human-approved\",\n planner: config.planner ?? DEFAULT_PLANNER,\n history: [],\n isRunning: false\n };\n\n this.maxSteps = config.maxSteps ?? 20;\n this.stepDelayMs = config.stepDelayMs ?? 500;\n this.events = events;\n this.signal = config.signal;\n }\n\n getSession(): AgentSession {\n return { ...this.session, history: [...this.session.history] };\n }\n\n get isRunning(): boolean {\n return this.session.isRunning;\n }\n\n get hasPendingAction(): boolean {\n return this.session.pendingAction != null;\n }\n\n async start(): Promise<ContentResult> {\n this.isStopped = false;\n this.recentActionSigs = [];\n this.session.isRunning = true;\n this.events.onStart?.(this.getSession());\n return this.runLoop();\n }\n\n async resume(): Promise<ContentResult> {\n if (this.session.pendingAction) {\n const approvalResult = await this.approvePendingAction();\n if (approvalResult.status === \"error\") {\n return approvalResult;\n }\n }\n this.session.isRunning = true;\n return this.runLoop();\n }\n\n private async runLoop(): Promise<ContentResult> {\n let consecutiveErrors = 0;\n let lastError: string | undefined;\n\n for (let step = 0; step < this.maxSteps; step += 1) {\n if (this.isStopped || !this.session.isRunning) {\n return { status: \"done\", message: \"Stopped\" };\n }\n\n if (this.signal?.aborted) {\n this.session.isRunning = false;\n return { status: \"done\", message: \"Aborted\" };\n }\n\n const result = await this.tick(lastError);\n this.events.onStep?.(result, this.getSession());\n\n if (result.status === \"error\") {\n consecutiveErrors += 1;\n lastError = result.message;\n this.session.history.push(`Error: ${result.message}`);\n\n if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {\n this.session.isRunning = false;\n this.events.onError?.(new Error(result.message), this.getSession());\n this.events.onDone?.(result, this.getSession());\n return result;\n }\n\n await this.delay(this.stepDelayMs);\n continue;\n }\n\n consecutiveErrors = 0;\n lastError = undefined;\n this.session.history.push(result.message);\n\n if (result.status === \"needs_approval\") {\n this.session.pendingAction = result.action;\n this.session.isRunning = false;\n if (result.action) {\n this.events.onApprovalRequired?.(result.action, this.getSession());\n }\n return result;\n }\n\n if ([\"done\", \"blocked\"].includes(result.status)) {\n this.session.isRunning = false;\n this.events.onDone?.(result, this.getSession());\n return result;\n }\n\n await this.delay(this.stepDelayMs);\n }\n\n const result: ContentResult = { status: \"done\", message: \"Reached max steps\" };\n this.session.history.push(result.message);\n this.session.isRunning = false;\n this.events.onMaxStepsReached?.(this.getSession());\n this.events.onDone?.(result, this.getSession());\n return result;\n }\n\n async approvePendingAction(): Promise<ContentResult> {\n if (!this.session.pendingAction) {\n return { status: \"error\", message: \"No pending action to approve\" };\n }\n\n try {\n const message = await executeAction(this.session.pendingAction);\n const result: ContentResult = {\n status: \"executed\",\n message,\n action: this.session.pendingAction\n };\n this.session.history.push(message);\n this.session.pendingAction = undefined;\n this.session.isRunning = true;\n this.events.onStep?.(result, this.getSession());\n return result;\n } catch (error) {\n this.session.isRunning = false;\n this.events.onError?.(error, this.getSession());\n return { status: \"error\", message: String(error) };\n }\n }\n\n stop(): void {\n this.isStopped = true;\n this.session.isRunning = false;\n }\n\n private async tick(lastError?: string): Promise<ContentResult> {\n try {\n const snapshot = collectSnapshot();\n const plannerResult = await planNextAction(this.session.planner, {\n goal: this.session.goal,\n snapshot,\n history: this.session.history,\n lastError,\n memory: this.session.memory\n });\n\n // Carry working memory forward across steps\n if (plannerResult.memory !== undefined) {\n this.session.memory = plannerResult.memory;\n }\n\n return this.processAction(plannerResult, snapshot.candidates);\n } catch (error) {\n return { status: \"error\", message: String(error) };\n }\n }\n\n private async processAction(plannerResult: PlannerResult, candidates?: CandidateElement[]): Promise<ContentResult> {\n const { action } = plannerResult;\n const reflection = plannerResult.evaluation !== undefined || plannerResult.memory !== undefined || plannerResult.nextGoal !== undefined\n ? { evaluation: plannerResult.evaluation, memory: plannerResult.memory, nextGoal: plannerResult.nextGoal }\n : undefined;\n\n // Loop detection: if the same selector+action type appears LOOP_THRESHOLD times\n // in the recent window, the agent is stuck \u2014 force done rather than repeat.\n const sig = actionSignature(action);\n if (sig) {\n this.recentActionSigs.push(sig);\n if (this.recentActionSigs.length > LOOP_WINDOW) {\n this.recentActionSigs.shift();\n }\n const repeatCount = this.recentActionSigs.filter((s) => s === sig).length;\n if (repeatCount >= LOOP_THRESHOLD) {\n return {\n status: \"done\",\n action,\n message: \"Loop detected \u2014 goal appears to be complete.\",\n reflection\n };\n }\n }\n\n const risk = assessRisk(action, candidates);\n if (risk === \"blocked\") {\n return { status: \"blocked\", action, message: `Blocked action: ${JSON.stringify(action)}`, reflection };\n }\n\n if (this.session.mode === \"human-approved\" && risk === \"review\") {\n return { status: \"needs_approval\", action, message: `Approval needed for ${action.type}`, reflection };\n }\n\n if (action.type === \"done\") {\n return { status: \"done\", action, message: action.reason, reflection };\n }\n\n const message = await executeAction(action);\n return { status: \"executed\", action, message, reflection };\n }\n\n private async delay(ms: number): Promise<void> {\n await new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\nexport function createBrowserAgent(config: LibraryAgentConfig, events?: LibraryAgentEvents): BrowserAgent {\n return new BrowserAgent(config, events);\n}\n\nexport { createWebLLMBridge } from \"../core/webllm-bridge\";\nexport { parseAction, parsePlannerResult } from \"../shared/parse-action\";\nexport type { BrowserAgentWebLLMBridge, WebLLMEngineLike } from \"../core/webllm-bridge\";\n\nexport type {\n AgentAction,\n AgentMode,\n AgentSession,\n ContentResult,\n LibraryAgentConfig,\n LibraryAgentEvents,\n PlannerConfig,\n PlannerInput,\n PlannerKind,\n PlannerResult,\n RiskLevel\n} from \"../shared/contracts\";\n"],
5
+ "mappings": ";;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA,SAAS,uBAAuB,MAA6B;AAC3D,QAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,MAAI,UAAU,GAAI,QAAO;AAEzB,MAAI,QAAQ;AACZ,MAAI,WAAW;AACf,MAAI,UAAU;AAEd,WAAS,IAAI,OAAO,IAAI,KAAK,QAAQ,KAAK;AACxC,UAAM,KAAK,KAAK,CAAC;AACjB,QAAI,SAAS;AAAE,gBAAU;AAAO;AAAA,IAAU;AAC1C,QAAI,OAAO,QAAQ,UAAU;AAAE,gBAAU;AAAM;AAAA,IAAU;AACzD,QAAI,OAAO,KAAK;AAAE,iBAAW,CAAC;AAAU;AAAA,IAAU;AAClD,QAAI,SAAU;AACd,QAAI,OAAO,IAAK;AAAA,aACP,OAAO,KAAK;AACnB;AACA,UAAI,UAAU,EAAG,QAAO,KAAK,MAAM,OAAO,IAAI,CAAC;AAAA,IACjD;AAAA,EACF;AAGA,MAAI,QAAQ,GAAG;AACb,WAAO,KAAK,MAAM,KAAK,IAAI,IAAI,OAAO,KAAK;AAAA,EAC7C;AAEA,SAAO;AACT;AAQO,SAAS,YAAY,KAA0B;AACpD,QAAM,aAAa,IAAI,MAAM,8BAA8B;AAC3D,QAAM,YAAY,aAAa,WAAW,CAAC,EAAE,KAAK,IAAI,IAAI,KAAK;AAE/D,QAAM,UAAU,uBAAuB,SAAS;AAChD,MAAI,CAAC,SAAS;AACZ,WAAO,EAAE,MAAM,QAAQ,QAAQ,4BAA4B,IAAI,MAAM,GAAG,GAAG,CAAC,GAAG;AAAA,EACjF;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,OAAO;AAAA,EAC7B,QAAQ;AACN,WAAO,EAAE,MAAM,QAAQ,QAAQ,yBAAyB,QAAQ,MAAM,GAAG,GAAG,CAAC,GAAG;AAAA,EAClF;AAEA,MAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,MAAM,QAAQ,MAAM,GAAG;AAC1E,WAAO,EAAE,MAAM,QAAQ,QAAQ,gCAAgC;AAAA,EACjE;AAEA,QAAM,MAAM;AACZ,MAAI,OAAO,IAAI,SAAS,YAAY,CAAC,YAAY,IAAI,IAAI,IAAI,GAAG;AAC9D,WAAO,EAAE,MAAM,QAAQ,QAAQ,mCAAmC,OAAO,IAAI,IAAI,CAAC,GAAG;AAAA,EACvF;AAEA,QAAM,IAAI,IAAI;AACd,OAAK,MAAM,WAAW,MAAM,UAAU,MAAM,aAAa,MAAM,YAAY,OAAO,IAAI,aAAa,UAAU;AAC3G,WAAO,EAAE,MAAM,QAAQ,QAAQ,sDAAsD,CAAC,GAAG;AAAA,EAC3F;AACA,MAAI,MAAM,aAAa,OAAO,IAAI,UAAU,UAAU;AACpD,WAAO,EAAE,MAAM,QAAQ,QAAQ,0DAA0D;AAAA,EAC3F;AACA,MAAI,MAAM,UAAU,OAAO,IAAI,SAAS,UAAU;AAChD,WAAO,EAAE,MAAM,QAAQ,QAAQ,sDAAsD;AAAA,EACvF;AACA,MAAI,MAAM,cAAc,OAAO,IAAI,QAAQ,UAAU;AACnD,WAAO,EAAE,MAAM,QAAQ,QAAQ,yDAAyD;AAAA,EAC1F;AACA,MAAI,MAAM,YAAY,OAAO,IAAI,WAAW,UAAU;AACpD,WAAO,EAAE,MAAM,QAAQ,QAAQ,0DAA0D;AAAA,EAC3F;AACA,MAAI,MAAM,UAAU,OAAO,IAAI,OAAO,UAAU;AAC9C,WAAO,EAAE,MAAM,QAAQ,QAAQ,oDAAoD;AAAA,EACrF;AACA,MAAI,MAAM,UAAU,OAAO,IAAI,WAAW,UAAU;AAClD,WAAO,EAAE,MAAM,QAAQ,QAAQ,wDAAwD;AAAA,EACzF;AAEA,SAAO;AACT;AAWO,SAAS,mBAAmB,KAA4B;AAC7D,QAAM,aAAa,IAAI,MAAM,8BAA8B;AAC3D,QAAM,YAAY,aAAa,WAAW,CAAC,EAAE,KAAK,IAAI,IAAI,KAAK;AAE/D,QAAM,UAAU,uBAAuB,SAAS;AAChD,MAAI,CAAC,SAAS;AACZ,WAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,QAAQ,qBAAqB,IAAI,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE;AAAA,EACtF;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,OAAO;AAAA,EAC7B,QAAQ;AACN,WAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,QAAQ,qBAAqB,QAAQ,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE;AAAA,EAC1F;AAEA,MAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,MAAM,QAAQ,MAAM,GAAG;AAC1E,WAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,QAAQ,gCAAgC,EAAE;AAAA,EAC7E;AAEA,QAAM,MAAM;AAGZ,MAAI,OAAO,IAAI,WAAW,YAAY,IAAI,WAAW,MAAM;AACzD,UAAM,SAAS,YAAY,KAAK,UAAU,IAAI,MAAM,CAAC;AACrD,WAAO;AAAA,MACL;AAAA,MACA,YAAY,OAAO,IAAI,eAAe,WAAW,IAAI,aAAa;AAAA,MAClE,QAAY,OAAO,IAAI,WAAe,WAAW,IAAI,SAAa;AAAA,MAClE,UACE,OAAO,IAAI,aAAa,WACpB,IAAI,WACJ,OAAO,IAAI,cAAc,WACvB,IAAI,YACJ;AAAA,IACV;AAAA,EACF;AAGA,SAAO,EAAE,QAAQ,YAAY,OAAO,EAAE;AACxC;AApJA,IAEa,uBAEP;AAJN;AAAA;AAAA;AAEO,IAAM,wBAAwB;AAErC,IAAM,cAAc,oBAAI,IAAI;AAAA,MAC1B;AAAA,MAAS;AAAA,MAAQ;AAAA,MAAY;AAAA,MAAW;AAAA,MAAU;AAAA,MAAS;AAAA,MAAQ;AAAA,IACrE,CAAC;AAAA;AAAA;;;ACJD,SAAS,SAAS,UAA+B;AAC/C,QAAM,OAAO,SAAS,cAAc,QAAQ;AAC5C,MAAI,gBAAgB,aAAa;AAC/B,WAAO;AAAA,EACT;AAIA,QAAM,WAAW,SAAS,MAAM,QAAQ;AACxC,MAAI,UAAU;AACZ,UAAM,MAAM,SAAS,CAAC;AAEtB,UAAM,YAAY,SAAS,MAAM,qCAAqC;AACtE,QAAI,WAAW;AACb,YAAM,WAAW,SAAS,cAAc,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,IAAI;AACnF,UAAI,oBAAoB,YAAa,QAAO;AAAA,IAC9C;AAEA,UAAM,WAAW,SAAS,iBAAiB,GAAG;AAC9C,QAAI,SAAS,WAAW,KAAK,SAAS,CAAC,aAAa,aAAa;AAC/D,aAAO,SAAS,CAAC;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,uBAAuB,QAAQ,EAAE;AACnD;AAEA,SAAS,oBAAoB,IAAkD;AAC7E,KAAG,cAAc,IAAI,WAAW,SAAS,EAAE,SAAS,MAAM,YAAY,KAAK,CAAC,CAAC;AAC7E,KAAG,cAAc,IAAI,MAAM,UAAU,EAAE,SAAS,KAAK,CAAC,CAAC;AACzD;AAEA,eAAsB,cAAc,QAAsC;AACxE,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK,SAAS;AACZ,YAAM,KAAK,SAAS,OAAO,QAAQ;AACnC,UAAK,GAAyB,UAAU;AACtC,cAAM,IAAI,MAAM,wBAAwB,OAAO,QAAQ,EAAE;AAAA,MAC3D;AACA,SAAG,MAAM;AACT,aAAO,WAAW,OAAO,QAAQ;AAAA,IACnC;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,QAAQ,SAAS,OAAO,QAAQ;AACtC,UAAI,MAAM,UAAU,OAAO,MAAM;AAC/B,eAAO,qCAAqC,OAAO,QAAQ;AAAA,MAC7D;AACA,YAAM,MAAM;AACZ,UAAI,OAAO,YAAY;AACrB,cAAM,QAAQ;AACd,4BAAoB,KAAK;AAAA,MAC3B;AACA,YAAM,QAAQ,GAAG,MAAM,KAAK,GAAG,OAAO,IAAI;AAC1C,0BAAoB,KAAK;AACzB,UAAI,MAAM,MAAM,QAAQ,OAAO,IAAI,MAAM,IAAI;AAC3C,cAAM,IAAI,MAAM,sDAAsD,OAAO,QAAQ,EAAE;AAAA,MACzF;AACA,aAAO,cAAc,OAAO,QAAQ;AAAA,IACtC;AAAA,IACA,KAAK,YAAY;AACf,aAAO,SAAS,OAAO,OAAO;AAC9B,aAAO,gBAAgB,OAAO,GAAG;AAAA,IACnC;AAAA,IACA,KAAK,WAAW;AACd,YAAM,QAAQ,SAAS,OAAO,QAAQ,EAAE,UAAU,KAAK;AACvD,UAAI,CAAC,OAAO;AACV,cAAM,IAAI,MAAM,oCAAoC,OAAO,QAAQ,EAAE;AAAA,MACvE;AACA,aAAO,GAAG,OAAO,KAAK,KAAK,KAAK;AAAA,IAClC;AAAA,IACA,KAAK,UAAU;AACb,YAAM,SAAS,OAAO,WAAW,SAAS,OAAO,QAAQ,IAAI,SAAS;AACtE,aAAO,SAAS,EAAE,KAAK,OAAO,QAAQ,UAAU,SAAS,CAAC;AAC1D,aAAO,YAAY,OAAO,SAAS,IAAI,SAAS,IAAI,IAAI,KAAK,IAAI,OAAO,MAAM,CAAC;AAAA,IACjF;AAAA,IACA,KAAK,SAAS;AACZ,eAAS,OAAO,QAAQ,EAAE,MAAM;AAChC,aAAO,WAAW,OAAO,QAAQ;AAAA,IACnC;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,OAAO,EAAE,CAAC;AAC7D,aAAO,UAAU,OAAO,EAAE;AAAA,IAC5B;AAAA,IACA,KAAK,QAAQ;AACX,aAAO,OAAO;AAAA,IAChB;AAAA,IACA;AACE,aAAO;AAAA,EACX;AACF;;;ACzFA,IAAM,qBACJ;AAEF,IAAM,iBAAiB;AAEvB,SAAS,QAAQ,SAA0B;AACzC,MAAI,EAAE,mBAAmB,cAAc;AACrC,WAAO,QAAQ,QAAQ,YAAY;AAAA,EACrC;AAEA,MAAI,QAAQ,IAAI;AACd,WAAO,IAAI,IAAI,OAAO,QAAQ,EAAE,CAAC;AAAA,EACnC;AAGA,QAAM,MAAM,QAAQ,QAAQ,YAAY;AACxC,MAAI,QAAQ,WAAW,QAAQ,cAAc,QAAQ,UAAU;AAC7D,UAAM,OAAO,QAAQ,aAAa,MAAM;AACxC,QAAI,QAAQ,SAAS,iBAAiB,GAAG,GAAG,SAAS,IAAI,OAAO,IAAI,CAAC,GAAG,EAAE,WAAW,GAAG;AACtF,aAAO,GAAG,GAAG,SAAS,IAAI,OAAO,IAAI,CAAC;AAAA,IACxC;AACA,UAAM,OAAQ,QAA6B;AAC3C,UAAM,cAAc,QAAQ,aAAa,aAAa;AACtD,QAAI,eAAe,SAAS,iBAAiB,GAAG,GAAG,gBAAgB,IAAI,OAAO,WAAW,CAAC,GAAG,EAAE,WAAW,GAAG;AAC3G,aAAO,GAAG,GAAG,gBAAgB,IAAI,OAAO,WAAW,CAAC;AAAA,IACtD;AACA,UAAM,YAAY,QAAQ,aAAa,YAAY;AACnD,QAAI,aAAa,SAAS,iBAAiB,GAAG,GAAG,eAAe,IAAI,OAAO,SAAS,CAAC,GAAG,EAAE,WAAW,GAAG;AACtG,aAAO,GAAG,GAAG,eAAe,IAAI,OAAO,SAAS,CAAC;AAAA,IACnD;AAEA,QAAI,QAAQ,MAAM;AAChB,YAAM,QAAQ,GAAG,GAAG,SAAS,IAAI,OAAO,IAAI,CAAC,UAAU,IAAI,OAAO,IAAI,CAAC;AACvE,UAAI,SAAS,iBAAiB,KAAK,EAAE,WAAW,GAAG;AACjD,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAGA,MAAI,QAAQ,YAAY,QAAQ,KAAK;AACnC,UAAM,YAAY,QAAQ,aAAa,YAAY;AACnD,QAAI,aAAa,SAAS,iBAAiB,GAAG,GAAG,eAAe,IAAI,OAAO,SAAS,CAAC,GAAG,EAAE,WAAW,GAAG;AACtG,aAAO,GAAG,GAAG,eAAe,IAAI,OAAO,SAAS,CAAC;AAAA,IACnD;AAAA,EACF;AAEA,QAAM,QAAkB,CAAC;AACzB,MAAI,UAA8B;AAClC,SAAO,WAAW,MAAM,SAAS,GAAG;AAClC,QAAI,OAAO,QAAQ,QAAQ,YAAY;AACvC,QAAI,QAAQ,UAAU,SAAS,GAAG;AAChC,cAAQ,IAAI,MAAM,KAAK,QAAQ,SAAS,EAAE,MAAM,GAAG,CAAC,EAAE,IAAI,IAAI,MAAM,EAAE,KAAK,GAAG,CAAC;AAAA,IACjF;AACA,UAAM,SAA6B,QAAQ;AAC3C,QAAI,QAAQ;AACV,YAAM,WAAW,MAAM,KAAK,OAAO,QAAQ,EAAE,OAAO,CAAC,MAAe,EAAE,YAAY,QAAS,OAAO;AAClG,UAAI,SAAS,SAAS,GAAG;AACvB,cAAM,QAAQ,SAAS,QAAQ,OAAO,IAAI;AAC1C,gBAAQ,gBAAgB,KAAK;AAAA,MAC/B;AAAA,IACF;AACA,UAAM,QAAQ,IAAI;AAClB,cAAU;AAAA,EACZ;AACA,SAAO,MAAM,KAAK,KAAK;AACzB;AAEA,SAAS,UAAU,IAA0B;AAC3C,QAAM,QAAQ,OAAO,iBAAiB,EAAE;AACxC,MAAI,GAAG,iBAAiB,QAAQ,GAAG,YAAY,UAAU,MAAM,aAAa,QAAS,QAAO;AAC5F,MAAI,MAAM,YAAY,UAAU,MAAM,eAAe,YAAY,MAAM,YAAY,IAAK,QAAO;AAE/F,QAAM,OAAO,GAAG,sBAAsB;AACtC,SAAO,KAAK,QAAQ,KAAK,KAAK,SAAS;AACzC;AAEA,SAAS,aAAa,IAA0B;AAC9C,QAAM,OAAO,GAAG,sBAAsB;AACtC,SACE,KAAK,SAAS,KACd,KAAK,MAAM,OAAO,eAClB,KAAK,QAAQ,KACb,KAAK,OAAO,OAAO;AAEvB;AAEA,SAAS,gBAAgB,IAA0B;AACjD,MAAI,GAAG,UAAU,SAAS,QAAQ,EAAG,QAAO;AAC5C,MAAI,GAAG,aAAa,eAAe,MAAM,OAAQ,QAAO;AACxD,QAAM,cAAc,GAAG,aAAa,cAAc;AAClD,MAAI,eAAe,gBAAgB,QAAS,QAAO;AACnD,MAAI,GAAG,aAAa,cAAc,MAAM,OAAQ,QAAO;AACvD,SAAO;AACT;AAGA,SAAS,mBAAmB,IAAyB;AACnD,MAAI,GAAG,IAAI;AACT,UAAM,QAAQ,SAAS,cAAgC,cAAc,IAAI,OAAO,GAAG,EAAE,CAAC,IAAI;AAC1F,QAAI,MAAO,QAAO,MAAM,UAAU,KAAK;AAAA,EACzC;AAEA,QAAM,aAAa,GAAG,aAAa,iBAAiB;AACpD,MAAI,YAAY;AACd,UAAM,UAAU,SAAS,eAAe,UAAU;AAClD,QAAI,QAAS,QAAO,QAAQ,UAAU,KAAK;AAAA,EAC7C;AAEA,QAAM,YAAY,GAAG,aAAa,YAAY;AAC9C,MAAI,UAAW,QAAO,UAAU,KAAK;AAErC,QAAM,cAAc,GAAG,QAAQ,OAAO;AACtC,MAAI,aAAa;AACf,WAAO,MAAM,KAAK,YAAY,UAAU,EACrC,OAAO,CAAC,MAAM,EAAE,aAAa,KAAK,SAAS,EAC3C,IAAI,CAAC,MAAM,EAAE,aAAa,KAAK,KAAK,EAAE,EACtC,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,EACb;AAEA,SAAO;AACT;AAEO,SAAS,kBAAgC;AAC9C,QAAM,WAAW,MAAM;AAAA,IACrB,SAAS,iBAA8B,kBAAkB;AAAA,EAC3D,EAAE,OAAO,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,sBAAsB,CAAC;AAGtE,QAAM,SAAS,SAAS,OAAO,YAAY;AAC3C,QAAM,YAAY,SAAS,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;AAC3D,QAAM,QAAQ,CAAC,GAAG,QAAQ,GAAG,SAAS,EAAE,MAAM,GAAG,cAAc;AAE/D,QAAM,aAAiC,MAAM,IAAI,CAAC,SAAS;AACzD,UAAM,cACH,KAA0B,aAAa,KAAK,KAAK,KAAK,aAAa,aAAa,GAAG,KAAK;AAC3F,UAAM,kBAAkB,mBAAmB,IAAI;AAC/C,WAAO;AAAA,MACL,UAAU,QAAQ,IAAI;AAAA,MACtB,MAAM,KAAK,aAAa,MAAM,KAAK,KAAK,QAAQ,YAAY;AAAA,MAC5D,OAAO,KAAK,aAAa,KAAK,aAAa,MAAM,KAAK,IAAI,KAAK,EAAE,MAAM,GAAG,GAAG;AAAA,MAC7E,aAAa,eAAe;AAAA,MAC5B,OAAO,mBAAmB;AAAA,MAC1B,QAAQ,gBAAgB,IAAI,KAAK;AAAA,IACnC;AAAA,EACF,CAAC;AAED,QAAM,cAAc,SAAS,KAAK,UAAU,QAAQ,QAAQ,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;AAErF,SAAO;AAAA,IACL,KAAK,OAAO,SAAS;AAAA,IACrB,OAAO,SAAS;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACF;;;ACrJA,IAAM,cAAc;AACpB,IAAM,iBAAiB;AACvB,IAAM,eAAe;AACrB,IAAM,gBAAgB;AAEtB,SAAS,WAAW,YAAgC,MAA4C;AAC9F,QAAM,QAAQ,KAAK,YAAY;AAC/B,SAAO,WAAW;AAAA,IAChB,CAAC,MACC,EAAE,KAAK,YAAY,EAAE,SAAS,KAAK,MAClC,EAAE,aAAa,YAAY,EAAE,SAAS,KAAK,KAAK,WAChD,EAAE,OAAO,YAAY,EAAE,SAAS,KAAK,KAAK;AAAA,EAC/C;AACF;AAEA,SAAS,UAAU,YAA8D;AAC/E,SAAO,WAAW;AAAA,IAChB,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,SAAS,cAAc,EAAE,SAAS,SAAS,OAAO,KAAK,EAAE,SAAS,SAAS,UAAU;AAAA,EACtH;AACF;AAEA,SAAS,WAAW,YAA8D;AAChF,SAAO,WAAW;AAAA,IAChB,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,SAAS,OAAO,EAAE,SAAS,SAAS,QAAQ,KAAK,EAAE,SAAS,SAAS,GAAG;AAAA,EAC1G;AACF;AAEA,SAAS,cAAc,OAAkC;AACvD,QAAM,EAAE,MAAM,UAAU,QAAQ,IAAI;AAEpC,QAAM,WAAW,KAAK,MAAM,WAAW;AACvC,MAAI,UAAU;AACZ,WAAO,EAAE,MAAM,YAAY,KAAK,SAAS,CAAC,EAAE;AAAA,EAC9C;AAEA,QAAM,YAAY,KAAK,MAAM,YAAY;AACzC,MAAI,WAAW;AACb,UAAM,CAAC,EAAE,MAAM,SAAS,IAAI;AAC5B,UAAM,SAAS,WAAW,SAAS,YAAY,SAAS,KAAK,UAAU,SAAS,UAAU;AAC1F,QAAI,QAAQ;AACV,aAAO,EAAE,MAAM,QAAQ,UAAU,OAAO,UAAU,MAAM,YAAY,MAAM,OAAO,OAAO,SAAS,OAAO,QAAQ,OAAO,YAAY;AAAA,IACrI;AAAA,EACF;AAEA,QAAM,cAAc,KAAK,MAAM,cAAc;AAC7C,MAAI,aAAa;AACf,UAAMA,SAAQ,UAAU,SAAS,UAAU;AAC3C,QAAIA,QAAO;AACT,aAAO,EAAE,MAAM,QAAQ,UAAUA,OAAM,UAAU,MAAM,YAAY,CAAC,EAAE,KAAK,GAAG,YAAY,MAAM,OAAOA,OAAM,SAASA,OAAM,QAAQA,OAAM,YAAY;AAAA,IACxJ;AAAA,EACF;AAEA,QAAM,aAAa,KAAK,MAAM,aAAa;AAC3C,MAAI,YAAY;AACd,UAAM,SAAS,WAAW,SAAS,YAAY,WAAW,CAAC,EAAE,KAAK,CAAC;AACnE,QAAI,QAAQ;AACV,aAAO,EAAE,MAAM,SAAS,UAAU,OAAO,UAAU,OAAO,OAAO,KAAK;AAAA,IACxE;AAAA,EACF;AAEA,QAAM,aAAa,UAAU,SAAS,UAAU;AAChD,QAAM,cAAc,WAAW,SAAS,UAAU;AAElD,MAAI,cAAc,CAAC,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,OAAO,CAAC,GAAG;AAC7D,UAAM,aAAa,KAAK,QAAQ,iCAAiC,EAAE,EAAE,KAAK;AAC1E,WAAO,EAAE,MAAM,QAAQ,UAAU,WAAW,UAAU,MAAM,YAAY,YAAY,MAAM,OAAO,WAAW,SAAS,WAAW,QAAQ,WAAW,YAAY;AAAA,EACjK;AAEA,MAAI,eAAe,CAAC,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,SAAS,CAAC,GAAG;AAChE,WAAO,EAAE,MAAM,SAAS,UAAU,YAAY,UAAU,OAAO,YAAY,KAAK;AAAA,EAClF;AAEA,SAAO,EAAE,MAAM,QAAQ,QAAQ,yCAAyC;AAC1E;AAGA,SAAS,gBAAgB,KAAiD;AAExE,MAAI,YAAY,OAAO,OAAQ,IAAsB,WAAW,UAAU;AACxE,WAAO;AAAA,EACT;AAEA,SAAO,EAAE,QAAQ,IAAmB;AACtC;AAEA,eAAe,iBAAiB,KAAuE;AACrG,QAAM,EAAE,oBAAAC,qBAAoB,uBAAAC,uBAAsB,IAAI,MAAM;AAC5D,QAAM,SAASD,oBAAmB,GAAG;AACrC,QAAM,cAAc,OAAO,OAAO,SAAS,UAAUC,uBAAsB,KAAK,OAAO,OAAO,MAAM;AACpG,SAAO,EAAE,QAAQ,YAAY;AAC/B;AAGA,eAAe,wBACb,KAC4E;AAC5E,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,EAAE,QAAQ,YAAY,IAAI,MAAM,iBAAiB,GAAG;AAC1D,WAAO,EAAE,QAAQ,aAAa,SAAS,IAAI;AAAA,EAC7C;AAEA,SAAO,EAAE,QAAQ,gBAAgB,GAAG,GAAG,aAAa,MAAM;AAC5D;AAEA,eAAsB,eAAe,QAAuB,OAA6C;AACvG,MAAI,OAAO,SAAS,aAAa;AAC/B,WAAO,EAAE,QAAQ,cAAc,KAAK,EAAE;AAAA,EACxC;AAEA,QAAM,SAAU,OAA4D;AAC5E,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,MACL,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAEA,QAAM,eAAe,EAAE,GAAG,OAAO,cAAc,OAAO,aAAa;AACnE,QAAM,eAAe,MAAM,wBAAwB,MAAM,OAAO,KAAK,cAAc,OAAO,OAAO,CAAC;AAElG,MAAI,CAAC,aAAa,aAAa;AAC7B,WAAO,aAAa;AAAA,EACtB;AAEA,MAAI,OAAO,oBAAoB,aAAa,SAAS;AACnD,UAAM,eAAe,MAAM;AAAA,MACzB,MAAM,OAAO,iBAAiB,cAAc,aAAa,SAAS,OAAO,OAAO;AAAA,IAClF;AAEA,QAAI,CAAC,aAAa,aAAa;AAC7B,aAAO,aAAa;AAAA,IACtB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,IACV;AAAA,EACF;AACF;;;ACrJA,IAAM,iBAAiB;AAEvB,SAAS,iBAAiB,MAAwB;AAChD,SAAO,QAAQ,QAAQ,eAAe,KAAK,IAAI;AACjD;AAEA,SAAS,cAAc,UAAkB,YAAqD;AAC5F,QAAM,QAAQ,YAAY,KAAK,CAAC,MAAM,EAAE,aAAa,QAAQ;AAC7D,SAAO,QAAS,CAAC,MAAM,OAAO,MAAM,MAAM,MAAM,WAAW,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,KAAK,SAAa;AACzG;AAEO,SAAS,WAAW,QAAqB,YAA4C;AAC1F,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK,YAAY;AAEf,UAAI,OAAO,IAAI,WAAW,GAAG,KAAK,OAAO,IAAI,WAAW,GAAG,KAAK,OAAO,IAAI,WAAW,IAAI,KAAK,OAAO,IAAI,WAAW,KAAK,GAAG;AAC3H,eAAO;AAAA,MACT;AACA,UAAI;AACF,cAAM,OAAO,IAAI,IAAI,OAAO,GAAG;AAC/B,YAAI,CAAC,CAAC,SAAS,QAAQ,EAAE,SAAS,KAAK,QAAQ,GAAG;AAChD,iBAAO;AAAA,QACT;AAAA,MACF,QAAQ;AAEN,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,IACA,KAAK,SAAS;AACZ,YAAM,OAAO,OAAO,SAAS,cAAc,OAAO,UAAU,UAAU,KAAK,OAAO;AAClF,aAAO,iBAAiB,IAAI,IAAI,WAAW;AAAA,IAC7C;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,OAAO,OAAO,SAAS,cAAc,OAAO,UAAU,UAAU,KAAK,OAAO;AAClF,aAAO,iBAAiB,IAAI,IAAI,WAAW;AAAA,IAC7C;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;;;ACjDA;;;ACCA,IAAM,0BAA0B;AAEhC,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAEX,SAAS,gBAAgB,WAA6B,OAAuB;AAC3E,QAAM,QAAQ;AAAA,IACZ,GAAG,UAAU,QAAQ,SAAS;AAAA,IAC9B,aAAa,KAAK,UAAU,UAAU,QAAQ,CAAC;AAAA,IAC/C,SAAS,KAAK,UAAU,UAAU,QAAQ,EAAE,CAAC;AAAA,EAC/C;AAEA,MAAI,UAAU,OAAO;AACnB,UAAM,KAAK,UAAU,KAAK,UAAU,UAAU,KAAK,CAAC,EAAE;AAAA,EACxD;AACA,MAAI,UAAU,aAAa;AACzB,UAAM,KAAK,gBAAgB,KAAK,UAAU,UAAU,WAAW,CAAC,EAAE;AAAA,EACpE;AACA,MAAI,UAAU,QAAQ;AACpB,UAAM,KAAK,eAAe;AAAA,EAC5B;AAEA,SAAO,IAAI,QAAQ,CAAC,KAAK,MAAM,KAAK,KAAK,CAAC;AAC5C;AAEO,SAAS,kBAAkB,cAA+B;AAC/D,MAAI,CAAC,cAAc,KAAK,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,SAAO,GAAG,qBAAqB;AAAA;AAAA;AAAA,EAAiC,aAAa,KAAK,CAAC;AACrF;AAEO,SAAS,iBAAiB,OAA6B;AAC5D,QAAM,EAAE,MAAM,UAAU,SAAS,QAAQ,UAAU,IAAI;AACvD,QAAM,aAAa,SAAS,WAAW,IAAI,eAAe,EAAE,KAAK,IAAI;AACrE,QAAM,eAAe,QAAQ,SAAS,IAClC,QAAQ,IAAI,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,IAAI,EAAE,EAAE,KAAK,IAAI,IAC/D;AAEJ,SAAO;AAAA,IACL,SAAS,IAAI;AAAA,IACb;AAAA,IACA;AAAA,IACA,UAAU,SAAS,GAAG;AAAA,IACtB,YAAY,SAAS,KAAK;AAAA,IAC1B,yBAAyB,uBAAuB,YAAY,KAAK,UAAU,SAAS,YAAY,MAAM,GAAG,uBAAuB,CAAC,CAAC;AAAA,IAClI;AAAA,IACA,2BAA2B,SAAS,WAAW,MAAM;AAAA,IACrD,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB,SAAS,KAAK,UAAU,MAAM,IAAI,QAAQ;AAAA,IAC7D,eAAe,YAAY,KAAK,UAAU,SAAS,IAAI,QAAQ;AAAA,IAC/D;AAAA,EACF,EAAE,KAAK,IAAI;AACb;;;ADpFO,IAAM,6BAA6B;AAAA,EACxC;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAEX,IAAM,4BAA4B;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAqCX,SAAS,eAAe,QAAgC;AACtD,MAAI,OAAO,OAAO,SAAS,QAAQ;AACjC,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,OAAO,OAAO;AAC7B,SAAO,sBAAsB,KAAK,MAAM;AAC1C;AAEA,SAAS,cAAc,SAA0B;AAC/C,MAAI,OAAO,YAAY,UAAU;AAC/B,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,QAAQ,OAAO,GAAG;AAC1B,UAAM,SAAS,QACZ,IAAI,CAAC,SAAS;AACb,UAAI,OAAO,SAAS,UAAU;AAC5B,eAAO;AAAA,MACT;AACA,UAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,UAAU,QAAQ,OAAO,KAAK,SAAS,UAAU;AAChG,eAAO,KAAK;AAAA,MACd;AACA,aAAO;AAAA,IACT,CAAC,EACA,OAAO,OAAO;AAEjB,WAAO,OAAO,KAAK,IAAI;AAAA,EACzB;AAEA,SAAO;AACT;AAEA,SAAS,cAAc,UAAiC;AACtD,SAAO;AAAA,IACL,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,QAAQ,sCAAsC,QAAQ,WAAW,aAAa,IAAI,KAAK,GAAG;AAAA,IAC5F;AAAA,EACF;AACF;AAEA,eAAe,iBAAiB,QAA0B,UAAyB,SAAmC;AACpH,QAAM,WAAW,MAAM,OAAO,KAAK,YAAY,OAAO;AAAA,IACpD;AAAA,IACA,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,GAAI,UAAU,EAAE,OAAO,QAAQ,IAAI,CAAC;AAAA,EACtC,CAAC;AAED,SAAO,cAAc,SAAS,UAAU,CAAC,GAAG,SAAS,OAAO,EAAE,KAAK;AACrE;AAEA,SAAS,kBAAkB,OAAoC;AAC7D,SAAO;AAAA,IACL,EAAE,MAAM,UAAU,SAAS,kBAAkB,MAAM,YAAY,EAAE;AAAA,IACjE,EAAE,MAAM,QAAQ,SAAS,iBAAiB,KAAK,EAAE;AAAA,EACnD;AACF;AAEA,eAAe,4BACb,QACA,cACA,WACA,SACwB;AAExB,QAAM,mBAAkC;AAAA,IACtC,GAAG;AAAA,IACH,EAAE,MAAM,aAAa,SAAS,UAAU;AAAA,IACxC,EAAE,MAAM,QAAQ,SAAS,2BAA2B;AAAA,EACtD;AAEA,QAAM,OAAO,MAAM,iBAAiB,QAAQ,kBAAkB,OAAO;AACrE,QAAM,UAAU,mBAAmB,IAAI;AACvC,MAAI,CAAC,eAAe,OAAO,EAAG,QAAO;AAGrC,QAAM,mBAAkC;AAAA,IACtC,GAAG;AAAA,IACH,EAAE,MAAM,aAAa,SAAS,KAAK;AAAA,IACnC,EAAE,MAAM,QAAQ,SAAS,0BAA0B;AAAA,EACrD;AAEA,QAAM,OAAO,MAAM,iBAAiB,QAAQ,kBAAkB,OAAO;AACrE,QAAM,UAAU,mBAAmB,IAAI;AACvC,MAAI,CAAC,eAAe,OAAO,EAAG,QAAO;AAErC,SAAO,cAAc,CAAC;AACxB;AAEO,SAAS,mBAAmB,QAAoD;AACrF,SAAO;AAAA,IACL,MAAM,KAAK,OAAqB,SAA0C;AACxE,YAAM,eAAe,kBAAkB,KAAK;AAG5C,YAAM,OAAO,MAAM,iBAAiB,QAAQ,cAAc,OAAO;AACjE,YAAM,UAAU,mBAAmB,IAAI;AACvC,UAAI,CAAC,eAAe,OAAO,EAAG,QAAO;AAGrC,aAAO,4BAA4B,QAAQ,cAAc,MAAM,OAAO;AAAA,IACxE;AAAA,IAEA,MAAM,iBAAiB,OAAqB,WAAmB,SAA0C;AACvG,YAAM,eAAe,kBAAkB,KAAK;AAC5C,aAAO,4BAA4B,QAAQ,cAAc,WAAW,OAAO;AAAA,IAC7E;AAAA,EACF;AACF;;;AE8FA;AA/OA,IAAM,kBAAiC,EAAE,MAAM,YAAY;AAG3D,IAAM,yBAAyB;AAG/B,IAAM,cAAc;AACpB,IAAM,iBAAiB;AAEvB,SAAS,gBAAgB,QAAoC;AAC3D,MAAI,OAAO,SAAS,WAAW,OAAO,SAAS,UAAU,OAAO,SAAS,SAAS;AAChF,WAAO,GAAG,OAAO,IAAI,IAAI,OAAO,QAAQ;AAAA,EAC1C;AACA,SAAO;AACT;AAEO,IAAM,eAAN,MAAmB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,mBAA6B,CAAC;AAAA,EAEtC,YAAY,QAA4B,SAA6B,CAAC,GAAG;AACvE,SAAK,UAAU;AAAA,MACb,IAAI,OAAO,WAAW;AAAA,MACtB,OAAO;AAAA,MACP,MAAM,OAAO;AAAA,MACb,MAAM,OAAO,QAAQ;AAAA,MACrB,SAAS,OAAO,WAAW;AAAA,MAC3B,SAAS,CAAC;AAAA,MACV,WAAW;AAAA,IACb;AAEA,SAAK,WAAW,OAAO,YAAY;AACnC,SAAK,cAAc,OAAO,eAAe;AACzC,SAAK,SAAS;AACd,SAAK,SAAS,OAAO;AAAA,EACvB;AAAA,EAEA,aAA2B;AACzB,WAAO,EAAE,GAAG,KAAK,SAAS,SAAS,CAAC,GAAG,KAAK,QAAQ,OAAO,EAAE;AAAA,EAC/D;AAAA,EAEA,IAAI,YAAqB;AACvB,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA,EAEA,IAAI,mBAA4B;AAC9B,WAAO,KAAK,QAAQ,iBAAiB;AAAA,EACvC;AAAA,EAEA,MAAM,QAAgC;AACpC,SAAK,YAAY;AACjB,SAAK,mBAAmB,CAAC;AACzB,SAAK,QAAQ,YAAY;AACzB,SAAK,OAAO,UAAU,KAAK,WAAW,CAAC;AACvC,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA,EAEA,MAAM,SAAiC;AACrC,QAAI,KAAK,QAAQ,eAAe;AAC9B,YAAM,iBAAiB,MAAM,KAAK,qBAAqB;AACvD,UAAI,eAAe,WAAW,SAAS;AACrC,eAAO;AAAA,MACT;AAAA,IACF;AACA,SAAK,QAAQ,YAAY;AACzB,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA,EAEA,MAAc,UAAkC;AAC9C,QAAI,oBAAoB;AACxB,QAAI;AAEJ,aAAS,OAAO,GAAG,OAAO,KAAK,UAAU,QAAQ,GAAG;AAClD,UAAI,KAAK,aAAa,CAAC,KAAK,QAAQ,WAAW;AAC7C,eAAO,EAAE,QAAQ,QAAQ,SAAS,UAAU;AAAA,MAC9C;AAEA,UAAI,KAAK,QAAQ,SAAS;AACxB,aAAK,QAAQ,YAAY;AACzB,eAAO,EAAE,QAAQ,QAAQ,SAAS,UAAU;AAAA,MAC9C;AAEA,YAAMC,UAAS,MAAM,KAAK,KAAK,SAAS;AACxC,WAAK,OAAO,SAASA,SAAQ,KAAK,WAAW,CAAC;AAE9C,UAAIA,QAAO,WAAW,SAAS;AAC7B,6BAAqB;AACrB,oBAAYA,QAAO;AACnB,aAAK,QAAQ,QAAQ,KAAK,UAAUA,QAAO,OAAO,EAAE;AAEpD,YAAI,qBAAqB,wBAAwB;AAC/C,eAAK,QAAQ,YAAY;AACzB,eAAK,OAAO,UAAU,IAAI,MAAMA,QAAO,OAAO,GAAG,KAAK,WAAW,CAAC;AAClE,eAAK,OAAO,SAASA,SAAQ,KAAK,WAAW,CAAC;AAC9C,iBAAOA;AAAA,QACT;AAEA,cAAM,KAAK,MAAM,KAAK,WAAW;AACjC;AAAA,MACF;AAEA,0BAAoB;AACpB,kBAAY;AACZ,WAAK,QAAQ,QAAQ,KAAKA,QAAO,OAAO;AAExC,UAAIA,QAAO,WAAW,kBAAkB;AACtC,aAAK,QAAQ,gBAAgBA,QAAO;AACpC,aAAK,QAAQ,YAAY;AACzB,YAAIA,QAAO,QAAQ;AACjB,eAAK,OAAO,qBAAqBA,QAAO,QAAQ,KAAK,WAAW,CAAC;AAAA,QACnE;AACA,eAAOA;AAAA,MACT;AAEA,UAAI,CAAC,QAAQ,SAAS,EAAE,SAASA,QAAO,MAAM,GAAG;AAC/C,aAAK,QAAQ,YAAY;AACzB,aAAK,OAAO,SAASA,SAAQ,KAAK,WAAW,CAAC;AAC9C,eAAOA;AAAA,MACT;AAEA,YAAM,KAAK,MAAM,KAAK,WAAW;AAAA,IACnC;AAEA,UAAM,SAAwB,EAAE,QAAQ,QAAQ,SAAS,oBAAoB;AAC7E,SAAK,QAAQ,QAAQ,KAAK,OAAO,OAAO;AACxC,SAAK,QAAQ,YAAY;AACzB,SAAK,OAAO,oBAAoB,KAAK,WAAW,CAAC;AACjD,SAAK,OAAO,SAAS,QAAQ,KAAK,WAAW,CAAC;AAC9C,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,uBAA+C;AACnD,QAAI,CAAC,KAAK,QAAQ,eAAe;AAC/B,aAAO,EAAE,QAAQ,SAAS,SAAS,+BAA+B;AAAA,IACpE;AAEA,QAAI;AACF,YAAM,UAAU,MAAM,cAAc,KAAK,QAAQ,aAAa;AAC9D,YAAM,SAAwB;AAAA,QAC5B,QAAQ;AAAA,QACR;AAAA,QACA,QAAQ,KAAK,QAAQ;AAAA,MACvB;AACA,WAAK,QAAQ,QAAQ,KAAK,OAAO;AACjC,WAAK,QAAQ,gBAAgB;AAC7B,WAAK,QAAQ,YAAY;AACzB,WAAK,OAAO,SAAS,QAAQ,KAAK,WAAW,CAAC;AAC9C,aAAO;AAAA,IACT,SAAS,OAAO;AACd,WAAK,QAAQ,YAAY;AACzB,WAAK,OAAO,UAAU,OAAO,KAAK,WAAW,CAAC;AAC9C,aAAO,EAAE,QAAQ,SAAS,SAAS,OAAO,KAAK,EAAE;AAAA,IACnD;AAAA,EACF;AAAA,EAEA,OAAa;AACX,SAAK,YAAY;AACjB,SAAK,QAAQ,YAAY;AAAA,EAC3B;AAAA,EAEA,MAAc,KAAK,WAA4C;AAC7D,QAAI;AACF,YAAM,WAAW,gBAAgB;AACjC,YAAM,gBAAgB,MAAM,eAAe,KAAK,QAAQ,SAAS;AAAA,QAC/D,MAAM,KAAK,QAAQ;AAAA,QACnB;AAAA,QACA,SAAS,KAAK,QAAQ;AAAA,QACtB;AAAA,QACA,QAAQ,KAAK,QAAQ;AAAA,MACvB,CAAC;AAGD,UAAI,cAAc,WAAW,QAAW;AACtC,aAAK,QAAQ,SAAS,cAAc;AAAA,MACtC;AAEA,aAAO,KAAK,cAAc,eAAe,SAAS,UAAU;AAAA,IAC9D,SAAS,OAAO;AACd,aAAO,EAAE,QAAQ,SAAS,SAAS,OAAO,KAAK,EAAE;AAAA,IACnD;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,eAA8B,YAAyD;AACjH,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,aAAa,cAAc,eAAe,UAAa,cAAc,WAAW,UAAa,cAAc,aAAa,SAC1H,EAAE,YAAY,cAAc,YAAY,QAAQ,cAAc,QAAQ,UAAU,cAAc,SAAS,IACvG;AAIJ,UAAM,MAAM,gBAAgB,MAAM;AAClC,QAAI,KAAK;AACP,WAAK,iBAAiB,KAAK,GAAG;AAC9B,UAAI,KAAK,iBAAiB,SAAS,aAAa;AAC9C,aAAK,iBAAiB,MAAM;AAAA,MAC9B;AACA,YAAM,cAAc,KAAK,iBAAiB,OAAO,CAAC,MAAM,MAAM,GAAG,EAAE;AACnE,UAAI,eAAe,gBAAgB;AACjC,eAAO;AAAA,UACL,QAAQ;AAAA,UACR;AAAA,UACA,SAAS;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,OAAO,WAAW,QAAQ,UAAU;AAC1C,QAAI,SAAS,WAAW;AACtB,aAAO,EAAE,QAAQ,WAAW,QAAQ,SAAS,mBAAmB,KAAK,UAAU,MAAM,CAAC,IAAI,WAAW;AAAA,IACvG;AAEA,QAAI,KAAK,QAAQ,SAAS,oBAAoB,SAAS,UAAU;AAC/D,aAAO,EAAE,QAAQ,kBAAkB,QAAQ,SAAS,uBAAuB,OAAO,IAAI,IAAI,WAAW;AAAA,IACvG;AAEA,QAAI,OAAO,SAAS,QAAQ;AAC1B,aAAO,EAAE,QAAQ,QAAQ,QAAQ,SAAS,OAAO,QAAQ,WAAW;AAAA,IACtE;AAEA,UAAM,UAAU,MAAM,cAAc,MAAM;AAC1C,WAAO,EAAE,QAAQ,YAAY,QAAQ,SAAS,WAAW;AAAA,EAC3D;AAAA,EAEA,MAAc,MAAM,IAA2B;AAC7C,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA,EACxD;AACF;AAEO,SAAS,mBAAmB,QAA4B,QAA2C;AACxG,SAAO,IAAI,aAAa,QAAQ,MAAM;AACxC;",
6
+ "names": ["input", "parsePlannerResult", "PARSE_FAILURE_PATTERN", "result"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "OmniBrowser Agent",
4
- "version": "0.2.2",
4
+ "version": "0.2.35",
5
5
  "description": "Local-first AI browser operator — heuristic and WebLLM planners.",
6
6
  "permissions": ["activeTab", "tabs", "scripting", "storage"],
7
7
  "host_permissions": ["http://*/*", "https://*/*"],
@@ -6,6 +6,7 @@ export declare class BrowserAgent {
6
6
  private events;
7
7
  private isStopped;
8
8
  private signal?;
9
+ private recentActionSigs;
9
10
  constructor(config: LibraryAgentConfig, events?: LibraryAgentEvents);
10
11
  getSession(): AgentSession;
11
12
  get isRunning(): boolean;
@@ -39,6 +39,8 @@ export interface CandidateElement {
39
39
  placeholder?: string;
40
40
  /** Associated <label> text resolved via for/id, aria-labelledby, aria-label, or wrapping <label> */
41
41
  label?: string;
42
+ /** True when the element is currently active/selected (has .active class, aria-selected, aria-current, or aria-pressed) */
43
+ active?: boolean;
42
44
  }
43
45
  export interface PageSnapshot {
44
46
  url: string;
@@ -1,4 +1,5 @@
1
1
  import type { AgentAction, PlannerResult } from "./contracts";
2
+ export declare const PARSE_FAILURE_PATTERN: RegExp;
2
3
  /**
3
4
  * Parse an AgentAction from raw LLM output.
4
5
  *
@@ -1,2 +1,2 @@
1
- import type { AgentAction, RiskLevel } from "./contracts";
2
- export declare function assessRisk(action: AgentAction): RiskLevel;
1
+ import type { AgentAction, CandidateElement, RiskLevel } from "./contracts";
2
+ export declare function assessRisk(action: AgentAction, candidates?: CandidateElement[]): RiskLevel;
package/icons/big.png ADDED
Binary file
package/icons/logo.png ADDED
Binary file
Binary file
package/notebook/.env ADDED
@@ -0,0 +1 @@
1
+ HF_TOKEN=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