@lasso-ai/cli 2.1.3 → 2.1.6

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/overlay.js CHANGED
@@ -9668,6 +9668,21 @@
9668
9668
  position: relative;
9669
9669
  }
9670
9670
 
9671
+ .lasso-model-chevron {
9672
+ flex: 0 0 16px;
9673
+ width: 16px;
9674
+ height: 16px;
9675
+ opacity: 0.72;
9676
+ transition: transform 160ms ease, opacity 160ms ease;
9677
+ }
9678
+
9679
+ .lasso-prompt-model[aria-expanded="true"] .lasso-model-chevron,
9680
+ .lasso-command-model-btn[aria-expanded="true"] .lasso-model-chevron,
9681
+ .lasso-cli-subgroup-header[aria-expanded="true"] .lasso-model-chevron {
9682
+ transform: rotate(180deg);
9683
+ opacity: 1;
9684
+ }
9685
+
9671
9686
  .lasso-prompt-model {
9672
9687
  height: 28px;
9673
9688
  max-width: 190px;
@@ -9786,7 +9801,7 @@
9786
9801
  cursor: pointer;
9787
9802
  }
9788
9803
  .lasso-cli-subgroup-header:hover { background: var(--lo-surface-hover); color: var(--lo-text); }
9789
- .lasso-cli-subgroup-chevron { color: var(--lo-text-3); font-size: 13px; line-height: 10px; }
9804
+ .lasso-cli-subgroup-chevron { color: var(--lo-text-3); }
9790
9805
  .lasso-cli-subgroup-items { padding-left: 6px; }
9791
9806
 
9792
9807
  .lasso-model-item-icon {
@@ -19563,130 +19578,6 @@
19563
19578
  return escapeHtml3(value2);
19564
19579
  }
19565
19580
 
