@lasso-ai/cli 2.1.9 → 2.1.10

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.
@@ -523,7 +523,7 @@ async function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort
523
523
  }
524
524
  }
525
525
  // Handler for edit/ask - uses runtime
526
- async function handleHostedConversation(taskId, prompt, model, provider, messages, socket, signal) {
526
+ async function handleHostedConversation(taskId, prompt, model, provider, messages, socket, signal, conversationId) {
527
527
  const requestController = new AbortController();
528
528
  const abortRequest = () => requestController.abort();
529
529
  signal.addEventListener("abort", abortRequest, { once: true });
@@ -549,6 +549,8 @@ async function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort
549
549
  prompt,
550
550
  model: { id: model, provider },
551
551
  messages: Array.isArray(messages) ? messages : undefined,
552
+ conversationId: conversationId || undefined,
553
+ projectId: projectId || undefined,
552
554
  }),
553
555
  signal: requestController.signal,
554
556
  });
@@ -606,7 +608,7 @@ async function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort
606
608
  taskControllers.set(taskId, controller);
607
609
  const isLocalAgent = ["claude_code", "opencode", "codex_cli", "cursor"].includes(provider || "");
608
610
  if ((message.type === "ask" || isConversationalPrompt(instruction)) && !isLocalAgent) {
609
- await handleHostedConversation(taskId, instruction, model, provider, messages, socket, controller.signal);
611
+ await handleHostedConversation(taskId, instruction, model, provider, messages, socket, controller.signal, message.conversationId);
610
612
  return;
611
613
  }
612
614
  // Do not echo the user's prompt into the thinking feed. The prompt is
@@ -935,7 +937,7 @@ async function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort
935
937
  try {
936
938
  const isLocalAgent = ["claude_code", "opencode", "codex_cli", "cursor"].includes(provider || "");
937
939
  if (isConversationalPrompt(prompt) && !isLocalAgent) {
938
- await handleHostedConversation(taskId, prompt, model, provider, message.messages, socket, controller.signal);
940
+ await handleHostedConversation(taskId, prompt, model, provider, message.messages, socket, controller.signal, message.conversationId);
939
941
  return;
940
942
  }
941
943
  const backendModelId = (0, backends_1.modelIdForBackend)(model || "", provider || "");
package/dist/overlay.js CHANGED
@@ -19679,6 +19679,63 @@
19679
19679
  return null;
19680
19680
  }
19681
19681
  }
19682
+ function getStorageProjectKey() {
19683
+ const proj = state.collab?.projectId || state.collabProjectId || "default";
19684
+ return `lasso:sessions:${proj}`;
19685
+ }
19686
+ function saveLocalSession() {
19687
+ try {
19688
+ const projKey = getStorageProjectKey();
19689
+ if (!activeConversationId) {
19690
+ activeConversationId = `local-session-${Date.now()}`;
19691
+ }
19692
+ const sessionData = {
19693
+ id: activeConversationId,
19694
+ messages: commandMessages,
19695
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
19696
+ title: commandMessages.find((m) => m.role === "user")?.content?.slice(0, 80) || "Untitled Session"
19697
+ };
19698
+ localStorage.setItem(`lasso:session:${activeConversationId}`, JSON.stringify(sessionData));
19699
+ const listRaw = localStorage.getItem(projKey);
19700
+ let list2 = listRaw ? JSON.parse(listRaw) : [];
19701
+ list2 = list2.filter((item) => item.id !== activeConversationId);
19702
+ list2.unshift({
19703
+ id: activeConversationId,
19704
+ title: sessionData.title,
19705
+ updatedAt: sessionData.updatedAt
19706
+ });
19707
+ localStorage.setItem(projKey, JSON.stringify(list2.slice(0, 30)));
19708
+ } catch (err) {
19709
+ }
19710
+ }
19711
+ function loadLocalSession(convId) {
19712
+ try {
19713
+ const raw = localStorage.getItem(`lasso:session:${convId}`);
19714
+ if (!raw) return false;
19715
+ const session = JSON.parse(raw);
19716
+ if (session && Array.isArray(session.messages)) {
19717
+ commandMessages = session.messages;
19718
+ activeConversationId = convId;
19719
+ renderMessages();
19720
+ scrollToBottom();
19721
+ return true;
19722
+ }
19723
+ } catch {
19724
+ }
19725
+ return false;
19726
+ }
19727
+ function restoreLatestSession() {
19728
+ try {
19729
+ const projKey = getStorageProjectKey();
19730
+ const listRaw = localStorage.getItem(projKey);
19731
+ if (!listRaw) return;
19732
+ const list2 = JSON.parse(listRaw);
19733
+ if (list2 && list2.length > 0 && !activeConversationId) {
19734
+ loadLocalSession(list2[0].id);
19735
+ }
19736
+ } catch {
19737
+ }
19738
+ }
19682
19739
  async function createServerConversation(scope2 = "private") {
19683
19740
  const projectId = state.collab?.projectId || state.collabProjectId;
19684
19741
  if (!projectId) return null;
@@ -19690,10 +19747,29 @@
19690
19747
  }
