@lasso-ai/cli 2.1.13 → 2.1.15

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
@@ -7815,16 +7815,7 @@
7815
7815
  });
7816
7816
 
7817
7817
  // src/overlay/state.ts
7818
- var COLLAB_COLORS = [
7819
- "#6ea0ff",
7820
- "#81c995",
7821
- "#fdd663",
7822
- "#f28b82",
7823
- "#c084fc",
7824
- "#22d3ee",
7825
- "#fb923c",
7826
- "#a78bfa"
7827
- ];
7818
+ var COLLAB_COLORS = ["#6ea0ff", "#81c995", "#fdd663", "#f28b82", "#c084fc", "#22d3ee", "#fb923c", "#a78bfa"];
7828
7819
  function userNameColor(userId) {
7829
7820
  if (!userId) return COLLAB_COLORS[0];
7830
7821
  let hash = 0;
@@ -13593,16 +13584,7 @@
13593
13584
  this.commentPins.className = "lasso-comment-pins";
13594
13585
  this.dragLayer = document.createElement("div");
13595
13586
  this.dragLayer.className = "lasso-drag-layer";
13596
- this.shadow.append(
13597
- this.remoteLayer,
13598
- this.spotlightBox,
13599
- this.hoverBox,
13600
- this.selectedBox,
13601
- this.label,
13602
- this.activityStrip,
13603
- this.commentPins,
13604
- this.dragLayer
13605
- );
13587
+ this.shadow.append(this.remoteLayer, this.spotlightBox, this.hoverBox, this.selectedBox, this.label, this.activityStrip, this.commentPins, this.dragLayer);
13606
13588
  }
13607
13589
  isLassoElement(el) {
13608
13590
  if (!el) return false;
@@ -17133,9 +17115,7 @@
17133
17115
  if (!avatarsEl || !countEl) return;
17134
17116
  avatarsEl.innerHTML = "";
17135
17117
  const offline = Boolean(state.collabProjectId && !state.collabJoined);
17136
- const others = [...state.presenceUsers.values()].filter(
17137
- (user) => user.socketId !== state.collabSocket?.id
17138
- );
17118
+ const others = [...state.presenceUsers.values()].filter((user) => user.socketId !== state.collabSocket?.id);
17139
17119
  for (const user of others.slice(0, 6)) {
17140
17120
  const av = document.createElement("button");
17141
17121
  av.type = "button";
@@ -17322,13 +17302,7 @@
17322
17302
  }
17323
17303
  function getSupportedMimeType() {
17324
17304
  if (typeof MediaRecorder === "undefined") return "audio/webm";
17325
- const types = [
17326
- "audio/webm;codecs=opus",
17327
- "audio/webm",
17328
- "audio/mp4",
17329
- "audio/ogg;codecs=opus",
17330
- "audio/wav"
17331
- ];
17305
+ const types = ["audio/webm;codecs=opus", "audio/webm", "audio/mp4", "audio/ogg;codecs=opus", "audio/wav"];
17332
17306
  for (const t of types) {
17333
17307
  if (MediaRecorder.isTypeSupported(t)) return t;
17334
17308
  }
@@ -17350,53 +17324,48 @@
17350
17324
  const base64 = await blobToBase64(blob);
17351
17325
  if (state.bridgeSocket?.readyState === WebSocket.OPEN) {
17352
17326
  try {
17353
- const result = await new Promise(
17354
- (resolve, reject) => {
17355
- const requestId = `transcribe_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
17356
- const timeout = window.setTimeout(() => {
17357
- cleanup();
17358
- reject(new Error("Transcription timed out via local bridge"));
17359
- }, 25e3);
17360
- const handler = (event) => {
17361
- try {
17362
- const msg = JSON.parse(event.data);
17363
- if (msg.type === "transcribe_result" && msg.requestId === requestId) {
17364
- cleanup();
17365
- if (msg.success) {
17366
- resolve({ text: msg.text || "", provider: msg.provider || "gradium" });
17367
- } else {
17368
- reject(new Error(msg.error || "Transcription failed"));
17369
- }
17327
+ const result = await new Promise((resolve, reject) => {
17328
+ const requestId = `transcribe_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
17329
+ const timeout = window.setTimeout(() => {
17330
+ cleanup();
17331
+ reject(new Error("Transcription timed out via local bridge"));
17332
+ }, 25e3);
17333
+ const handler = (event) => {
17334
+ try {
17335
+ const msg = JSON.parse(event.data);
17336
+ if (msg.type === "transcribe_result" && msg.requestId === requestId) {
17337
+ cleanup();
17338
+ if (msg.success) {
17339
+ resolve({ text: msg.text || "", provider: msg.provider || "gradium" });
17340
+ } else {
17341
+ reject(new Error(msg.error || "Transcription failed"));
17370
17342
  }
17371
- } catch {
17372
17343
  }
17373
- };
17374
- const cleanup = () => {
17375
- window.clearTimeout(timeout);
17376
- state.bridgeSocket?.removeEventListener("message", handler);
17377
- };
17378
- state.bridgeSocket?.addEventListener("message", handler);
17379
- state.bridgeSocket?.send(
17380
- JSON.stringify({
17381
- type: "transcribe",
17382
- requestId,
17383
- audio: base64,
17384
- mimeType,
17385
- language: "en"
17386
- })
17387
- );
17388
- }
17389
- );
17344
+ } catch {
17345
+ }
17346
+ };
17347
+ const cleanup = () => {
17348
+ window.clearTimeout(timeout);
17349
+ state.bridgeSocket?.removeEventListener("message", handler);
17350
+ };
17351
+ state.bridgeSocket?.addEventListener("message", handler);
17352
+ state.bridgeSocket?.send(
17353
+ JSON.stringify({
17354
+ type: "transcribe",
17355
+ requestId,
17356
+ audio: base64,
17357
+ mimeType,
17358
+ language: "en"
17359
+ })
17360
+ );
17361
+ });
17390
17362
  return result;
17391
17363
  } catch (bridgeErr) {
17392
17364
  console.warn("[transcribe] Bridge transcription failed, falling back to direct server fetch:", bridgeErr);
17393
17365
  }