19566
- // src/overlay/browser/context.ts
19567
- var import_html2canvas = __toESM(require_html2canvas());
19568
- async function capturePageContext() {
19569
- const url2 = window.location.href;
19570
- const route = window.location.pathname + window.location.search;
19571
- const title = document.title || "Untitled Page";
19572
- const doc = document.documentElement;
19573
- const viewport = {
19574
- width: window.innerWidth,
19575
- height: window.innerHeight,
19576
- scrollX: window.scrollX || window.pageXOffset || 0,
19577
- scrollY: window.scrollY || window.pageYOffset || 0,
19578
- scrollHeight: doc.scrollHeight
19579
- };
19580
- const headings = [];
19581
- const headingEls = document.querySelectorAll("h1, h2, h3, h4");
19582
- for (const el of headingEls) {
19583
- if (el.closest("#lasso-root")) continue;
19584
- const text = (el.textContent || "").trim().replace(/\s+/g, " ");
19585
- if (text) {
19586
- const level = parseInt(el.tagName.replace(/h/i, ""), 10) || 2;
19587
- headings.push({ level, text: text.slice(0, 120) });
19588
- if (headings.length >= 15) break;
19589
- }
19590
- }
19591
- const sections = [];
19592
- const sectionEls = document.querySelectorAll(
19593
- "header, nav, main, section, article, footer, [role='banner'], [role='main'], [role='navigation'], [id*='hero'], [class*='hero'], [id*='feature'], [class*='feature'], [id*='pricing'], [class*='pricing'], [id*='testimonial'], [class*='testimonial']"
19594
- );
19595
- for (const el of sectionEls) {
19596
- if (el.closest("#lasso-root")) continue;
19597
- const tag = el.tagName.toLowerCase();
19598
- const id = el.id ? `#${el.id}` : "";
19599
- const ariaLabel = el.getAttribute("aria-label");
19600
- const name = ariaLabel ? `${tag} (${ariaLabel})` : id ? `${tag}${id}` : tag;
19601
- const textPreview = (el.textContent || "").trim().replace(/\s+/g, " ").slice(0, 160);
19602
- if (textPreview && !sections.some((s) => s.textPreview === textPreview)) {
19603
- sections.push({ name, textPreview });
19604
- if (sections.length >= 12) break;
19605
- }
19606
- }
19607
- const buttons = [];
19608
- const buttonEls = document.querySelectorAll("button, a[role='button'], input[type='button'], input[type='submit'], a.btn, a[class*='button']");
19609
- for (const el of buttonEls) {
19610
- if (el.closest("#lasso-root")) continue;
19611
- const text = (el.textContent || el.value || "").trim().replace(/\s+/g, " ");
19612
- if (text && !buttons.includes(text)) {
19613
- buttons.push(text.slice(0, 50));
19614
- if (buttons.length >= 12) break;
19615
- }
19616
- }
19617
- const links = [];
19618
- const linkEls = document.querySelectorAll("nav a, header a, a.nav-link");
19619
- for (const el of linkEls) {
19620
- if (el.closest("#lasso-root")) continue;
19621
- const text = (el.textContent || "").trim().replace(/\s+/g, " ");
19622
- if (text && !links.includes(text) && !buttons.includes(text)) {
19623
- links.push(text.slice(0, 40));
19624
- if (links.length >= 10) break;
19625
- }
19626
- }
19627
- const inputs = [];
19628
- const inputEls = document.querySelectorAll("input:not([type='hidden']), textarea, select");
19629
- for (const el of inputEls) {
19630
- if (el.closest("#lasso-root")) continue;
19631
- const type = (el.getAttribute("type") || el.tagName.toLowerCase()).slice(0, 20);
19632
- const placeholder = el.getAttribute("placeholder")?.slice(0, 60);
19633
- inputs.push({ type, placeholder });
19634
- if (inputs.length >= 8) break;
19635
- }
19636
- const bodyClone = document.body ? document.body.cloneNode(true) : null;
19637
- if (bodyClone) {
19638
- const lassoRoot = bodyClone.querySelector("#lasso-root");
19639
- if (lassoRoot) lassoRoot.remove();
19640
- }
19641
- const bodyText = (bodyClone?.innerText || document.body?.innerText || "").replace(/\s+/g, " ").slice(0, 1500);
19642
- let selectedElement = void 0;
19643
- if (state.selected && !state.selected.closest("#lasso-root")) {
19644
- const el = state.selected;
19645
- selectedElement = {
19646
- tag: el.tagName.toLowerCase(),
19647
- id: el.id || void 0,
19648
- classes: Array.from(el.classList),
19649
- text: (el.textContent || "").trim().slice(0, 100),
19650
- source: el.getAttribute("data-source") || el.getAttribute("data-lasso-source") || void 0
19651
- };
19652
- }
19653
- let screenshot = void 0;
19654
- try {
19655
- const options = {
19656
- backgroundColor: null,
19657
- useCORS: true,
19658
- logging: false,
19659
- scale: 0.6,
19660
- ignoreElements: (node) => node.id === "lasso-root" || Boolean(node.closest?.("#lasso-root"))
19661
- };
19662
- const canvasPromise = (0, import_html2canvas.default)(document.body, options);
19663
- const timeoutPromise = new Promise((r) => setTimeout(() => r(null), 1200));
19664
- const canvas = await Promise.race([canvasPromise, timeoutPromise]);
19665
- if (canvas && typeof canvas.toDataURL === "function") {
19666
- screenshot = canvas.toDataURL("image/jpeg", 0.65);
19667
- }
19668
- } catch (err) {
19669
- console.warn("[lasso] visual snapshot failed:", err);
19670
- }
19671
- return {
19672
- url: url2,
19673
- route,
19674
- title,
19675
- viewport,
19676
- screenshot,
19677
- runtimeErrors: [...state.runtimeErrors],
19678
- domSummary: {
19679
- headings,
19680
- sections,
19681
- buttons,
19682
- links,
19683
- inputs,
19684
- visibleTextSnippet: bodyText
19685
- },
19686
- selectedElement
19687
- };
19688
- }
19689
-
19690
19581
  // src/overlay/commandbar/commandbar.ts
19691
19582
  var commandBarEl = null;
19692
19583
  var commandInput = null;
@@ -20076,10 +19967,10 @@
20076
19967
  ></textarea>
