@lasso-ai/cli 1.0.18 → 1.0.20

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
@@ -7878,6 +7878,9 @@
7878
7878
  chatHistory = [];
7879
7879
  changesHistory = [];
7880
7880
  pendingChanges = [];
7881
+ agentTasks = [];
7882
+ activeTaskId = null;
7883
+ promptTaskId = null;
7881
7884
  runtimeErrors = [];
7882
7885
  screenshotPromise = Promise.resolve({});
7883
7886
  // Agent State
@@ -7902,7 +7905,7 @@
7902
7905
  myUser = null;
7903
7906
  presenceUsers = /* @__PURE__ */ new Map();
7904
7907
  lockMap = /* @__PURE__ */ new Map();
7905
- heldLockElement = "";
7908
+ heldLockElements = /* @__PURE__ */ new Set();
7906
7909
  spotlightUserId = null;
7907
7910
  // Comments
7908
7911
  commentThreads = /* @__PURE__ */ new Map();
@@ -10882,6 +10885,61 @@
10882
10885
  display: none;
10883
10886
  }
10884
10887
 
10888
+ .lasso-task-panel {
10889
+ position: fixed;
10890
+ right: 12px;
10891
+ bottom: 82px;
10892
+ width: 390px;
10893
+ max-width: calc(100vw - 24px);
10894
+ max-height: min(560px, calc(100vh - 104px));
10895
+ padding: 14px;
10896
+ display: flex;
10897
+ flex-direction: column;
10898
+ gap: 10px;
10899
+ color: var(--lo-text);
10900
+ background: var(--lo-surface);
10901
+ border: 1px solid var(--lo-border);
10902
+ border-radius: var(--lo-radius-xl);
10903
+ box-shadow: 0 18px 50px rgba(0,0,0,.35);
10904
+ z-index: 30;
10905
+ opacity: 0;
10906
+ transform: translateY(8px) scale(.98);
10907
+ transition: opacity 150ms ease, transform 150ms ease;
10908
+ pointer-events: auto;
10909
+ }
10910
+
10911
+ .lasso-task-panel.visible { opacity: 1; transform: translateY(0) scale(1); }
10912
+ .lasso-task-panel[hidden] { display: none; }
10913
+ .lasso-task-header, .lasso-task-title-row, .lasso-task-detail-heading { display: flex; align-items: center; }
10914
+ .lasso-task-header { justify-content: space-between; }
10915
+ .lasso-task-title-row { gap: 7px; font-size: 13px; font-weight: 600; }
10916
+ .lasso-task-badge { min-width: 18px; padding: 2px 5px; border-radius: 999px; background: var(--lo-indigo); color: #fff; font-size: 10px; text-align: center; }
10917
+ .lasso-task-close, .lasso-task-back { border: 0; background: transparent; color: var(--lo-text-3); cursor: pointer; }
10918
+ .lasso-task-close { font-size: 20px; line-height: 20px; }
10919
+ .lasso-task-body { min-height: 90px; max-height: 470px; overflow: auto; }
10920
+ .lasso-task-list { display: flex; flex-direction: column; gap: 3px; }
10921
+ .lasso-task-row { width: 100%; display: flex; align-items: center; gap: 9px; padding: 9px; border: 0; border-radius: var(--lo-radius-md); background: transparent; color: var(--lo-text); text-align: left; cursor: pointer; }
10922
+ .lasso-task-row:hover, .lasso-task-row.active { background: var(--lo-surface-hover); }
10923
+ .lasso-task-status { width: 8px; height: 8px; flex: 0 0 auto; border-radius: 50%; background: var(--lo-text-3); }
10924
+ .lasso-task-status.thinking, .lasso-task-status.working { background: var(--lo-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--lo-primary) 18%, transparent); }
10925
+ .lasso-task-status.review { background: #fbbf24; }
10926
+ .lasso-task-status.complete { background: #34d399; }
10927
+ .lasso-task-status.error { background: #f87171; }
10928
+ .lasso-task-row-copy { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
10929
+ .lasso-task-row-copy strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 500; }
10930
+ .lasso-task-row-copy small, .lasso-task-message { color: var(--lo-text-3); font-size: 11px; }
10931
+ .lasso-task-empty { padding: 25px 10px; color: var(--lo-text-3); font-size: 12px; text-align: center; }
10932
+ .lasso-task-detail { padding: 4px 2px; }
10933
+ .lasso-task-detail-heading { justify-content: space-between; margin-bottom: 12px; }
10934
+ .lasso-task-detail-status { color: var(--lo-primary); font-size: 11px; font-weight: 600; }
10935
+ .lasso-task-prompt { margin: 0 0 10px; font-size: 13px; line-height: 1.45; }
10936
+ .lasso-task-message { margin: 0 0 12px; line-height: 1.45; }
10937
+ .lasso-task-activity { display: flex; flex-direction: column; gap: 4px; max-height: 130px; margin: 0 0 12px; padding: 8px; overflow: auto; border-radius: var(--lo-radius-md); background: var(--lo-surface-2); color: var(--lo-text-3); font-size: 10.5px; line-height: 1.35; }
10938
+ .lasso-task-changes { margin-bottom: 10px; color: var(--lo-text-2); font-size: 11px; }
10939
+ .lasso-task-apply { padding: 7px 11px; border: 0; border-radius: var(--lo-radius-full); background: var(--lo-primary); color: #111214; font: inherit; font-size: 11px; font-weight: 600; cursor: pointer; }
10940
+
10941
+ .lasso-agent-tasks-badge { position: absolute; top: 5px; right: 5px; width: 6px; height: 6px; border-radius: 50%; background: var(--lo-primary); }
10942
+
10885
10943
  .lasso-todo-header {
10886
10944
  display: flex;
10887
10945
  align-items: center;
@@ -12064,6 +12122,208 @@
12064
12122
  };
12065
12123
  var terminal_default = data4;
12066
12124
 
12125
+ // src/overlay/tasks/tasks.ts
12126
+ var panel = null;
12127
+ var list = null;
12128
+ var detail = null;
12129
+ var badge = null;
12130
+ var selectionListeners = /* @__PURE__ */ new Set();
12131
+ var statusLabel = {
12132
+ queued: "Queued",
12133
+ thinking: "Thinking",
12134
+ working: "Working",
12135
+ review: "Review",
12136
+ complete: "Complete",
12137
+ error: "Error",
12138
+ stopped: "Stopped"
12139
+ };
12140
+ function taskId() {
12141
+ return `task-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
12142
+ }
12143
+ function sameChangeList(left, right) {
12144
+ return left.length === right.length && left.every((change, index) => {
12145
+ const other = right[index];
12146
+ return Boolean(other) && change.filePath === other.filePath && change.oldString === other.oldString && change.newString === other.newString;
12147
+ });
12148
+ }
12149
+ function onAgentTaskSelected(listener) {
12150
+ selectionListeners.add(listener);
12151
+ return () => selectionListeners.delete(listener);
12152
+ }
12153
+ function notifyTaskSelection(task) {
12154
+ for (const listener of selectionListeners) listener(task);
12155
+ }
12156
+ function createAgentTask(instruction, messages = state.chatHistory, changesHistory = state.changesHistory, element = state.selected, selectionId = state.selectionId) {
12157
+ const now = (/* @__PURE__ */ new Date()).toISOString();
12158
+ const task = {
12159
+ id: taskId(),
12160
+ instruction,
12161
+ status: "queued",
12162
+ message: "Queued",
12163
+ pendingChanges: [],
12164
+ messages: messages.map((message) => ({ ...message })),
12165
+ changesHistory: changesHistory.map((entry) => ({ ...entry, changes: entry.changes.map((change) => ({ ...change })) })),
12166
+ activity: ["Queued"],
12167
+ lastInstruction: instruction,
12168
+ selectionId,
12169
+ element: element || void 0,
12170
+ createdAt: now,
12171
+ updatedAt: now
12172
+ };
12173
+ state.agentTasks.unshift(task);
12174
+ state.activeTaskId = task.id;
12175
+ renderTasks();
12176
+ return task;
12177
+ }
12178
+ function getAgentTask(id) {
12179
+ return id ? state.agentTasks.find((task) => task.id === id) : void 0;
12180
+ }
12181
+ function updateAgentTask(id, patch) {
12182
+ if (!id) return;
12183
+ const task = getAgentTask(id);
12184
+ if (!task) return;
12185
+ Object.assign(task, patch, { updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
12186
+ renderTasks();
12187
+ }
12188
+ function recordTaskActivity(id, text) {
12189
+ const task = getAgentTask(id);
12190
+ const value2 = text.trim();
12191
+ if (!task || !value2 || task.activity[task.activity.length - 1] === value2) return;
12192
+ updateAgentTask(id, { activity: [...task.activity, value2].slice(-200) });
12193
+ }
12194
+ function recordTaskChangeHistory(id, summary, changes) {
12195
+ const task = getAgentTask(id);
12196
+ if (!task) return;
12197
+ const previous = task.changesHistory[task.changesHistory.length - 1];
12198
+ if (previous?.summary === summary && sameChangeList(previous.changes, changes)) return;
12199
+ updateAgentTask(id, {
12200
+ changesHistory: [...task.changesHistory, { summary, changes: changes.map((change) => ({ ...change })), createdAt: (/* @__PURE__ */ new Date()).toISOString() }]
12201
+ });
12202
+ }
12203
+ function appendTaskMessage(id, role, text) {
12204
+ const task = getAgentTask(id);
12205
+ const content = text.trim();
12206
+ if (!task || !content) return;
12207
+ const previous = task.messages[task.messages.length - 1];
12208
+ if (previous?.role === role && previous.content === content) return;
12209
+ const message = {
12210
+ role,
12211
+ content,
12212
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
12213
+ contextId: task.selectionId || void 0
12214
+ };
12215
+ updateAgentTask(task.id, { messages: [...task.messages, message] });
12216
+ return message;
12217
+ }
12218
+ function selectAgentTask(id) {
12219
+ const task = getAgentTask(id);
12220
+ state.activeTaskId = task?.id || null;
12221
+ state.promptTaskId = task?.id || null;
12222
+ notifyTaskSelection(task || null);
12223
+ renderTasks();
12224
+ }
12225
+ function toggleTaskPanel(force) {
12226
+ if (!panel) return;
12227
+ panel.hidden = force === void 0 ? !panel.hidden : !force;
12228
+ panel.classList.toggle("visible", !panel.hidden);
12229
+ if (!panel.hidden) renderTasks();
12230
+ }
12231
+ function buildTaskPanel() {
12232
+ const dom = getDOM();
12233
+ panel = document.createElement("div");
12234
+ panel.className = "lasso-task-panel";
12235
+ panel.hidden = true;
12236
+ panel.innerHTML = `
12237
+ <div class="lasso-task-header">
12238
+ <div class="lasso-task-title-row">
12239
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M7 8h10M7 12h6M7 16h8"/></svg>
12240
+ <span>Agent tasks</span><span class="lasso-task-badge" hidden>0</span>
12241
+ </div>
12242
+ <button class="lasso-task-close" type="button" aria-label="Close tasks">\xD7</button>
12243
+ </div>
12244
+ <div class="lasso-task-body">
12245
+ <div class="lasso-task-list"></div>
12246
+ <div class="lasso-task-detail" hidden></div>
12247
+ </div>
12248
+ `;
12249
+ dom.shadow.appendChild(panel);
12250
+ list = panel.querySelector(".lasso-task-list");
12251
+ detail = panel.querySelector(".lasso-task-detail");
12252
+ badge = panel.querySelector(".lasso-task-badge");
12253
+ panel.querySelector(".lasso-task-close").addEventListener("click", () => toggleTaskPanel(false));
12254
+ renderTasks();
12255
+ return panel;
12256
+ }
12257
+ function renderTasks() {
12258
+ if (!list || !detail || !badge) return;
12259
+ const activeCount = state.agentTasks.filter((task) => task.status !== "complete" && task.status !== "error" && task.status !== "stopped").length;
12260
+ badge.hidden = state.agentTasks.length === 0;
12261
+ badge.textContent = String(activeCount || state.agentTasks.length);
12262
+ const navBadge = getDOM().shadow.querySelector(".lasso-agent-tasks-badge");
12263
+ if (navBadge) navBadge.hidden = activeCount === 0;
12264
+ list.replaceChildren();
12265
+ if (!state.agentTasks.length) {
12266
+ list.innerHTML = `<div class="lasso-task-empty">Prompt an element to start a task.</div>`;
12267
+ } else {
12268
+ for (const task of state.agentTasks) {
12269
+ const button = document.createElement("button");
12270
+ button.type = "button";
12271
+ button.className = `lasso-task-row${task.id === state.activeTaskId ? " active" : ""}`;
12272
+ button.innerHTML = `<span class="lasso-task-status ${task.status}"></span><span class="lasso-task-row-copy"><strong></strong><small>${statusLabel[task.status]}</small></span>`;
12273
+ button.querySelector("strong").textContent = task.instruction;
12274
+ button.addEventListener("click", () => selectAgentTask(task.id));
12275
+ list.appendChild(button);
12276
+ }
12277
+ }
12278
+ const selected = getAgentTask(state.activeTaskId);
12279
+ if (!selected) {
12280
+ detail.hidden = true;
12281
+ return;
12282
+ }
12283
+ detail.hidden = false;
12284
+ detail.replaceChildren();
12285
+ const heading = document.createElement("div");
12286
+ heading.className = "lasso-task-detail-heading";
12287
+ heading.innerHTML = `<span class="lasso-task-detail-status ${selected.status}">${statusLabel[selected.status]}</span><button type="button" class="lasso-task-back">All tasks</button>`;
12288
+ heading.querySelector("button").addEventListener("click", () => selectAgentTask(null));
12289
+ const prompt = document.createElement("p");
12290
+ prompt.className = "lasso-task-prompt";
12291
+ prompt.textContent = selected.instruction;
12292
+ const activity = document.createElement("p");
12293
+ activity.className = "lasso-task-message";
12294
+ activity.textContent = selected.response || selected.message;
12295
+ detail.append(heading, prompt, activity);
12296
+ if (selected.activity.length) {
12297
+ const activityList = document.createElement("div");
12298
+ activityList.className = "lasso-task-activity";
12299
+ for (const entry of selected.activity.slice(-8)) {
12300
+ const line = document.createElement("div");
12301
+ line.textContent = entry;
12302
+ activityList.appendChild(line);
12303
+ }
12304
+ detail.appendChild(activityList);
12305
+ }
12306
+ const pendingChanges = selected.pendingChanges.length ? selected.pendingChanges : selected.changes || [];
12307
+ if (pendingChanges.length) {
12308
+ const changes = document.createElement("div");
12309
+ changes.className = "lasso-task-changes";
12310
+ changes.textContent = `${pendingChanges.length} proposed file change${pendingChanges.length === 1 ? "" : "s"}`;
12311
+ detail.appendChild(changes);
12312
+ if (selected.status === "review") {
12313
+ const apply = document.createElement("button");
12314
+ apply.type = "button";
12315
+ apply.className = "lasso-task-apply";
12316
+ apply.textContent = "Apply changes";
12317
+ apply.addEventListener("click", () => {
12318
+ if (state.bridgeSocket?.readyState !== WebSocket.OPEN) return;
12319
+ state.bridgeSocket.send(JSON.stringify({ type: "apply", taskId: selected.id, changes: pendingChanges }));
12320
+ updateAgentTask(selected.id, { status: "working", message: "Applying changes\u2026", activity: [...selected.activity, "Applying changes\u2026"] });
12321
+ });
12322
+ detail.appendChild(apply);
12323
+ }
12324
+ }
12325
+ }
12326
+
12067
12327
  // src/overlay/notifications.ts
12068
12328
  var permissionRequest = null;
12069
12329
  function requestAgentNotificationPermission() {
@@ -16841,11 +17101,11 @@
16841
17101
  }
16842
17102
  function updateCommentsBadge() {
16843
17103
  const dom = getDOM();
16844
- const badge = dom.shadow.querySelector(".lasso-comments-badge");
16845
- if (!badge) return;
17104
+ const badge2 = dom.shadow.querySelector(".lasso-comments-badge");
17105
+ if (!badge2) return;
16846
17106
  const openCount = threadsInScope().filter((t) => t.root.status === "OPEN").length;
16847
- badge.classList.toggle("visible", openCount > 0);
16848
- badge.textContent = String(openCount);
17107
+ badge2.classList.toggle("visible", openCount > 0);
17108
+ badge2.textContent = String(openCount);
16849
17109
  }
16850
17110
  function commentPosition(thread) {
16851
17111
  const saved = thread.root.meta?.position;
@@ -17596,7 +17856,7 @@
17596
17856
  ta.remove();
17597
17857
  }
17598
17858
  }
17599
- var panel = null;
17859
+ var panel2 = null;
17600
17860
  var items = [];
17601
17861
  var scope = "private";
17602
17862
  var PRIVATE_STORAGE_KEY = "lasso:clipboard:private";
@@ -17655,7 +17915,7 @@
17655
17915
  <div class="lasso-clipboard-list"></div>
17656
17916
  `;
17657
17917
  getDOM().shadow.appendChild(el);
17658
- panel = el;
17918
+ panel2 = el;
17659
17919
  el.querySelector(".lasso-clipboard-close").addEventListener("click", () => toggleClipboardPanel(false));
17660
17920
  el.querySelectorAll(".lasso-clipboard-tab").forEach((button) => {
17661
17921
  button.addEventListener("click", () => {
@@ -17762,14 +18022,14 @@
17762
18022
  return TYPE_COLORS[type] ?? TYPE_COLORS.text;
17763
18023
  }
17764
18024
  function render() {
17765
- const list = panel?.querySelector(".lasso-clipboard-list");
17766
- if (!list) return;
18025
+ const list2 = panel2?.querySelector(".lasso-clipboard-list");
18026
+ if (!list2) return;
17767
18027
  const visible = items.filter((item) => item.scope === scope);
17768
18028
  if (!visible.length) {
17769
- list.innerHTML = `<div class="lasso-clipboard-empty">${scope === "shared" && !state.collabJoined ? "Join a collaboration session to share items." : "No saved items yet."}</div>`;
18029
+ list2.innerHTML = `<div class="lasso-clipboard-empty">${scope === "shared" && !state.collabJoined ? "Join a collaboration session to share items." : "No saved items yet."}</div>`;
17770
18030
  return;
17771
18031
  }
17772
- list.innerHTML = visible.map((item) => {
18032
+ list2.innerHTML = visible.map((item) => {
17773
18033
  const color = typeColor(item.type);
17774
18034
  const icon = typeIcon(item.type);
17775
18035
  const preview = escapeHtml3(item.content).slice(0, 220);
@@ -17793,7 +18053,7 @@
17793
18053
  </div>
17794
18054
  </div>`;
17795
18055
  }).join("");
17796
- list.querySelectorAll(".lasso-clipboard-item").forEach((row) => {
18056
+ list2.querySelectorAll(".lasso-clipboard-item").forEach((row) => {
17797
18057
  const item = visible.find((candidate) => candidate.uid === row.dataset.id);
17798
18058
  if (!item) return;
17799
18059
  const copyBtn = row.querySelector(".lasso-clipboard-copy-item");
@@ -17828,7 +18088,7 @@
17828
18088
  });
17829
18089
  }
17830
18090
  function setStatus(message) {
17831
- const sub = panel?.querySelector(".lasso-clipboard-subtitle");
18091
+ const sub = panel2?.querySelector(".lasso-clipboard-subtitle");
17832
18092
  if (!sub) return;
17833
18093
  const previous = sub.textContent;
17834
18094
  sub.textContent = message;
@@ -17837,10 +18097,10 @@
17837
18097
  }, 1800);
17838
18098
  }
17839
18099
  function toggleClipboardPanel(force) {
17840
- if (!panel) return;
17841
- const next = force === void 0 ? Boolean(panel.hidden) : force;
17842
- panel.hidden = !next;
17843
- panel.classList.toggle("visible", next);
18100
+ if (!panel2) return;
18101
+ const next = force === void 0 ? Boolean(panel2.hidden) : force;
18102
+ panel2.hidden = !next;
18103
+ panel2.classList.toggle("visible", next);
17844
18104
  if (next) requestItems();
17845
18105
  }
17846
18106
  function escapeHtml3(value2) {
@@ -18048,10 +18308,10 @@
18048
18308
  state: "online",
18049
18309
  selection: currentSelectionPayload()
18050
18310
  });
18051
- if (state.heldLockElement) {
18311
+ for (const elementId of state.heldLockElements) {
18052
18312
  collabEmit("lock:extend", {
18053
18313
  sessionId: state.collabProjectId,
18054
- elementId: state.heldLockElement
18314
+ elementId
18055
18315
  });
18056
18316
  }
18057
18317
  }, 3e4);
@@ -18104,7 +18364,7 @@
18104
18364
  }
18105
18365
  const key = elementKey(state.selected);
18106
18366
  const lock = state.lockMap.get(key);
18107
- const mine = state.heldLockElement === key;
18367
+ const mine = state.heldLockElements.has(key);
18108
18368
  if (lock) {
18109
18369
  dom.heldLockChip.hidden = false;
18110
18370
  dom.heldLockChip.classList.toggle("held", mine);
@@ -18139,7 +18399,7 @@
18139
18399
  },
18140
18400
  (res) => {
18141
18401
  if (res.ok && res.lock) {
18142
- state.heldLockElement = key;
18402
+ state.heldLockElements.add(key);
18143
18403
  state.lockMap.set(key, res.lock);
18144
18404
  updateLockChip();
18145
18405
  resolve(true);
@@ -18158,14 +18418,16 @@
18158
18418
  );
18159
18419
  });
18160
18420
  }
18161
- function releaseHeldLock() {
18162
- if (!state.heldLockElement) return;
18163
- const elementId = state.heldLockElement;
18164
- state.heldLockElement = "";
18165
- updateLockChip();
18166
- if (state.collabSocket?.connected && state.collabJoined) {
18167
- collabEmit("lock:release", { sessionId: state.collabProjectId, elementId });
18421
+ function releaseHeldLock(elementId) {
18422
+ const elementIds = elementId ? [elementId] : Array.from(state.heldLockElements);
18423
+ for (const id of elementIds) {
18424
+ if (!state.heldLockElements.delete(id)) continue;
18425
+ state.lockMap.delete(id);
18426
+ if (state.collabSocket?.connected && state.collabJoined) {
18427
+ collabEmit("lock:release", { sessionId: state.collabProjectId, elementId: id });
18428
+ }
18168
18429
  }
18430
+ updateLockChip();
18169
18431
  }
18170
18432
 
18171
18433
  // src/overlay/icons/lasso.ts
@@ -18334,6 +18596,10 @@
18334
18596
  `;
18335
18597
  dom.shadow.appendChild(rev);
18336
18598
  reviewPanel = rev;
18599
+ onAgentTaskSelected((task) => {
18600
+ if (task) restoreTaskPrompt(task);
18601
+ else resetPromptSession();
18602
+ });
18337
18603
  const promptTop = el.querySelector(".lasso-prompt-top");
18338
18604
  let dragState = null;
18339
18605
  promptTop.addEventListener("pointerdown", (event) => {
@@ -18395,10 +18661,19 @@
18395
18661
  stopButton.addEventListener("click", (event) => {
18396
18662
  event.preventDefault();
18397
18663
  event.stopPropagation();
18664
+ const taskId2 = state.promptTaskId || void 0;
18665
+ const task = taskId2 ? getAgentTask(taskId2) : void 0;
18398
18666
  if (state.bridgeSocket?.readyState === WebSocket.OPEN) {
18399
- state.bridgeSocket.send(JSON.stringify({ type: "stop" }));
18667
+ state.bridgeSocket.send(JSON.stringify({ type: "stop", taskId: taskId2 }));
18668
+ }
18669
+ if (taskId2) {
18670
+ appendChat("assistant", "Agent stopped.", taskId2);
18671
+ if (task) {
18672
+ updateAgentTask(task.id, { status: "stopped", message: "Agent stopped." });
18673
+ recordTaskActivity(task.id, "Agent stopped.");
18674
+ }
18400
18675
  }
18401
- appendChat("assistant", "Agent stopped.");
18676
+ if (task?.element) releaseHeldLock(elementKey(task.element));
18402
18677
  resetAgentState();
18403
18678
  });
18404
18679
  const voiceBtn = el.querySelector(".lasso-prompt-voice");
@@ -18455,20 +18730,22 @@
18455
18730
  });
18456
18731
  rev.querySelector(".lasso-review-close").addEventListener("click", closeReview);
18457
18732
  rev.querySelector(".lasso-review-undo").addEventListener("click", () => {
18733
+ const taskId2 = state.promptTaskId || void 0;
18458
18734
  if (state.pendingChanges.length && rev.querySelector(".lasso-review-apply").hidden) {
18459
18735
  if (state.bridgeSocket?.readyState === WebSocket.OPEN) {
18460
- state.bridgeSocket.send(JSON.stringify({ type: "undo" }));
18736
+ state.bridgeSocket.send(JSON.stringify({ type: "undo", taskId: taskId2 }));
18461
18737
  }
18462
18738
  return;
18463
18739
  }
18464
18740
  closeReview();
18465
- appendChat("assistant", "Kept as a proposal. Nothing was changed.");
18741
+ appendChat("assistant", "Kept as a proposal. Nothing was changed.", taskId2);
18466
18742
  });
18467
18743
  rev.querySelector(".lasso-review-apply").addEventListener("click", () => {
18468
- if (!state.bridgeSocket || state.bridgeSocket.readyState !== WebSocket.OPEN || !state.pendingChanges.length) return;
18744
+ const taskId2 = state.promptTaskId;
18745
+ if (!taskId2 || !state.bridgeSocket || state.bridgeSocket.readyState !== WebSocket.OPEN || !state.pendingChanges.length) return;
18469
18746
  requestAgentNotificationPermission();
18470
- state.bridgeSocket.send(JSON.stringify({ type: "apply", changes: state.pendingChanges }));
18471
- appendChat("assistant", "Applying the reviewed change\u2026");
18747
+ state.bridgeSocket.send(JSON.stringify({ type: "apply", taskId: taskId2, changes: state.pendingChanges }));
18748
+ appendChat("assistant", "Applying the reviewed change\u2026", taskId2);
18472
18749
  });
18473
18750
  return { prompt: el, review: rev };
18474
18751
  }
@@ -18637,29 +18914,50 @@
18637
18914
  text.textContent = value2;
18638
18915
  line.append(prefix, text);
18639
18916
  agentLogElement.append(line);
18640
- if (agentLogExpanded) agentLogElement.scrollTop = agentLogElement.scrollHeight;
18917
+ agentLogElement.scrollTop = agentLogElement.scrollHeight;
18641
18918
  }
18642
18919
  if (agentLogToggle) agentLogToggle.hidden = false;
18643
18920
  }
18644
- function appendChat(role, text) {
18921
+ function loadAgentLog(lines) {
18922
+ clearAgentLog();
18923
+ for (const line of lines) appendAgentLog(line);
18924
+ }
18925
+ function renderChatThread() {
18645
18926
  const thread = promptEl?.querySelector(".lasso-chat-thread");
18646
- if (!thread || !text.trim()) return;
18647
- const previous = thread.lastElementChild;
18648
- if (previous?.classList.contains(role) && previous.dataset.rawText === text) return;
18649
- state.chatHistory.push({
18650
- role,
18651
- content: text,
18652
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
18653
- contextId: state.selectionId || void 0
18654
- });
18655
- const item = document.createElement("div");
18656
- item.className = `lasso-chat-message ${role}`;
18657
- item.dataset.rawText = text;
18658
- renderChatText(item, text);
18659
- thread.appendChild(item);
18660
- while (thread.children.length > 6) thread.firstElementChild?.remove();
18927
+ if (!thread) return;
18928
+ thread.replaceChildren();
18929
+ for (const message of state.chatHistory.slice(-6)) {
18930
+ const item = document.createElement("div");
18931
+ item.className = `lasso-chat-message ${message.role}`;
18932
+ item.dataset.rawText = message.content;
18933
+ renderChatText(item, message.content);
18934
+ thread.appendChild(item);
18935
+ }
18661
18936
  thread.scrollTop = thread.scrollHeight;
18662
18937
  }
18938
+ function appendChat(role, text, taskId2 = state.promptTaskId) {
18939
+ const content = text.trim();
18940
+ if (!content) return;
18941
+ const task = taskId2 ? getAgentTask(taskId2) : void 0;
18942
+ if (taskId2 && !task) return;
18943
+ if (task) {
18944
+ const previous = task.messages[task.messages.length - 1];
18945
+ if (previous?.role === role && previous.content === content) return;
18946
+ const message = appendTaskMessage(task.id, role, content);
18947
+ if (!message || state.promptTaskId !== task.id) return;
18948
+ state.chatHistory = [...task.messages];
18949
+ } else {
18950
+ const previous = state.chatHistory[state.chatHistory.length - 1];
18951
+ if (previous?.role === role && previous.content === content) return;
18952
+ state.chatHistory.push({
18953
+ role,
18954
+ content,
18955
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
18956
+ contextId: state.selectionId || void 0
18957
+ });
18958
+ }
18959
+ renderChatThread();
18960
+ }
18663
18961
  function renderChatText(container, text) {
18664
18962
  const codePattern = /(`+)([\s\S]*?)\1/g;
18665
18963
  let lastIndex = 0;
@@ -18677,40 +18975,42 @@
18677
18975
  container.append(document.createTextNode(text.slice(lastIndex)));
18678
18976
  }
18679
18977
  }
18680
- function setAgentStatus(status, message, detail) {
18978
+ function setAgentStatus(status, message, detail2, taskId2 = state.promptTaskId, appendMessage = true) {
18681
18979
  if (!agentStatusElement || !agentStatusMessage || !sendButton) return;
18682
- const logMessage = detail?.trim() || message.trim();
18980
+ const logMessage = detail2?.trim() || message.trim();
18683
18981
  const lastLog = agentLogLines[agentLogLines.length - 1];
18684
- if (agentStatusElement.dataset.status === status && agentStatusMessage.textContent === message && (status === "thinking" || status === "working") && (!logMessage || lastLog === logMessage)) return;
18982
+ if (agentStatusElement.dataset.status === status && agentStatusMessage.textContent === message && (!logMessage || lastLog === logMessage)) return;
18685
18983
  appendAgentLog(logMessage);
18984
+ if (taskId2) recordTaskActivity(taskId2, logMessage);
18985
+ const running = status === "thinking" || status === "working";
18686
18986
  agentStatusElement.hidden = status === "review";
18687
18987
  agentStatusElement.dataset.status = status;
18688
18988
  agentStatusMessage.textContent = message;
18689
18989
  const badgeEl = agentStatusElement.querySelector(".lasso-agent-status-badge");
18690
18990
  if (badgeEl) {
18691
- badgeEl.textContent = status === "thinking" ? "thinking" : status === "working" ? "executing" : status;
18991
+ badgeEl.textContent = status === "thinking" ? "thinking" : status === "working" ? "executing" : status === "complete" ? "done" : status;
18692
18992
  badgeEl.className = `lasso-agent-status-badge ${status}`;
18693
18993
  }
18694
- state.agentRunning = status === "thinking" || status === "working";
18695
- sendButton.classList.toggle("loading", state.agentRunning);
18696
- if (stopButton) stopButton.hidden = !state.agentRunning;
18697
- if (promptInput) promptInput.disabled = state.agentRunning;
18994
+ state.agentRunning = running;
18995
+ sendButton.classList.toggle("loading", running);
18996
+ if (stopButton) stopButton.hidden = !running;
18997
+ if (promptInput) promptInput.disabled = false;
18698
18998
  const label = sendButton.querySelector("span");
18699
18999
  if (label) {
18700
- label.textContent = status === "review" ? "Review" : status === "error" ? "Retry" : state.agentRunning ? "" : "Send";
19000
+ label.textContent = status === "review" ? "Review" : status === "error" ? "Retry" : running ? "" : "Send";
18701
19001
  }
18702
- sendButton.setAttribute("aria-label", state.agentRunning ? "Agent is working" : status === "error" ? "Retry request" : "Send request");
18703
- sendButton.dataset.state = status === "error" ? "retry" : state.agentRunning ? "working" : status;
19002
+ sendButton.setAttribute("aria-label", running ? "Agent is working" : status === "error" ? "Retry request" : "Send request");
19003
+ sendButton.dataset.state = status === "error" ? "retry" : running ? "working" : status;
18704
19004
  syncSendButtonState();
18705
- if (status === "review" || status === "error" || status === "stopped") {
18706
- appendChat(status === "error" ? "error" : "assistant", message);
19005
+ if (appendMessage && (status === "review" || status === "error" || status === "stopped")) {
19006
+ appendChat(status === "error" ? "error" : "assistant", message, taskId2);
18707
19007
  }
18708
19008
  }
18709
19009
  function syncSendButtonState() {
18710
19010
  if (!promptInput || !sendButton) return;
18711
19011
  const hasInstruction = Boolean(promptInput.value.trim()) || sendButton.dataset.state === "retry" && Boolean(state.lastInstruction?.trim());
18712
19012
  const isReviewAction = sendButton.dataset.state === "review";
18713
- sendButton.disabled = state.agentRunning || !hasInstruction && !isReviewAction;
19013
+ sendButton.disabled = !hasInstruction && !isReviewAction;
18714
19014
  }
18715
19015
  function resetAgentState() {
18716
19016
  state.agentRunning = false;
@@ -18738,6 +19038,64 @@
18738
19038
  if (stopButton) stopButton.hidden = true;
18739
19039
  if (promptInput) promptInput.disabled = false;
18740
19040
  }
19041
+ function hideReview() {
19042
+ if (reviewPanel) reviewPanel.hidden = true;
19043
+ }
19044
+ function resetPromptSession() {
19045
+ state.promptTaskId = null;
19046
+ state.chatHistory = [];
19047
+ state.changesHistory = [];
19048
+ state.pendingChanges = [];
19049
+ state.lastInstruction = "";
19050
+ state.selectionId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
19051
+ state.screenshotPromise = Promise.resolve({});
19052
+ hideReview();
19053
+ clearAgentLog();
19054
+ resetAgentState();
19055
+ renderChatThread();
19056
+ if (promptInput) {
19057
+ promptInput.value = "";
19058
+ syncSendButtonState();
19059
+ }
19060
+ }
19061
+ function restoreTaskPrompt(task) {
19062
+ state.activeTaskId = task.id;
19063
+ state.promptTaskId = task.id;
19064
+ state.chatHistory = [...task.messages];
19065
+ state.changesHistory = task.changesHistory.map((entry) => ({ ...entry, changes: entry.changes.map((change) => ({ ...change })) }));
19066
+ state.pendingChanges = [...task.pendingChanges];
19067
+ state.lastInstruction = task.lastInstruction;
19068
+ state.selectionId = task.selectionId;
19069
+ state.screenshotPromise = Promise.resolve({});
19070
+ hideReview();
19071
+ clearAgentLog();
19072
+ loadAgentLog(task.activity);
19073
+ renderChatThread();
19074
+ const dom = getDOM();
19075
+ if (task.element?.isConnected) {
19076
+ state.selected = task.element;
19077
+ state.elementRegistry.set(elementKey(task.element), task.element);
19078
+ updateSelectedVisual();
19079
+ updateLockChip();
19080
+ if (promptElement) promptElement.textContent = getElementLabel(task.element);
19081
+ positionPrompt(task.element);
19082
+ } else {
19083
+ state.selected = null;
19084
+ dom.selectedBox.style.display = "none";
19085
+ dom.label.style.display = "none";
19086
+ if (promptElement) promptElement.textContent = "Agent task";
19087
+ if (promptEl) {
19088
+ promptEl.style.left = `${Math.max(12, (window.innerWidth - 360) / 2)}px`;
19089
+ promptEl.style.top = `${Math.max(12, (window.innerHeight - 200) / 2)}px`;
19090
+ }
19091
+ updateLockChip();
19092
+ }
19093
+ if (promptEl) promptEl.classList.add("visible");
19094
+ setSelectMode(false);
19095
+ if (task.status === "review" && state.pendingChanges.length) showReview(state.pendingChanges, task.message);
19096
+ setAgentStatus(task.status, task.message, task.detail, task.id, false);
19097
+ requestAnimationFrame(() => promptInput?.focus());
19098
+ }
18741
19099
  function showReview(changes, summary) {
18742
19100
  if (!reviewPanel) return;
18743
19101
  const subtitle = reviewPanel.querySelector(".lasso-review-subtitle");
@@ -18771,7 +19129,7 @@
18771
19129
  reviewPanel.style.top = `${top}px`;
18772
19130
  }
18773
19131
  function closeReview() {
18774
- if (reviewPanel) reviewPanel.hidden = true;
19132
+ hideReview();
18775
19133
  resetAgentState();
18776
19134
  }
18777
19135
  function positionPrompt(el) {
@@ -18802,11 +19160,10 @@
18802
19160
  promptEl.classList.remove("visible");
18803
19161
  if (promptInput) promptInput.value = "";
18804
19162
  sendPresenceUpdate({ selection: null });
18805
- if (state.heldLockElement) releaseHeldLock();
18806
- updateLockChip();
18807
19163
  renderRemoteBoxes();
18808
19164
  renderComments();
18809
19165
  state.selected = null;
19166
+ updateLockChip();
18810
19167
  getDOM().selectedBox.style.display = "none";
18811
19168
  getDOM().label.style.display = "none";
18812
19169
  if (reenter) {
@@ -18816,18 +19173,19 @@
18816
19173
  async function handleSend(event) {
18817
19174
  event.preventDefault();
18818
19175
  event.stopPropagation();
18819
- if (!state.selected || !promptInput || !sendButton) return;
19176
+ const selectedElement = state.selected;
19177
+ if (!selectedElement || !promptInput || !sendButton) return;
18820
19178
  const instruction = promptInput.value.trim() || (sendButton.dataset.state === "retry" ? state.lastInstruction : "");
18821
19179
  if (!instruction) {
18822
19180
  promptInput.focus();
18823
19181
  return;
18824
19182
  }
18825
- clearAgentLog();
18826
19183
  requestAgentNotificationPermission();
18827
19184
  const isQuestion = /^(hi|hello|hey|thanks|thank you|what|why|how|when|where|who|which|is|are|does|do|can|could|would|should|tell me|explain|describe)\b/i.test(instruction) || /\?$/.test(instruction);
18828
19185
  const isExplicitEdit = /\b(change|edit|update|make|add|remove|delete|fix|replace|turn|convert|style|restyle|move|rename|implement|build|create|increase|decrease|hide|show|align|resize|set|enable|disable)\b/i.test(instruction);
18829
19186
  const wantsAnswer = isQuestion && !/\b(can|could|would|please)\s+you\s+(change|edit|update|add|fix|make)\b/i.test(instruction) || !isExplicitEdit && !isQuestion;
18830
- if (!state.bridgeSocket || state.bridgeSocket.readyState !== WebSocket.OPEN) {
19187
+ const bridgeSocket = state.bridgeSocket;
19188
+ if (!bridgeSocket || bridgeSocket.readyState !== WebSocket.OPEN) {
18831
19189
  setAgentStatus(
18832
19190
  "error",
18833
19191
  "The Lasso agent bridge is not connected. Start Lasso with your dev server and try again."
@@ -18835,39 +19193,54 @@
18835
19193
  return;
18836
19194
  }
18837
19195
  if (!wantsAnswer) {
18838
- const ownershipGranted = await acquireOwnership(state.selected);
19196
+ const ownershipGranted = await acquireOwnership(selectedElement);
18839
19197
  if (!ownershipGranted) return;
18840
19198
  }
18841
19199
  if (!wantsAnswer && state.collabSocket?.connected && state.collabJoined) {
18842
19200
  collabEmit("collab:action", {
18843
19201
  sessionId: state.collabProjectId,
18844
19202
  status: "preparing",
18845
- elementId: elementKey(state.selected),
19203
+ elementId: elementKey(selectedElement),
18846
19204
  summary: `Edits for ${instruction.slice(0, 80)}`
18847
19205
  });
18848
19206
  }
19207
+ const selectionId = state.selectionId;
19208
+ const model = state.selectedModel;
19209
+ const screenshotPromise = state.screenshotPromise;
18849
19210
  appendChat("user", instruction);
18850
- setAgentStatus("thinking", "Thinking\u2026");
19211
+ const messages = state.chatHistory.map((message) => ({ ...message }));
19212
+ const changesHistory = state.changesHistory.map((entry) => ({ ...entry, changes: entry.changes.map((change) => ({ ...change })) }));
19213
+ const task = createAgentTask(instruction, messages, changesHistory, selectedElement, selectionId);
19214
+ updateAgentTask(task.id, { status: "thinking", message: "Thinking\u2026", activity: [...task.activity, "Thinking\u2026"] });
19215
+ resetPromptSession();
19216
+ state.promptTaskId = task.id;
18851
19217
  state.lastInstruction = instruction;
18852
- promptInput.value = "";
18853
- const rect = state.selected.getBoundingClientRect();
18854
- const computed = getComputedStyle(state.selected);
19218
+ const rect = selectedElement.getBoundingClientRect();
19219
+ const computed = getComputedStyle(selectedElement);
18855
19220
  const attributes = Object.fromEntries(
18856
- Array.from(state.selected.attributes).map((attr) => [attr.name, attr.value])
19221
+ Array.from(selectedElement.attributes).map((attr) => [attr.name, attr.value])
18857
19222
  );
18858
19223
  const needsVisualContext = /\b(look|visual|appearance|color|colour|background|image|icon|spacing|layout|position|align|responsive|style|restyle|font|size)\b/i.test(instruction);
18859
- const screenshots = needsVisualContext && state.selected ? await captureElementScreenshot(state.selected) : await state.screenshotPromise;
18860
- state.bridgeSocket.send(
19224
+ const screenshots = needsVisualContext ? await captureElementScreenshot(selectedElement) : await screenshotPromise;
19225
+ if (bridgeSocket.readyState !== WebSocket.OPEN) {
19226
+ const message = "The Lasso agent bridge disconnected before the task could start.";
19227
+ updateAgentTask(task.id, { status: "error", message });
19228
+ appendChat("error", message, task.id);
19229
+ if (!wantsAnswer) releaseHeldLock(elementKey(selectedElement));
19230
+ return;
19231
+ }
19232
+ bridgeSocket.send(
18861
19233
  JSON.stringify({
18862
19234
  type: wantsAnswer ? "ask" : "edit",
19235
+ taskId: task.id,
18863
19236
  question: wantsAnswer ? instruction : void 0,
18864
19237
  instruction,
18865
- messages: state.chatHistory,
18866
- changesHistory: state.changesHistory,
18867
- model: state.selectedModel.id,
18868
- provider: state.selectedModel.provider,
19238
+ messages,
19239
+ changesHistory,
19240
+ model: model.id,
19241
+ provider: model.provider,
18869
19242
  context: {
18870
- selectionId: state.selectionId,
19243
+ selectionId,
18871
19244
  position: {
18872
19245
  top: rect.top,
18873
19246
  left: rect.left,
@@ -18898,18 +19271,18 @@
18898
19271
  screenshots
18899
19272
  },
18900
19273
  element: {
18901
- tag: state.selected.tagName.toLowerCase(),
18902
- group: getElementGroup(state.selected),
18903
- label: getElementLabel(state.selected),
18904
- html: state.selected.outerHTML.slice(0, 6e3),
18905
- sourceHint: state.selected.getAttribute("data-source") || state.selected.getAttribute("data-lasso-source") || getSourceHint(state.selected)
19274
+ tag: selectedElement.tagName.toLowerCase(),
19275
+ group: getElementGroup(selectedElement),
19276
+ label: getElementLabel(selectedElement),
19277
+ html: selectedElement.outerHTML.slice(0, 6e3),
19278
+ sourceHint: selectedElement.getAttribute("data-source") || selectedElement.getAttribute("data-lasso-source") || getSourceHint(selectedElement)
18906
19279
  }
18907
19280
  })
18908
19281
  );
18909
19282
  }
18910
19283
  function openPromptForSelected(selected) {
18911
19284
  if (!promptEl || !promptElement) return;
18912
- clearAgentLog();
19285
+ resetPromptSession();
18913
19286
  promptElement.textContent = getElementLabel(selected);
18914
19287
  positionPrompt(selected);
18915
19288
  promptEl.classList.add("visible");
@@ -19648,7 +20021,7 @@
19648
20021
  state.hovered = el;
19649
20022
  updateHoverVisual(state.hovered);
19650
20023
  }
19651
- function updateSelectedVisual2() {
20024
+ function updateSelectedVisual() {
19652
20025
  const dom = getDOM();
19653
20026
  if (!state.selected) {
19654
20027
  dom.selectedBox.style.display = "none";
@@ -19711,25 +20084,25 @@
19711
20084
  const parts2 = filePath.split("/");
19712
20085
  const name = parts2.pop() || filePath;
19713
20086
  const dir = parts2.length ? parts2.join("/") + "/" : "";
19714
- let badge = "M";
20087
+ let badge2 = "M";
19715
20088
  let badgeClass = "mod";
19716
20089
  if (code.includes("A")) {
19717
- badge = "A";
20090
+ badge2 = "A";
19718
20091
  badgeClass = "add";
19719
20092
  } else if (code.includes("D")) {
19720
- badge = "D";
20093
+ badge2 = "D";
19721
20094
  badgeClass = "del";
19722
20095
  } else if (code.includes("R")) {
19723
- badge = "R";
20096
+ badge2 = "R";
19724
20097
  badgeClass = "ren";
19725
20098
  } else if (code === "??" || code.includes("?")) {
19726
- badge = "U";
20099
+ badge2 = "U";
19727
20100
  badgeClass = "unt";
19728
20101
  } else if (code.includes("M")) {
19729
- badge = "M";
20102
+ badge2 = "M";
19730
20103
  badgeClass = "mod";
19731
20104
  }
19732
- return { code, badge, badgeClass, path: filePath, dir, name };
20105
+ return { code, badge: badge2, badgeClass, path: filePath, dir, name };
19733
20106
  });
19734
20107
  }
19735
20108
  function escapeHtml4(text) {
@@ -19940,7 +20313,7 @@
19940
20313
  const uninitView = gitPanelEl.querySelector(".lasso-git-uninit-view");
19941
20314
  const repoView = gitPanelEl.querySelector(".lasso-git-repo-view");
19942
20315
  const filesList = gitPanelEl.querySelector(".lasso-git-files-list");
19943
- const badge = gitPanelEl.querySelector(".lasso-git-changes-badge");
20316
+ const badge2 = gitPanelEl.querySelector(".lasso-git-changes-badge");
19944
20317
  const commitBtn = gitPanelEl.querySelector(".lasso-git-commit-btn");
19945
20318
  const commitBtnText = gitPanelEl.querySelector(".lasso-git-commit-btn-text");
19946
20319
  const pushBtn = gitPanelEl.querySelector(".lasso-git-push-btn");
@@ -19963,12 +20336,12 @@
19963
20336
  uninitView.hidden = next.isRepo;
19964
20337
  repoView.hidden = !next.isRepo;
19965
20338
  }
19966
- if (filesList && badge && next.isRepo) {
20339
+ if (filesList && badge2 && next.isRepo) {
19967
20340
  const rawStatus = next.status || [];
19968
20341
  const parsed = parseGitStatus(rawStatus);
19969
20342
  const count = parsed.length;
19970
- badge.textContent = count > 0 ? String(count) : "Clean";
19971
- badge.className = `lasso-git-changes-badge${count > 0 ? " has-changes" : " clean"}`;
20343
+ badge2.textContent = count > 0 ? String(count) : "Clean";
20344
+ badge2.className = `lasso-git-changes-badge${count > 0 ? " has-changes" : " clean"}`;
19972
20345
  if (count === 0) {
19973
20346
  filesList.innerHTML = `
19974
20347
  <div class="lasso-git-clean-state">
@@ -20153,6 +20526,11 @@
20153
20526
  </svg>
20154
20527
  </button>
20155
20528
 
20529
+ <button class="lasso-tool-btn agent-tasks-tool" type="button" aria-label="Agent tasks" title="Agent tasks">
20530
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M7 8h10M7 12h6M7 16h8"/></svg>
20531
+ <i class="lasso-agent-tasks-badge" hidden></i>
20532
+ </button>
20533
+
20156
20534
  <button class="lasso-tool-btn notepad-tool" type="button" aria-label="Notepad" title="Scratchpad & Notes">
20157
20535
  <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
20158
20536
  <path d="M16 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8l-5-5z"/>
@@ -20228,6 +20606,7 @@
20228
20606
  const commentBtn = toolbar.querySelector(".comment-tool");
20229
20607
  const gitBtn = toolbar.querySelector(".git-tool");
20230
20608
  const todoBtn = toolbar.querySelector(".todo-tool");
20609
+ const agentTasksBtn = toolbar.querySelector(".agent-tasks-tool");
20231
20610
  const notepadBtn = toolbar.querySelector(".notepad-tool");
20232
20611
  const clipboardBtn = toolbar.querySelector(".clipboard-tool");
20233
20612
  const voiceBtn = toolbar.querySelector(".voice-tool");
@@ -20264,6 +20643,11 @@
20264
20643
  e.stopPropagation();
20265
20644
  toggleTodoPanel();
20266
20645
  });
20646
+ agentTasksBtn.addEventListener("click", (e) => {
20647
+ e.preventDefault();
20648
+ e.stopPropagation();
20649
+ toggleTaskPanel();
20650
+ });
20267
20651
  notepadBtn.addEventListener("click", (e) => {
20268
20652
  e.preventDefault();
20269
20653
  e.stopPropagation();
@@ -20359,39 +20743,81 @@
20359
20743
  setGeneratedCommitMessage(message.message || "", message.error);
20360
20744
  }
20361
20745
  if (message.type === "agent_status" && message.status && message.message) {
20362
- setAgentStatus(message.status, message.message, message.detail);
20746
+ const taskId2 = message.taskId;
20747
+ const task = taskId2 ? getAgentTask(taskId2) : void 0;
20748
+ const taskStatus = message.status === "error" ? "error" : message.status === "stopped" ? "stopped" : message.status;
20749
+ if (task) {
20750
+ const patch = {
20751
+ status: taskStatus,
20752
+ message: message.message,
20753
+ detail: message.detail
20754
+ };
20755
+ if (message.status === "review" && message.changes) {
20756
+ patch.changes = message.changes;
20757
+ patch.pendingChanges = message.changes;
20758
+ recordTaskChangeHistory(task.id, message.message, message.changes);
20759
+ }
20760
+ updateAgentTask(task.id, patch);
20761
+ recordTaskActivity(task.id, message.detail || message.message);
20762
+ }
20763
+ if (message.status === "review") {
20764
+ appendChat("assistant", message.message, taskId2);
20765
+ if (message.changes?.length) void notifyAgent("Review requested", message.message);
20766
+ } else if (message.status === "error" || message.status === "stopped") {
20767
+ appendChat(message.status === "error" ? "error" : "assistant", message.message, taskId2);
20768
+ if (task?.element) releaseHeldLock(elementKey(task.element));
20769
+ void notifyAgent(message.status === "error" ? "Agent error" : "Agent stopped", message.message);
20770
+ }
20771
+ if (taskId2 && taskId2 !== state.promptTaskId) return;
20772
+ setAgentStatus(message.status, message.message, message.detail, taskId2, false);
20363
20773
  setDragCardStatus(message.status, message.message);
20364
20774
  if (message.status === "review" && message.changes?.length) {
20365
- void notifyAgent("Review requested", message.message);
20366
- state.pendingChanges = message.changes;
20367
- state.changesHistory.push({
20368
- summary: message.message,
20369
- changes: message.changes,
20370
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
20371
- });
20775
+ if (task) state.changesHistory = task.changesHistory.map((entry) => ({ ...entry, changes: entry.changes.map((change) => ({ ...change })) }));
20776
+ state.pendingChanges = [...message.changes];
20372
20777
  showReview(message.changes, message.message);
20373
20778
  }
20374
20779
  }
20375
20780
  if (message.type === "assistant_message" && message.message) {
20781
+ const taskId2 = message.taskId;
20782
+ const task = taskId2 ? getAgentTask(taskId2) : void 0;
20783
+ if (task) {
20784
+ updateAgentTask(task.id, { status: "complete", message: "Complete", response: message.message });
20785
+ recordTaskActivity(task.id, "Agent complete");
20786
+ }
20787
+ appendChat("assistant", message.message, taskId2);
20788
+ if (taskId2 && taskId2 !== state.promptTaskId) {
20789
+ void notifyAgent("Agent complete", message.message);
20790
+ return;
20791
+ }
20376
20792
  void notifyAgent("Agent complete", message.message);
20377
- appendChat("assistant", message.message);
20378
20793
  resetAgentState();
20379
20794
  }
20380
20795
  if (message.type === "applied" || message.type === "undone") {
20381
- void notifyAgent(message.type === "applied" ? "Changes applied" : "Change undone", message.message || "Done.");
20382
- appendChat("assistant", message.message || "Done.");
20383
- releaseHeldLock();
20796
+ const taskId2 = message.taskId;
20797
+ const task = taskId2 ? getAgentTask(taskId2) : void 0;
20798
+ if (task) {
20799
+ updateAgentTask(task.id, { status: "complete", message: message.message || "Done.", changes: [], pendingChanges: [] });
20800
+ recordTaskActivity(task.id, message.message || "Done.");
20801
+ }
20802
+ const isPromptTask = !taskId2 || taskId2 === state.promptTaskId;
20803
+ const result = message.message || "Done.";
20804
+ void notifyAgent(message.type === "applied" ? "Changes applied" : "Change undone", result);
20805
+ appendChat("assistant", result, taskId2);
20806
+ if (task?.element) releaseHeldLock(elementKey(task.element));
20807
+ else if (isPromptTask && state.selected) releaseHeldLock(elementKey(state.selected));
20384
20808
  if (state.collabSocket?.connected && state.collabJoined) {
20385
20809
  collabEmit("collab:action", {
20386
20810
  sessionId: state.collabProjectId,
20387
20811
  status: "idle",
20388
- elementId: state.selected ? elementKey(state.selected) : void 0,
20389
- summary: message.message || (message.type === "undone" ? "Undid the last change" : "Applied the change")
20812
+ elementId: task?.element ? elementKey(task.element) : state.selected ? elementKey(state.selected) : void 0,
20813
+ summary: result || (message.type === "undone" ? "Undid the last change" : "Applied the change")
20390
20814
  });
20391
20815
  }
20392
- closeReview();
20393
- resetDrag(false);
20394
- state.pendingChanges = [];
20816
+ if (isPromptTask) {
20817
+ closeReview();
20818
+ resetDrag(false);
20819
+ state.pendingChanges = [];
20820
+ }
20395
20821
  }
20396
20822
  } catch {
20397
20823
  console.warn("[lasso] Invalid bridge message");
@@ -20417,6 +20843,7 @@
20417
20843
  buildTodoPanel();
20418
20844
  buildNotepadPanel();
20419
20845
  buildClipboardPanel();
20846
+ buildTaskPanel();
20420
20847
  buildPrompt();
20421
20848
  buildDrag();
20422
20849
  initErrorListeners();
@@ -20432,7 +20859,7 @@
20432
20859
  renderCommentPins();
20433
20860
  }
20434
20861
  if (state.selected) {
20435
- updateSelectedVisual2();
20862
+ updateSelectedVisual();
20436
20863
  if (!state.promptDragged) {
20437
20864
  positionPrompt(state.selected);
20438
20865
  }
@@ -20474,13 +20901,8 @@
20474
20901
  updateLockChip();
20475
20902
  sendPresenceUpdate({});
20476
20903
  state.promptDragged = false;
20477
- state.lastInstruction = "";
20478
- state.selectionId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
20479
- state.chatHistory = [];
20480
- state.changesHistory = [];
20481
- state.screenshotPromise = Promise.resolve({});
20482
20904
  dom.hoverBox.style.display = "none";
20483
- updateSelectedVisual2();
20905
+ updateSelectedVisual();
20484
20906
  openPromptForSelected(target);
20485
20907
  },
20486
20908
  true