19691
19748
  async function loadConversationList() {
19692
19749
  const projectId = state.collab?.projectId || state.collabProjectId;
19750
+ try {
19751
+ const projKey = getStorageProjectKey();
19752
+ const listRaw = localStorage.getItem(projKey);
19753
+ if (listRaw) {
19754
+ const localList = JSON.parse(listRaw);
19755
+ conversationList = localList.map((item) => ({
19756
+ id: item.id,
19757
+ title: item.title,
19758
+ scope: "local",
19759
+ updatedAt: item.updatedAt
19760
+ }));
19761
+ }
19762
+ } catch {
19763
+ }
19693
19764
  if (!projectId) return;
19694
19765
  const data6 = await apiFetch(`/agent/conversations?projectId=${encodeURIComponent(projectId)}`);
19695
- if (data6?.conversations) {
19696
- conversationList = data6.conversations;
19766
+ if (data6?.conversations && Array.isArray(data6.conversations)) {
19767
+ 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
+ ];
19772
+ conversationList = merged;
19697
19773
  }
19698
19774
  }
19699
19775
  function formatDateAgo(dateStr) {
@@ -19745,6 +19821,9 @@
19745
19821
  activeConversationId = convId;
19746
19822
  if (commandHistoryPopover) commandHistoryPopover.hidden = true;
19747
19823
  commandHistoryBtn?.classList.remove("active");
19824
+ if (loadLocalSession(convId)) {
19825
+ return;
19826
+ }
19748
19827
  const data6 = await apiFetch(`/agent/conversations/${convId}`);
19749
19828
  if (data6?.conversation?.messages && Array.isArray(data6.conversation.messages)) {
19750
19829
  commandMessages = data6.conversation.messages.map((m, idx) => ({
@@ -19762,6 +19841,7 @@
19762
19841
  changesApplied: m.changesApplied,
19763
19842
  error: m.error
19764
19843
  }));
19844
+ saveLocalSession();
19765
19845
  } else {
19766
19846
  commandMessages = [];
19767
19847
  }
@@ -20258,6 +20338,12 @@
20258
20338
  }
20259
20339
  return;
20260
20340
  }
20341
+ const continueBtn = target.closest(".lasso-command-continue-btn");
20342
+ if (continueBtn) {
20343
+ const msgId = continueBtn.closest(".lasso-command-msg")?.dataset.msgId;
20344
+ continueWithThoughts(msgId);
20345
+ return;
20346
+ }
20261
20347
  const retryBtn = target.closest(".lasso-command-retry-btn");
20262
20348
  if (retryBtn) {
20263
20349
  const msgId = retryBtn.closest(".lasso-command-msg")?.dataset.msgId;
@@ -20289,6 +20375,9 @@
20289
20375
  updateEmptyStateUserName();
20290
20376
  renderSuggestions();
20291
20377
  populateModelMenu();
20378
+ if (commandMessages.length === 0) {
20379
+ restoreLatestSession();
20380
+ }
20292
20381
  renderMessages();
20293
20382
  loadConversationList();
20294
20383
  }