20077
19968
  <div class="lasso-command-input-toolbar">
20078
19969
  <div class="lasso-command-model-select">
20079
- <button class="lasso-command-model-btn" type="button" aria-label="Select model">
19970
+ <button class="lasso-command-model-btn" type="button" aria-label="Select model" aria-expanded="false" aria-haspopup="listbox">
20080
19971
  <span class="lasso-command-model-icon"></span>
20081
19972
  <span class="lasso-command-model-label"></span>
20082
- <svg class="lasso-command-model-chevron" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
19973
+ <svg class="lasso-model-chevron" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
20083
19974
  <path d="M6 9l6 6 6-6"/>
20084
19975
  </svg>
20085
19976
  </button>
@@ -20152,7 +20043,7 @@
20152
20043
  if (!commandHistoryPopover) return;
20153
20044
  const isHidden = commandHistoryPopover.hidden;
20154
20045
  if (isHidden) {
20155
- if (commandModelMenu) commandModelMenu.hidden = true;
20046
+ setCommandModelMenuOpen(false);
20156
20047
  commandHistoryPopover.hidden = false;
20157
20048
  commandHistoryBtn?.classList.add("active");
20158
20049
  await loadConversationList();
@@ -20168,6 +20059,11 @@
20168
20059
  if (!item?.dataset.convId) return;
20169
20060
  await switchConversation(item.dataset.convId);
20170
20061
  });
20062
+ const setCommandModelMenuOpen = (open) => {
20063
+ if (!commandModelMenu || !commandModelBtn) return;
20064
+ commandModelMenu.hidden = !open;
20065
+ commandModelBtn.setAttribute("aria-expanded", String(open));
20066
+ };
20171
20067
  commandModelBtn.addEventListener("click", (event) => {
20172
20068
  event.preventDefault();
20173
20069
  event.stopPropagation();
@@ -20176,11 +20072,11 @@
20176
20072
  commandHistoryBtn?.classList.remove("active");
20177
20073
  }
20178
20074
  if (commandModelMenu) {
20179
- const willOpen = commandModelMenu.hidden;
20075
+ const willOpen = commandModelMenu?.hidden === true;
20180
20076
  if (willOpen) {
20181
20077
  populateModelMenu();
20182
20078
  }
20183
- commandModelMenu.hidden = !willOpen;
20079
+ setCommandModelMenuOpen(willOpen);
20184
20080
  }
20185
20081
  });
20186
20082
  commandModelMenu.addEventListener("click", (event) => {
@@ -20202,7 +20098,7 @@
20202
20098
  commandModelLabel.textContent = found.label;
20203
20099
  }
20204
20100
  populateModelMenu();
20205
- if (commandModelMenu) commandModelMenu.hidden = true;
20101
+ setCommandModelMenuOpen(false);
20206
20102
  });
20207
20103
  document.addEventListener("click", (event) => {
20208
20104
  const path = event.composedPath ? event.composedPath() : [];
@@ -20210,7 +20106,7 @@
20210
20106
  if (commandModelMenu && !commandModelMenu.hidden) {
20211
20107
  if (!path.includes(commandModelBtn) && !path.includes(commandModelMenu)) {
20212
20108
  if (!commandModelBtn?.contains(target) && !commandModelMenu?.contains(target)) {
20213
- commandModelMenu.hidden = true;
20109
+ setCommandModelMenuOpen(false);
20214
20110
  }
20215
20111
  }
20216
20112
  }
@@ -20223,6 +20119,11 @@
20223
20119
  }
20224
20120
  }
20225
20121
  });