17394
17366
  }
17395
17367
  const isLassoPage = typeof window !== "undefined" && window.location.hostname.endsWith(".lasso");
17396
- const candidateUrls = isLassoPage ? ["https://api.lasso.byorello.space/api/v1/transcribe"] : [
17397
- "https://api.lasso.byorello.space/api/v1/transcribe",
17398
- "/api/v1/transcribe"
17399
- ];
17368
+ const candidateUrls = isLassoPage ? ["https://api.lasso.byorello.space/api/v1/transcribe"] : ["https://api.lasso.byorello.space/api/v1/transcribe", "/api/v1/transcribe"];
17400
17369
  let lastError = new Error("Failed to contact transcription service");
17401
17370
  for (const url2 of candidateUrls) {
17402
17371
  try {
@@ -17445,12 +17414,7 @@
17445
17414
  const legacyGetUserMedia = nav && (nav.getUserMedia || nav.webkitGetUserMedia || nav.mozGetUserMedia || nav.msGetUserMedia);
17446
17415
  if (legacyGetUserMedia) {
17447
17416
  return new Promise((resolve, reject) => {
17448
- legacyGetUserMedia.call(
17449
- nav,
17450
- { audio: true },
17451
- resolve,
17452
- (err) => reject(new Error(err?.message || "Microphone access was denied."))
17453
- );
17417
+ legacyGetUserMedia.call(nav, { audio: true }, resolve, (err) => reject(new Error(err?.message || "Microphone access was denied.")));
17454
17418
  });
17455
17419
  }
17456
17420
  if (typeof window !== "undefined") {
@@ -17494,10 +17458,7 @@
17494
17458
  activeStream = stream;
17495
17459
  currentMimeType = getSupportedMimeType() || "audio/webm";
17496
17460
  if (typeof MediaRecorder !== "undefined") {
17497
- const recorder = new MediaRecorder(
17498
- stream,
17499
- currentMimeType ? { mimeType: currentMimeType } : void 0
17500
- );
17461
+ const recorder = new MediaRecorder(stream, currentMimeType ? { mimeType: currentMimeType } : void 0);
17501
17462
  activeRecorder = recorder;
17502
17463
  recordedChunks = [];
17503
17464
  recorder.ondataavailable = (event) => {
@@ -18091,9 +18052,7 @@
18091
18052
  presenceUsers.push({ label: user.name, type: "user", color: user.color || "#6ea0ff" });
18092
18053
  }
18093
18054
  }
18094
- const filteredStatic = (!query ? STATIC_TAGS : STATIC_TAGS.filter((t) => t.startsWith(query))).map(
18095
- (t) => ({ label: t, type: "tag", color: "#94a3b8" })
18096
- );
18055
+ const filteredStatic = (!query ? STATIC_TAGS : STATIC_TAGS.filter((t) => t.startsWith(query))).map((t) => ({ label: t, type: "tag", color: "#94a3b8" }));
18097
18056
  const suggestions = [...presenceUsers, ...filteredStatic].slice(0, 6);
18098
18057
  if (suggestions.length === 0) {
18099
18058
  tagMenu.hidden = true;
@@ -19038,7 +18997,8 @@
19038
18997
  function saveNote(text) {
19039
18998
  if (collabEmit("note:save", { body: text }, (response) => {
19040
18999
  if (!response.ok) saveNoteLocally(text);
19041
- })) return;
19000
+ }))
19001
+ return;
19042
19002
  saveNoteLocally(text);
19043
19003
  }
19044
19004
  function saveNoteLocally(text) {
@@ -19049,33 +19009,21 @@
19049
19009
  }
19050
19010
  function renderMarkdown(raw) {
19051
19011
  let t = raw.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
19052
- t = t.replace(
19053
- /```([\s\S]*?)```/g,
19054
- (_, code) => `<pre class="lasso-np-pre"><code>${code.trim()}</code></pre>`
19055
- );
19012
+ t = t.replace(/```([\s\S]*?)```/g, (_, code) => `<pre class="lasso-np-pre"><code>${code.trim()}</code></pre>`);
19056
19013
  t = t.replace(/^### (.+)$/gm, '<h3 class="lasso-np-h">$1</h3>');
19057
19014
  t = t.replace(/^## (.+)$/gm, '<h2 class="lasso-np-h">$1</h2>');
19058
19015
  t = t.replace(/^# (.+)$/gm, '<h1 class="lasso-np-h">$1</h1>');
19059
19016
  t = t.replace(/^---$/gm, '<hr class="lasso-np-hr" />');
19060
19017
  t = t.replace(/^&gt; (.+)$/gm, '<blockquote class="lasso-np-blockquote">$1</blockquote>');
19061
- t = t.replace(
19062
- /^- \[x\] (.+)$/gm,
19063
- '<div class="lasso-np-check done"><span class="lasso-np-check-box">\u2713</span><del>$1</del></div>'
19064
- );
19065
- t = t.replace(
19066
- /^- \[ \] (.+)$/gm,
19067
- '<div class="lasso-np-check"><span class="lasso-np-check-box">\u25CB</span>$1</div>'
19068
- );
19018
+ t = t.replace(/^- \[x\] (.+)$/gm, '<div class="lasso-np-check done"><span class="lasso-np-check-box">\u2713</span><del>$1</del></div>');
19019
+ t = t.replace(/^- \[ \] (.+)$/gm, '<div class="lasso-np-check"><span class="lasso-np-check-box">\u25CB</span>$1</div>');
19069
19020
  t = t.replace(/^[-*] (.+)$/gm, '<li class="lasso-np-li">$1</li>');
19070
19021
  t = t.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>");
19071
19022
  t = t.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
19072
19023
  t = t.replace(/\*(.+?)\*/g, "<em>$1</em>");
19073
19024
  t = t.replace(/~~(.+?)~~/g, "<del>$1</del>");
19074
19025
  t = t.replace(/`(.+?)`/g, '<code class="lasso-np-code">$1</code>');
19075
- t = t.replace(
19076
- /\[(.+?)\]\((.+?)\)/g,
19077
- '<a href="$2" target="_blank" rel="noopener" class="lasso-np-link">$1</a>'
19078
- );
19026
+ t = t.replace(/\[(.+?)\]\((.+?)\)/g, '<a href="$2" target="_blank" rel="noopener" class="lasso-np-link">$1</a>');
19079
19027
  t = t.replace(/\n\n/g, '</p><p class="lasso-np-p">');
19080
19028
  t = t.replace(/\n/g, "<br>");
19081
19029
  return `<p class="lasso-np-p">${t}</p>`;
@@ -19600,51 +19548,6 @@
19600
19548
  var activeStartTime = 0;
19601
19549
  var activeConversationId = null;
19602
19550
  var conversationList = [];
19603
- function isConversationalPrompt(prompt) {
19604
- const p = prompt.trim().toLowerCase().replace(/[!?.,;:]+$/, "");
19605
- const greetings = [
19606
- "hi",
19607
- "hello",
19608
- "hey",
19609
- "heya",
19610
- "howdy",
19611
- "sup",
19612
- "yo",
19613
- "good morning",
19614
- "good afternoon",
19615
- "good evening",
19616
- "thanks",
19617
- "thank you",
19618
- "thx",
19619
- "ty",
19620
- "cool",
19621
- "awesome",
19622
- "great",
19623
- "nice",
19624
- "who are you",
19625
- "what are you",
19626
- "what can you do",
19627
- "help",
19628
- "help me",
19629
- "what is lasso",
19630
- "how do you work",
19631
- "how does this work"
19632
- ];
19633
- if (greetings.includes(p)) return true;
19634
- if (/^(hi|hello|hey|howdy|yo)\b/i.test(p) && p.split(/\s+/).length <= 4) {
19635
- const buildKeywords = /\b(build|create|add|make|fix|update|modify|change|refactor|implement|delete|remove|style|install|wire|code)\b/i;
19636
- if (!buildKeywords.test(p)) return true;
19637
- }
19638
- const mutationVerbs = /\b(build|create|add|make|fix|update|modify|change|refactor|implement|delete|remove|style|install|wire|code|replace|set|switch|swap|rewrite|restyle|rebuild|apply)\b/i;
19639
- const infoPrefixes = /^(what|how|why|when|where|who|which|can you explain|explain|describe|tell me|check out|check|look at|summarize|give me an overview|overview|inspect)\b/i;
19640
- if (infoPrefixes.test(p) && !mutationVerbs.test(p)) {
19641
- return true;
19642
- }
19643
- if (/\b(about|doing|does this|is this|mean|purpose|work|repo|project|codebase|app|application|page|overview|summary)\b/i.test(p) && !mutationVerbs.test(p)) {
19644
- return true;
19645
- }
19646
- return false;
19647
- }
19648
19551
  function isRetryIntent(prompt) {
19649
19552
  const p = prompt.trim().toLowerCase().replace(/[!?.]+$/, "");
19650
19553
  return /^(try\s+again|retry|re-?try|do\s+it\s+again|run\s+again|attempt\s+again|go\s+again|one\s+more\s+time|again)$/.test(p);
@@ -19765,10 +19668,7 @@
19765
19668
  const data6 = await apiFetch(`/agent/conversations?projectId=${encodeURIComponent(projectId)}`);
19766
19669
  if (data6?.conversations && Array.isArray(data6.conversations)) {
19767
19670
  const serverIds = new Set(data6.conversations.map((c) => c.id));
19768
- const merged = [
19769
- ...data6.conversations,
19770
- ...conversationList.filter((local) => !serverIds.has(local.id))
19771
- ];
19671
+ const merged = [...data6.conversations, ...conversationList.filter((local) => !serverIds.has(local.id))];
19772
19672
  conversationList = merged;
19773
19673
  }
19774
19674
  }
@@ -20561,8 +20461,7 @@ Please proceed directly with the implementation and code changes from this point
20561
20461
  const taskId2 = `oneshot-${Date.now()}`;
20562
20462
  activeTaskId = taskId2;
20563
20463
  activeStartTime = Date.now();
20564
- const isChat = isConversationalPrompt(prompt);
20565
- const initialStepTitle = isChat ? "Preparing response\u2026" : "Preparing task\u2026";
20464
+ const initialStepTitle = "Preparing task\u2026";
20566
20465
  commandMessages.push({
20567
20466
  id: taskId2,
20568
20467
  role: "assistant",
@@ -20593,13 +20492,11 @@ Please proceed directly with the implementation and code changes from this point
20593
20492
  if (id) activeConversationId = id;
20594
20493
  });
20595
20494
  }
20596
- if (!isChat) {
20597
- collabEmit("collab:action", {
20598
- sessionId: state.collabProjectId,
20599
- status: "building",
20600
- summary: prompt.slice(0, 120)
20601
- });
20602
- }
20495
+ collabEmit("collab:action", {
20496
+ sessionId: state.collabProjectId,
20497
+ status: "building",
20498
+ summary: prompt.slice(0, 120)
20499
+ });
20603
20500
  if (state.bridgeSocket?.readyState === WebSocket.OPEN) {
20604
20501
  state.bridgeSocket.send(
20605
20502
  JSON.stringify({
@@ -20613,10 +20510,7 @@ Please proceed directly with the implementation and code changes from this point
20613
20510
  // The current prompt is sent in its dedicated `prompt` field. Keep it
20614
20511
  // out of history so the model sees it once, not once as history and
20615
20512
  // once as the active request.
20616
- messages: commandMessages.map((message, index) => ({ message, index })).filter(({ message, index }) => Boolean(message.content) && !message.error && index !== commandMessages.reduce(
20617
- (last, item, itemIndex) => item.role === "user" && item.content === prompt ? itemIndex : last,
20618
- -1
20619
- )).map(({ message }) => message).map((m) => ({ role: m.role, content: m.content }))
20513
+ messages: commandMessages.map((message, index) => ({ message, index })).filter(({ message, index }) => Boolean(message.content) && !message.error && index !== commandMessages.reduce((last, item, itemIndex) => item.role === "user" && item.content === prompt ? itemIndex : last, -1)).map(({ message }) => message).map((m) => ({ role: m.role, content: m.content }))
20620
20514
  })
20621
20515
  );
20622
20516
  } else {
@@ -20926,13 +20820,15 @@ Please proceed directly with the implementation and code changes from this point
20926
20820
  const activeFile = msg.activeDiffFile || changes[0]?.filePath;
20927
20821
  const scopeHtml = msg.todo && msg.todo.length ? `
20928
20822
  <div class="lasso-command-scope-list">
20929
- ${msg.todo.map((item) => `
20823
+ ${msg.todo.map(
20824
+ (item) => `
20930
20825
  <div class="lasso-command-scope-item">
20931
20826
  <span class="lasso-command-scope-action ${item.action}">${item.action === "create" ? "+" : item.action === "delete" ? "\u2212" : "~"}</span>
20932
20827
  <span class="lasso-command-scope-file">${escapeHtml4(item.file)}</span>
20933
20828
  <span class="lasso-command-scope-reason">${escapeHtml4(item.reason)}</span>
20934
20829
  </div>
20935
- `).join("")}
20830
+ `
20831
+ ).join("")}
20936
20832
  </div>
20937
20833
  ` : "";
20938
20834
  const approvedCount = changes.length - rejected.size;
@@ -21162,97 +21058,72 @@ Please proceed directly with the implementation and code changes from this point
21162
21058
  if (payload.sessionId !== state.collabProjectId || !payload.itemId) return;
21163
21059
  removeClipboardItem(payload.itemId);
21164
21060
  });
21165
- state.collabSocket.on(
21166
- "collab:action",
21167
- (payload) => {
21168
- if (payload.sessionId !== state.collabProjectId) return;
21169
- const name = payload.user?.name || "A teammate";
21170
- const status = payload.status || "";
21171
- if (status === "building" || status === "build") {
21172
- if (payload.user?.id && payload.user.id !== state.myUser?.id) {
21173
- state.buildModeUser = { id: payload.user.id, name };
21174
- showActivity(`\u23F8\uFE0F ${name} is in Build Mode. Editing is paused.`, "#fbbc04");
21175
- return;
21176
- }
21177
- } else if (status === "idle" || status === "done" || status === "complete") {
21178
- if (state.buildModeUser && (!payload.user?.id || state.buildModeUser.id === payload.user.id)) {
21179
- state.buildModeUser = null;
21180
- showActivity(`${name} finished building. Editing resumed.`, "#81c995");
21181
- return;
21182
- }
21061
+ state.collabSocket.on("collab:action", (payload) => {
21062
+ if (payload.sessionId !== state.collabProjectId) return;
21063
+ const name = payload.user?.name || "A teammate";
21064
+ const status = payload.status || "";
21065
+ if (status === "building" || status === "build") {
21066
+ if (payload.user?.id && payload.user.id !== state.myUser?.id) {
21067
+ state.buildModeUser = { id: payload.user.id, name };
21068
+ showActivity(`\u23F8\uFE0F ${name} is in Build Mode. Editing is paused.`, "#fbbc04");
21069
+ return;
21070
+ }
21071
+ } else if (status === "idle" || status === "done" || status === "complete") {
21072
+ if (state.buildModeUser && (!payload.user?.id || state.buildModeUser.id === payload.user.id)) {
21073
+ state.buildModeUser = null;
21074
+ showActivity(`${name} finished building. Editing resumed.`, "#81c995");
21075
+ return;
21183
21076
  }
21184
- const text = payload.summary ? `${name} is ${status === "idle" ? "done" : status.replace(/e?$/, "ing")} \u2014 ${payload.summary.slice(0, 90)}` : `${name} ${status === "idle" ? "finished working" : `is ${status}`}`;
21185
- showActivity(text, userNameColor(payload.user?.id || ""));
21186
- }
21187
- );
21188
- state.collabSocket.on(
21189
- "collab:spotlight",
21190
- (payload) => {
21191
- if (payload.sessionId !== state.collabProjectId || !payload.elementId) return;
21192
- const el = state.elementRegistry.get(payload.elementId) || null;
21193
- const by = payload.by || { id: "", name: "Teammate", photo: "" };
21194
- spotlightFor(by, el, payload.label || "");
21195
21077
  }
21196
- );
21197
- state.collabSocket.on(
21198
- "voice:offer",
21199
- (payload) => handleOffer(payload)
21200
- );
21201
- state.collabSocket.on(
21202
- "voice:answer",
21203
- (payload) => handleAnswer(payload)
21204
- );
21205
- state.collabSocket.on(
21206
- "voice:candidate",
21207
- (payload) => handleCandidate(payload)
21208
- );
21209
- state.collabSocket.on(
21210
- "voice:peer_left",
21211
- (payload) => closePeer(payload.socketId || "")
21212
- );
21078
+ const text = payload.summary ? `${name} is ${status === "idle" ? "done" : status.replace(/e?$/, "ing")} \u2014 ${payload.summary.slice(0, 90)}` : `${name} ${status === "idle" ? "finished working" : `is ${status}`}`;
21079
+ showActivity(text, userNameColor(payload.user?.id || ""));
21080
+ });
21081
+ state.collabSocket.on("collab:spotlight", (payload) => {
21082
+ if (payload.sessionId !== state.collabProjectId || !payload.elementId) return;
21083
+ const el = state.elementRegistry.get(payload.elementId) || null;
21084
+ const by = payload.by || { id: "", name: "Teammate", photo: "" };
21085
+ spotlightFor(by, el, payload.label || "");
21086
+ });
21087
+ state.collabSocket.on("voice:offer", (payload) => handleOffer(payload));
21088
+ state.collabSocket.on("voice:answer", (payload) => handleAnswer(payload));
21089
+ state.collabSocket.on("voice:candidate", (payload) => handleCandidate(payload));
21090
+ state.collabSocket.on("voice:peer_left", (payload) => closePeer(payload.socketId || ""));
21213
21091
  }
21214
21092
  function joinCollabSession() {
21215
21093
  if (!state.collabSocket?.connected || !state.collabProjectId) return;
21216
21094
  const selection = currentSelectionPayload();
21217
- collabEmit(
21218
- "session:join",
21219
- { sessionId: state.collabProjectId, selection },
21220
- (payload) => {
21221
- const countEl = getDOM().shadow.querySelector(".lasso-presence-count");
21222
- if (!payload.ok) {
21223
- console.warn("[Lasso Collab] Session join failed for", state.collabProjectId, ":", payload.error);
21224
- state.collabJoined = false;
21225
- if (countEl) countEl.textContent = state.collabProjectId ? "\xB7 closed" : "";
21226
- showActivity(
21227
- payload.error || "Could not join realtime session. Log in to your Lasso workspace and start Lasso to collaborate.",
21228
- "#fdd663"
21229
- );
21230
- return;
21231
- }
21232
- console.log("[Lasso Collab] Joined realtime session:", state.collabProjectId, "as", payload.me);
21233
- state.collabJoined = true;
21234
- if (countEl) countEl.textContent = "";
21235
- state.myUser = payload.me || null;
21236
- if (state.myUser?.name) {
21237
- try {
21238
- localStorage.setItem("lasso:user:name", state.myUser.name);
21239
- } catch {
21240
- }
21241
- updateEmptyStateUserName();
21242
- }
21243
- const snapshot = payload.snapshot || {};
21244
- applyPresence(snapshot.presence || []);
21245
- indexLocks(snapshot.locks || []);
21246
- for (const c of snapshot.comments || []) {
21247
- upsertComment(c);
21095
+ collabEmit("session:join", { sessionId: state.collabProjectId, selection }, (payload) => {
21096
+ const countEl = getDOM().shadow.querySelector(".lasso-presence-count");
21097
+ if (!payload.ok) {
21098
+ console.warn("[Lasso Collab] Session join failed for", state.collabProjectId, ":", payload.error);
21099
+ state.collabJoined = false;
21100
+ if (countEl) countEl.textContent = state.collabProjectId ? "\xB7 closed" : "";
21101
+ showActivity(payload.error || "Could not join realtime session. Log in to your Lasso workspace and start Lasso to collaborate.", "#fdd663");
21102
+ return;
21103
+ }
21104
+ console.log("[Lasso Collab] Joined realtime session:", state.collabProjectId, "as", payload.me);
21105
+ state.collabJoined = true;
21106
+ if (countEl) countEl.textContent = "";
21107
+ state.myUser = payload.me || null;
21108
+ if (state.myUser?.name) {
21109
+ try {
21110
+ localStorage.setItem("lasso:user:name", state.myUser.name);
21111
+ } catch {
21248
21112
  }
21249
- updateLockChip();
21250
- replaceTodos(snapshot.todos || []);
21251
- renderComments();
21252
- updateCommentsBadge();
21253
- startCollabHeartbeat();
21113
+ updateEmptyStateUserName();
21254
21114
  }
21255
- );
21115
+ const snapshot = payload.snapshot || {};
21116
+ applyPresence(snapshot.presence || []);
21117
+ indexLocks(snapshot.locks || []);
21118
+ for (const c of snapshot.comments || []) {
21119
+ upsertComment(c);
21120
+ }
21121
+ updateLockChip();
21122
+ replaceTodos(snapshot.todos || []);
21123
+ renderComments();
21124
+ updateCommentsBadge();
21125
+ startCollabHeartbeat();
21126
+ });
21256
21127
  }
21257
21128
  function startCollabHeartbeat() {
21258
21129
  if (state.collabHeartbeat) return;
@@ -21282,16 +21153,20 @@ Please proceed directly with the implementation and code changes from this point
21282
21153
  function initCursorTracking() {
21283
21154
  let lastEmit = 0;
21284
21155
  const THROTTLE_MS = 50;
21285
- document.addEventListener("mousemove", (e) => {
21286
- if (!state.collabSocket?.connected || !state.collabJoined || !state.collabProjectId) return;
21287
- const now = Date.now();
21288
- if (now - lastEmit < THROTTLE_MS) return;
21289
- lastEmit = now;
21290
- collabEmit("presence:update", {
21291
- sessionId: state.collabProjectId,
21292
- cursor: { x: e.clientX, y: e.clientY }
21293
- });
21294
- }, { passive: true });
21156
+ document.addEventListener(
21157
+ "mousemove",
21158
+ (e) => {
21159
+ if (!state.collabSocket?.connected || !state.collabJoined || !state.collabProjectId) return;
21160
+ const now = Date.now();
21161
+ if (now - lastEmit < THROTTLE_MS) return;
21162
+ lastEmit = now;
21163
+ collabEmit("presence:update", {
21164
+ sessionId: state.collabProjectId,
21165
+ cursor: { x: e.clientX, y: e.clientY }
21166
+ });
21167
+ },
21168
+ { passive: true }
21169
+ );
21295
21170
  }
21296
21171
  function initCollabPagehide() {
21297
21172
  window.addEventListener("pagehide", () => {
@@ -21707,9 +21582,7 @@ Please proceed directly with the implementation and code changes from this point
21707
21582
  }
21708
21583
  }
21709
21584
  const sections = [];
21710
- const sectionEls = document.querySelectorAll(
21711
- "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']"
21712
- );
21585
+ const sectionEls = document.querySelectorAll("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']");
21713
21586
  for (const el of sectionEls) {
21714
21587
  if (el.closest("#lasso-root")) continue;
21715
21588
  const tag = el.tagName.toLowerCase();
@@ -21811,9 +21684,7 @@ Please proceed directly with the implementation and code changes from this point
21811
21684
  if (!clean || state.runtimeErrors.includes(clean)) return;
21812
21685
  state.runtimeErrors = [...state.runtimeErrors.slice(-4), clean];
21813
21686
  if (notifyBridge && state.bridgeSocket?.readyState === WebSocket.OPEN) {
21814
- state.bridgeSocket.send(
21815
- JSON.stringify({ type: "runtime_error", selectionId: state.selectionId, details: clean })
21816
- );
21687
+ state.bridgeSocket.send(JSON.stringify({ type: "runtime_error", selectionId: state.selectionId, details: clean }));
21817
21688
  }
21818
21689
  }
21819
21690
  function formatConsoleArg(arg) {
@@ -21830,9 +21701,7 @@ Please proceed directly with the implementation and code changes from this point
21830
21701
  if (errorListenersInstalled) return;
21831
21702
  errorListenersInstalled = true;
21832
21703
  window.addEventListener("error", (event) => {
21833
- reportRuntimeError(
21834
- `${event.message || "Runtime error"}${event.filename ? ` \xB7 ${event.filename}:${event.lineno}` : ""}`
21835
- );
21704
+ reportRuntimeError(`${event.message || "Runtime error"}${event.filename ? ` \xB7 ${event.filename}:${event.lineno}` : ""}`);
21836
21705
  });
21837
21706
  window.addEventListener("unhandledrejection", (event) => {
21838
21707
  const reason = event.reason instanceof Error ? event.reason.message : String(event.reason || "Unhandled promise rejection");
@@ -22104,9 +21973,7 @@ The agent is automatically attempting to fix the build errors...`;
22104
21973
  }
22105
21974
  function findReusableAgentTask(instruction, element) {
22106
21975
  const normalized = normalizedInstruction(instruction);
22107
- return state.agentTasks.find(
22108
- (task) => normalizedInstruction(task.instruction) === normalized && task.element === (element || void 0)
22109
- );
21976
+ return state.agentTasks.find((task) => normalizedInstruction(task.instruction) === normalized && task.element === (element || void 0));
22110
21977
  }
22111
21978
  function getComponentConversation(selectionId) {
22112
21979
  const task = state.agentTasks.find((candidate) => candidate.selectionId === selectionId);
@@ -22825,9 +22692,7 @@ The agent is automatically attempting to fix the build errors...`;
22825
22692
  submitBtn.className = "primary";
22826
22693
  submitBtn.addEventListener("click", (e) => {
22827
22694
  e.preventDefault();
22828
- const checked = Array.from(
22829
- optionsContainer?.querySelectorAll(".lasso-agent-prompt-checkbox:checked") || []
22830
- ).map((cb) => cb.value);
22695
+ const checked = Array.from(optionsContainer?.querySelectorAll(".lasso-agent-prompt-checkbox:checked") || []).map((cb) => cb.value);
22831
22696
  respondToAgentPrompt(checked.length ? checked.join(", ") : "None");
22832
22697
  });
22833
22698
  actions.append(skipBtn, submitBtn);
@@ -22976,10 +22841,7 @@ The agent is automatically attempting to fix the build errors...`;
22976
22841
  ignoreElements: (node) => node.id === "lasso-root" || Boolean(node.closest?.("#lasso-root"))
22977
22842
  };
22978
22843
  try {
22979
- const [fullCanvas, elementCanvas] = await Promise.all([
22980
- (0, import_html2canvas2.default)(document.body, options),
22981
- (0, import_html2canvas2.default)(el, options)
22982
- ]);
22844
+ const [fullCanvas, elementCanvas] = await Promise.all([(0, import_html2canvas2.default)(document.body, options), (0, import_html2canvas2.default)(el, options)]);
22983
22845
  return {
22984
22846
  full: fullCanvas.toDataURL("image/jpeg", 0.72),
22985
22847
  element: elementCanvas.toDataURL("image/jpeg", 0.82)
@@ -23394,10 +23256,7 @@ The agent is automatically attempting to fix the build errors...`;
23394
23256
  requestAgentNotificationPermission();
23395
23257
  const bridgeSocket = state.bridgeSocket;
23396
23258
  if (!bridgeSocket || bridgeSocket.readyState !== WebSocket.OPEN) {
23397
- setAgentStatus(
23398
- "error",
23399
- "The Lasso agent bridge is not connected. Start Lasso with your dev server and try again."
23400
- );
23259
+ setAgentStatus("error", "The Lasso agent bridge is not connected. Start Lasso with your dev server and try again.");
23401
23260
  return;
23402
23261
  }
23403
23262
  const ownershipGranted = await acquireOwnership(selectedElement);
@@ -23441,9 +23300,7 @@ The agent is automatically attempting to fix the build errors...`;
23441
23300
  const changesHistory = state.changesHistory.map((entry) => ({ ...entry, changes: entry.changes.map((change) => ({ ...change })) }));
23442
23301
  const rect = selectedElement.getBoundingClientRect();
23443
23302
  const computed = getComputedStyle(selectedElement);
23444
- const attributes = Object.fromEntries(
23445
- Array.from(selectedElement.attributes).map((attr) => [attr.name, attr.value])
23446
- );
23303
+ const attributes = Object.fromEntries(Array.from(selectedElement.attributes).map((attr) => [attr.name, attr.value]));
23447
23304
  const needsVisualContext = /\b(look|visual|appearance|color|colour|background|image|icon|spacing|layout|position|align|responsive|style|restyle|font|size)\b/i.test(instruction);
23448
23305
  const screenshots = needsVisualContext ? await captureElementScreenshot(selectedElement) : await screenshotPromise;
23449
23306
  if (bridgeSocket.readyState !== WebSocket.OPEN) {
@@ -186,7 +186,13 @@ function loadFixtureLines(name) {
186
186
  adapter.parse(JSON.stringify({ type: "item.completed", item: { item_type: "command_execution", command: "ls", exit_code: 0, status: "completed" } }), ctx, sink);
187
187
  adapter.parse(JSON.stringify({
188
188
  type: "item.completed",
189
- item: { item_type: "file_change", changes: [{ path: "/proj/src/old.ts", kind: "delete" }, { path: "/proj/src/new.ts", kind: "add" }] },
189
+ item: {
190
+ item_type: "file_change",
191
+ changes: [
192
+ { path: "/proj/src/old.ts", kind: "delete" },
193
+ { path: "/proj/src/new.ts", kind: "add" },
194
+ ],
195
+ },
190
196
  }), ctx, sink);
191
197
  adapter.parse(JSON.stringify({ type: "turn.completed" }), ctx, sink);
192
198
  strict_1.default.equal(state.sessionId, "thr-1");