@@ -20359,6 +20448,24 @@
20359
20448
  commandMessages.splice(failedIndex, 1);
20360
20449
  void runPrompt(promptToRetry, true);
20361
20450
  }
20451
+ function continueWithThoughts(msgId) {
20452
+ if (activeTaskId) return;
20453
+ const targetMsg = msgId ? commandMessages.find((m) => m.id === msgId) : [...commandMessages].reverse().find((m) => m.role === "assistant");
20454
+ if (!targetMsg) return;
20455
+ const targetIndex = commandMessages.indexOf(targetMsg);
20456
+ const precedingUserMsg = [...commandMessages].slice(0, targetIndex).reverse().find((m) => m.role === "user");
20457
+ const originalPrompt = precedingUserMsg?.content || "";
20458
+ const thoughtSummary = (targetMsg.thinking?.steps || []).map((s) => `- ${s.title}${s.detail ? `: ${s.detail}` : ""}`).join("\n");
20459
+ const continuationPrompt = thoughtSummary ? `Continue executing the previous task: "${originalPrompt}".
20460
+
20461
+ Here is your prior analysis and plan:
20462
+ ${thoughtSummary}
20463
+
20464
+ Please proceed directly with the implementation and code changes from this point.` : originalPrompt;
20465
+ targetMsg.error = void 0;
20466
+ saveLocalSession();
20467
+ void runPrompt(continuationPrompt);
20468
+ }
20362
20469
  async function handleSubmit() {
20363
20470
  if (!commandInput) return;
20364
20471
  if (activeTaskId) {
@@ -20590,6 +20697,7 @@
20590
20697
  });
20591
20698
  }
20592
20699
  }
20700
+ saveLocalSession();
20593
20701
  renderMessages();
20594
20702
  scrollToBottom();
20595
20703
  } else if (message.status === "error" || message.status === "stopped") {
@@ -20610,6 +20718,7 @@
20610
20718
  sessionId: state.collabProjectId,
20611
20719
  status: "idle"
20612
20720
  });
20721
+ saveLocalSession();
20613
20722
  renderMessages();
20614
20723
  scrollToBottom();
20615
20724
  }
@@ -20688,10 +20797,18 @@
20688
20797
  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
20689
20798
  ${escapeHtml4(msg.error)}
20690
20799
  </div>
20691
- <button class="lasso-command-retry-btn" type="button" title="Retry with current model">
20692
- <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
20693
- Retry
20694
- </button>
20800
+ <div class="lasso-command-error-actions" style="display: flex; gap: 8px; margin-top: 6px; flex-wrap: wrap;">
20801
+ ${msg.thinking && msg.thinking.steps.length > 0 ? `
20802
+ <button class="lasso-command-continue-btn" type="button" title="Continue execution using the thoughts and analysis generated so far" style="display: inline-flex; align-items: center; gap: 5px; padding: 5px 10px; font-size: 12px; font-weight: 500; border-radius: 6px; background: rgba(59, 130, 246, 0.15); border: 1px solid rgba(59, 130, 246, 0.3); color: #93c5fd; cursor: pointer;">
20803
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg>
20804
+ Continue with thoughts so far
20805
+ </button>
20806
+ ` : ""}
20807
+ <button class="lasso-command-retry-btn" type="button" title="Retry from start" style="display: inline-flex; align-items: center; gap: 5px; padding: 5px 10px; font-size: 12px; font-weight: 500; border-radius: 6px; background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.15); color: #e2e8f0; cursor: pointer;">
20808
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
20809
+ Retry
20810
+ </button>
20811
+ </div>
20695
20812
  ` : ""}
20696
20813
  </div>
20697
20814
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lasso-ai/cli",
3
- "version": "2.1.9",
3
+ "version": "2.1.10",
4
4
  "description": "Select any part of your running app, describe a change, and let AI edit the real source code.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/cli/index.js",