20122
+ document.addEventListener("keydown", (event) => {
20123
+ if (event.key !== "Escape" || commandModelMenu?.hidden !== false) return;
20124
+ setCommandModelMenuOpen(false);
20125
+ commandModelBtn?.focus();
20126
+ });
20226
20127
  commandClose.addEventListener("click", (event) => {
20227
20128
  event.preventDefault();
20228
20129
  event.stopPropagation();
@@ -20391,6 +20292,10 @@
20391
20292
  function closeCommandBar() {
20392
20293
  if (!commandBarEl) return;
20393
20294
  commandBarEl.hidden = true;
20295
+ if (commandModelMenu) {
20296
+ commandModelMenu.hidden = true;
20297
+ commandModelBtn?.setAttribute("aria-expanded", "false");
20298
+ }
20394
20299
  isOneShotMode = false;
20395
20300
  getDOM().shadow.querySelector(".command-tool")?.classList.remove("active");
20396
20301
  }
@@ -20511,13 +20416,14 @@
20511
20416
  activeTaskId = taskId2;
20512
20417
  activeStartTime = Date.now();
20513
20418
  const isChat = isConversationalPrompt(prompt);
20419
+ const initialStepTitle = isChat ? "Preparing response\u2026" : "Preparing task\u2026";
20514
20420
  commandMessages.push({
20515
20421
  id: taskId2,
20516
20422
  role: "assistant",
20517
20423
  content: "",
20518
20424
  timestamp: Date.now(),
20519
20425
  thinking: {
20520
- steps: [{ title: isChat ? "Thinking..." : "Planning your request...", status: "running" }],
20426
+ steps: [{ title: initialStepTitle, status: "running" }],
20521
20427
  durationSeconds: 0,
20522
20428
  completed: false,
20523
20429
  expanded: true
@@ -20548,12 +20454,6 @@
20548
20454
  summary: prompt.slice(0, 120)
20549
20455
  });
20550
20456
  }
20551
- let pageContext = void 0;
20552
- try {
20553
- pageContext = await capturePageContext();
20554
- } catch (err) {
20555
- console.warn("[lasso] capturePageContext failed:", err);
20556
- }
20557
20457
  if (state.bridgeSocket?.readyState === WebSocket.OPEN) {
20558
20458
  state.bridgeSocket.send(
20559
20459
  JSON.stringify({
@@ -20564,8 +20464,13 @@
20564
20464
  scope: "project",
20565
20465
  model: state.selectedModel.id,
20566
20466
  provider: state.selectedModel.provider,
20567
- messages: commandMessages.filter((m) => m.content && !m.error).map((m) => ({ role: m.role, content: m.content })),
20568
- pageContext
20467
+ // The current prompt is sent in its dedicated `prompt` field. Keep it
20468
+ // out of history so the model sees it once, not once as history and
20469
+ // once as the active request.
20470
+ messages: commandMessages.map((message, index) => ({ message, index })).filter(({ message, index }) => Boolean(message.content) && !message.error && index !== commandMessages.reduce(
20471
+ (last, item, itemIndex) => item.role === "user" && item.content === prompt ? itemIndex : last,
20472
+ -1
20473
+ )).map(({ message }) => message).map((m) => ({ role: m.role, content: m.content }))
20569
20474
  })
20570
20475
  );
20571
20476
  } else {
@@ -20611,14 +20516,17 @@
20611
20516
  expanded: true
20612
20517
  };
20613
20518
  }
20614
- for (const step of targetMsg.thinking.steps) {
20615
- if (step.status === "running") step.status = "completed";
20519
+ const stepTitle = (message.message || "").trim();
20520
+ if (stepTitle && stepTitle !== "Thinking\u2026" && stepTitle !== "Thinking") {
20521
+ for (const step of targetMsg.thinking.steps) {
20522
+ if (step.status === "running") step.status = "completed";
20523
+ }
20524
+ targetMsg.thinking.steps.push({
20525
+ title: stepTitle,
20526
+ detail: message.detail,
20527
+ status: "running"
20528
+ });
20616
20529
  }
20617
- targetMsg.thinking.steps.push({
20618
- title: message.message,
20619
- detail: message.detail,
20620
- status: "running"
20621
- });
20622
20530
  renderMessages();
20623
20531
  scrollToBottom();
20624
20532
  } else if (message.status === "review" || message.status === "complete") {
@@ -21583,6 +21491,130 @@
21583
21491
  }
21584
21492
  }
21585
21493
 
21494
+ // src/overlay/browser/context.ts
21495
+ var import_html2canvas = __toESM(require_html2canvas());
21496
+ async function capturePageContext() {
21497
+ const url2 = window.location.href;
21498
+ const route = window.location.pathname + window.location.search;
21499
+ const title = document.title || "Untitled Page";
21500
+ const doc = document.documentElement;
21501
+ const viewport = {
21502
+ width: window.innerWidth,
21503
+ height: window.innerHeight,
21504
+ scrollX: window.scrollX || window.pageXOffset || 0,
21505
+ scrollY: window.scrollY || window.pageYOffset || 0,
21506
+ scrollHeight: doc.scrollHeight
21507
+ };
21508
+ const headings = [];
21509
+ const headingEls = document.querySelectorAll("h1, h2, h3, h4");
21510
+ for (const el of headingEls) {
21511
+ if (el.closest("#lasso-root")) continue;
21512
+ const text = (el.textContent || "").trim().replace(/\s+/g, " ");
21513
+ if (text) {
21514
+ const level = parseInt(el.tagName.replace(/h/i, ""), 10) || 2;
21515
+ headings.push({ level, text: text.slice(0, 120) });
21516
+ if (headings.length >= 15) break;
21517
+ }
21518
+ }
21519
+ const sections = [];
21520
+ const sectionEls = document.querySelectorAll(
21521
+ "header, nav, main, section, article, footer, [role='banner'], [role='main'], [role='navigation'], [id*='hero'], [class*='hero'], [id*='feature'], [class*='feature'], [id*='pricing'], [class*='pricing'], [id*='testimonial'], [class*='testimonial']"
21522
+ );
21523
+ for (const el of sectionEls) {
21524
+ if (el.closest("#lasso-root")) continue;
21525
+ const tag = el.tagName.toLowerCase();
21526
+ const id = el.id ? `#${el.id}` : "";
21527
+ const ariaLabel = el.getAttribute("aria-label");
21528
+ const name = ariaLabel ? `${tag} (${ariaLabel})` : id ? `${tag}${id}` : tag;
21529
+ const textPreview = (el.textContent || "").trim().replace(/\s+/g, " ").slice(0, 160);
21530
+ if (textPreview && !sections.some((s) => s.textPreview === textPreview)) {
21531
+ sections.push({ name, textPreview });
21532
+ if (sections.length >= 12) break;
21533
+ }
21534
+ }
21535
+ const buttons = [];
21536
+ const buttonEls = document.querySelectorAll("button, a[role='button'], input[type='button'], input[type='submit'], a.btn, a[class*='button']");
21537
+ for (const el of buttonEls) {
21538
+ if (el.closest("#lasso-root")) continue;
21539
+ const text = (el.textContent || el.value || "").trim().replace(/\s+/g, " ");
21540
+ if (text && !buttons.includes(text)) {
21541
+ buttons.push(text.slice(0, 50));
21542
+ if (buttons.length >= 12) break;
21543
+ }
21544
+ }
21545
+ const links = [];
21546
+ const linkEls = document.querySelectorAll("nav a, header a, a.nav-link");
21547
+ for (const el of linkEls) {
21548
+ if (el.closest("#lasso-root")) continue;
21549
+ const text = (el.textContent || "").trim().replace(/\s+/g, " ");
21550
+ if (text && !links.includes(text) && !buttons.includes(text)) {
21551
+ links.push(text.slice(0, 40));
21552
+ if (links.length >= 10) break;
21553
+ }
21554
+ }
21555
+ const inputs = [];
21556
+ const inputEls = document.querySelectorAll("input:not([type='hidden']), textarea, select");
21557
+ for (const el of inputEls) {
21558
+ if (el.closest("#lasso-root")) continue;
21559
+ const type = (el.getAttribute("type") || el.tagName.toLowerCase()).slice(0, 20);
21560
+ const placeholder = el.getAttribute("placeholder")?.slice(0, 60);
21561
+ inputs.push({ type, placeholder });
21562
+ if (inputs.length >= 8) break;
21563
+ }
21564
+ const bodyClone = document.body ? document.body.cloneNode(true) : null;
21565
+ if (bodyClone) {
21566
+ const lassoRoot = bodyClone.querySelector("#lasso-root");
21567
+ if (lassoRoot) lassoRoot.remove();
21568
+ }
21569
+ const bodyText = (bodyClone?.innerText || document.body?.innerText || "").replace(/\s+/g, " ").slice(0, 1500);
21570
+ let selectedElement = void 0;
21571
+ if (state.selected && !state.selected.closest("#lasso-root")) {
21572
+ const el = state.selected;
21573
+ selectedElement = {
21574
+ tag: el.tagName.toLowerCase(),
21575
+ id: el.id || void 0,
21576
+ classes: Array.from(el.classList),
21577
+ text: (el.textContent || "").trim().slice(0, 100),
21578
+ source: el.getAttribute("data-source") || el.getAttribute("data-lasso-source") || void 0
21579
+ };
21580
+ }
21581
+ let screenshot = void 0;
21582
+ try {
21583
+ const options = {
21584
+ backgroundColor: null,
21585
+ useCORS: true,
21586
+ logging: false,
21587
+ scale: 0.6,
21588
+ ignoreElements: (node) => node.id === "lasso-root" || Boolean(node.closest?.("#lasso-root"))
21589
+ };
21590
+ const canvasPromise = (0, import_html2canvas.default)(document.body, options);
21591
+ const timeoutPromise = new Promise((r) => setTimeout(() => r(null), 1200));
21592
+ const canvas = await Promise.race([canvasPromise, timeoutPromise]);
21593
+ if (canvas && typeof canvas.toDataURL === "function") {
21594
+ screenshot = canvas.toDataURL("image/jpeg", 0.65);
21595
+ }
21596
+ } catch (err) {
21597
+ console.warn("[lasso] visual snapshot failed:", err);
21598
+ }
21599
+ return {
21600
+ url: url2,
21601
+ route,
21602
+ title,
21603
+ viewport,
21604
+ screenshot,
21605
+ runtimeErrors: [...state.runtimeErrors],
21606
+ domSummary: {
21607
+ headings,
21608
+ sections,
21609
+ buttons,
21610
+ links,
21611
+ inputs,
21612
+ visibleTextSnippet: bodyText
21613
+ },
21614
+ selectedElement
21615
+ };
21616
+ }
21617
+
21586
21618
  // src/overlay/bridge/bridge.ts
21587
21619
  function reportRuntimeError(details, notifyBridge = true) {
21588
21620
  const clean = details.slice(0, 1200);
@@ -21647,6 +21679,10 @@
21647
21679
  state.bridgeSocket.addEventListener("message", (event) => {
21648
21680
  try {
21649
21681
  const message = JSON.parse(event.data);
21682
+ if (message.type === "page_context_request" && message.requestId) {
21683
+ void capturePageContext().then((pageContext) => send({ type: "page_context_response", requestId: message.requestId, pageContext })).catch((error) => send({ type: "page_context_response", requestId: message.requestId, error: error instanceof Error ? error.message : "Unable to capture page context." }));
21684
+ return;
21685
+ }
21650
21686
  if (message.type === "config") {
21651
21687
  state.apiKeyConfigured = Boolean(message.apiKeyConfigured);
21652
21688
  if (message.collab) {
@@ -22211,9 +22247,9 @@ The agent is automatically attempting to fix the build errors...`;
22211
22247
  </button>
22212
22248
 
22213
22249
  <div class="lasso-prompt-model-wrap">
22214
- <button class="lasso-prompt-model" type="button" aria-label="Choose model">
22250
+ <button class="lasso-prompt-model" type="button" aria-label="Choose model" aria-expanded="false" aria-haspopup="listbox">
22215
22251
  <span class="lasso-prompt-model-name"></span>
22216
- <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
22252
+ <svg class="lasso-model-chevron" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
22217
22253
  <path d="M6 9l6 6 6-6"/>
22218
22254
  </svg>
22219
22255
  </button>
@@ -22381,10 +22417,15 @@ The agent is automatically attempting to fix the build errors...`;
22381
22417
  };
22382
22418
  promptTop.addEventListener("pointerup", stopPromptDrag);
22383
22419
  promptTop.addEventListener("pointercancel", stopPromptDrag);
22420
+ const setModelMenuOpen = (open) => {
22421
+ if (!modelMenu || !modelBtn) return;
22422
+ modelMenu.hidden = !open;
22423
+ modelBtn.setAttribute("aria-expanded", String(open));
22424
+ };
22384
22425
  modelBtn.addEventListener("click", (event) => {
22385
22426
  event.preventDefault();
22386
22427
  event.stopPropagation();
22387
- if (modelMenu) modelMenu.hidden = !modelMenu.hidden;
22428
+ setModelMenuOpen(modelMenu?.hidden === true);
22388
22429
  });
22389
22430
  modelMenu.addEventListener("click", (event) => {
22390
22431
  event.stopPropagation();
@@ -22401,7 +22442,7 @@ The agent is automatically attempting to fix the build errors...`;
22401
22442
  state.selectedModel = found;
22402
22443
  rememberModel(found);
22403
22444
  syncModelMenu2();
22404
- if (modelMenu) modelMenu.hidden = true;
22445
+ setModelMenuOpen(false);
22405
22446
  });
22406
22447
  document.addEventListener("click", (event) => {
22407
22448
  if (!modelMenu || modelMenu.hidden) return;
@@ -22409,7 +22450,12 @@ The agent is automatically attempting to fix the build errors...`;
22409
22450
  if (path.includes(modelBtn) || path.includes(modelMenu)) return;
22410
22451
  const target = event.target;
22411
22452
  if (modelBtn?.contains(target) || modelMenu?.contains(target)) return;
22412
- modelMenu.hidden = true;
22453
+ setModelMenuOpen(false);
22454
+ });
22455
+ document.addEventListener("keydown", (event) => {
22456
+ if (event.key !== "Escape" || modelMenu?.hidden !== false) return;
22457
+ setModelMenuOpen(false);
22458
+ modelBtn?.focus();
22413
22459
  });
22414
22460
  refreshModelMenu();
22415
22461
  promptInput.addEventListener("input", syncSendButtonState);
@@ -22677,7 +22723,7 @@ The agent is automatically attempting to fix the build errors...`;
22677
22723
  subHeader.type = "button";
22678
22724
  subHeader.className = "lasso-cli-subgroup-header";
22679
22725
  subHeader.setAttribute("aria-expanded", String(openCliGroups.has(cliKey)));
22680
- subHeader.innerHTML = `<span>${providerLabels[cliKey] || cliKey}</span><span class="lasso-cli-subgroup-chevron">${openCliGroups.has(cliKey) ? "\u2303" : "\u2304"}</span>`;
22726
+ subHeader.innerHTML = `<span>${providerLabels[cliKey] || cliKey}</span><svg class="lasso-model-chevron lasso-cli-subgroup-chevron" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>`;
22681
22727
  const subItems = document.createElement("div");
22682
22728
  subItems.className = "lasso-cli-subgroup-items";
22683
22729
  subItems.hidden = !openCliGroups.has(cliKey);
@@ -23156,11 +23202,6 @@ The agent is automatically attempting to fix the build errors...`;
23156
23202
  promptInput.style.height = "auto";
23157
23203
  syncSendButtonState();
23158
23204
  requestAgentNotificationPermission();
23159
- const isQuestionPrefix = /^(what|why|how|when|where|who|which|explain|describe|tell me about)\b/i.test(instruction);
23160
- const isImperativeEdit = /\b(can|could|would|please)\s+(you\s+)?(change|edit|update|add|fix|make|replace|remove|install|redesign|switch|swap|set)\b/i.test(instruction);
23161
- const isConversationalQuestion = (isQuestionPrefix || /\?$/.test(instruction)) && !isImperativeEdit;
23162
- const isGreeting = /^(hi|hello|hey|thanks|thank you)\b/i.test(instruction) && instruction.split(/\s+/).length <= 4;
23163
- const wantsAnswer = isConversationalQuestion || isGreeting;
23164
23205
  const bridgeSocket = state.bridgeSocket;
23165
23206
  if (!bridgeSocket || bridgeSocket.readyState !== WebSocket.OPEN) {
23166
23207
  setAgentStatus(
@@ -23169,11 +23210,9 @@ The agent is automatically attempting to fix the build errors...`;
23169
23210
  );
23170
23211
  return;
23171
23212
  }
23172
- if (!wantsAnswer) {
23173
- const ownershipGranted = await acquireOwnership(selectedElement);
23174
- if (!ownershipGranted) return;
23175
- }
23176
- if (!wantsAnswer && state.collabSocket?.connected && state.collabJoined) {
23213
+ const ownershipGranted = await acquireOwnership(selectedElement);
23214
+ if (!ownershipGranted) return;
23215
+ if (state.collabSocket?.connected && state.collabJoined) {
23177
23216
  collabEmit("collab:action", {
23178
23217
  sessionId: state.collabProjectId,
23179
23218
  status: "preparing",
@@ -23221,14 +23260,13 @@ The agent is automatically attempting to fix the build errors...`;
23221
23260
  const message = "The Lasso agent bridge disconnected before the task could start.";
23222
23261
  updateAgentTask(task.id, { status: "error", message });
23223
23262
  appendChat("error", message, task.id);
23224
- if (!wantsAnswer) releaseHeldLock(elementKey(selectedElement));
23263
+ releaseHeldLock(elementKey(selectedElement));
23225
23264
  return;
23226
23265
  }
23227
23266
  bridgeSocket.send(
23228
23267
  JSON.stringify({
23229
- type: wantsAnswer ? "ask" : "edit",
23268
+ type: "edit",
23230
23269
  taskId: task.id,
23231
- question: wantsAnswer ? instruction : void 0,
23232
23270
  instruction,
23233
23271
  messages,
23234
23272
  changesHistory,
@@ -19,7 +19,8 @@ export interface ActionExecutionResult {
19
19
  }
20
20
  export declare class ActionExecutor {
21
21
  private projectPath;
22
- constructor(projectPath: string);
22
+ private browserContextProvider?;
23
+ constructor(projectPath: string, browserContextProvider?: (action: any) => Promise<any>);
23
24
  /**
24
25
  * Execute a single action
25
26
  */
@@ -11,8 +11,10 @@ const node_util_1 = require("node:util");
11
11
  const exec = (0, node_util_1.promisify)(node_child_process_1.exec);
12
12
  class ActionExecutor {
13
13
  projectPath;
14
- constructor(projectPath) {
14
+ browserContextProvider;
15
+ constructor(projectPath, browserContextProvider) {
15
16
  this.projectPath = projectPath;
17
+ this.browserContextProvider = browserContextProvider;
16
18
  }
17
19
  /**
18
20
  * Execute a single action
@@ -340,15 +342,22 @@ class ActionExecutor {
340
342
  const { id, data } = action;
341
343
  const url = data.url;
342
344
  const check = data.check;
343
- // TODO: Implement actual browser checking via Puppeteer or similar
345
+ if (!this.browserContextProvider) {
346
+ return {
347
+ success: false,
348
+ actionId: id,
349
+ error: "Live browser context is unavailable because no overlay is connected.",
350
+ };
351
+ }
352
+ const pageContext = await this.browserContextProvider(action);
344
353
  return {
345
354
  success: true,
346
355
  actionId: id,
347
- output: { url, check, result: "pending" },
356
+ output: { url, check, pageContext },
348
357
  observation: {
349
358
  type: "browser",
350
- summary: `Browser check pending: ${url}`,
351
- data: { url, check },
359
+ summary: `Captured live browser context${url ? ` for ${url}` : ""}`,
360
+ data: { url, check, pageContext },
352
361
  },
353
362
  };
354
363
  }
@@ -36,6 +36,7 @@ export declare class CodexCLIAdapter extends LocalAgentAdapter {
36
36
  protected handleLine(line: string, context: TaskContext, sink: EventSink): boolean;
37
37
  private handleItemCompleted;
38
38
  private recordFailure;
39
+ private describeError;
39
40
  protected inspectFailureText(text: string): {
40
41
  message: string;
41
42
  authFailure?: boolean;