@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/cli/bridge.d.ts +10 -0
- package/dist/cli/bridge.js +59 -44
- package/dist/overlay.js +550 -128
- package/package.json +1 -1
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
|
-
|
|
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
|
|
16845
|
-
if (!
|
|
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
|
-
|
|
16848
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
17766
|
-
if (!
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 (!
|
|
17841
|
-
const next = force === void 0 ? Boolean(
|
|
17842
|
-
|
|
17843
|
-
|
|
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
|
-
|
|
18311
|
+
for (const elementId of state.heldLockElements) {
|
|
18052
18312
|
collabEmit("lock:extend", {
|
|
18053
18313
|
sessionId: state.collabProjectId,
|
|
18054
|
-
elementId
|
|
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.
|
|
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.
|
|
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
|
-
|
|
18163
|
-
const
|
|
18164
|
-
|
|
18165
|
-
|
|
18166
|
-
|
|
18167
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
18917
|
+
agentLogElement.scrollTop = agentLogElement.scrollHeight;
|
|
18641
18918
|
}
|
|
18642
18919
|
if (agentLogToggle) agentLogToggle.hidden = false;
|
|
18643
18920
|
}
|
|
18644
|
-
function
|
|
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
|
|
18647
|
-
|
|
18648
|
-
|
|
18649
|
-
|
|
18650
|
-
role
|
|
18651
|
-
content
|
|
18652
|
-
|
|
18653
|
-
|
|
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,
|
|
18978
|
+
function setAgentStatus(status, message, detail2, taskId2 = state.promptTaskId, appendMessage = true) {
|
|
18681
18979
|
if (!agentStatusElement || !agentStatusMessage || !sendButton) return;
|
|
18682
|
-
const logMessage =
|
|
18980
|
+
const logMessage = detail2?.trim() || message.trim();
|
|
18683
18981
|
const lastLog = agentLogLines[agentLogLines.length - 1];
|
|
18684
|
-
if (agentStatusElement.dataset.status === status && agentStatusMessage.textContent === message && (
|
|
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 =
|
|
18695
|
-
sendButton.classList.toggle("loading",
|
|
18696
|
-
if (stopButton) stopButton.hidden = !
|
|
18697
|
-
if (promptInput) promptInput.disabled =
|
|
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" :
|
|
19000
|
+
label.textContent = status === "review" ? "Review" : status === "error" ? "Retry" : running ? "" : "Send";
|
|
18701
19001
|
}
|
|
18702
|
-
sendButton.setAttribute("aria-label",
|
|
18703
|
-
sendButton.dataset.state = status === "error" ? "retry" :
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
18853
|
-
const
|
|
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(
|
|
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
|
|
18860
|
-
|
|
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
|
|
18866
|
-
changesHistory
|
|
18867
|
-
model:
|
|
18868
|
-
provider:
|
|
19238
|
+
messages,
|
|
19239
|
+
changesHistory,
|
|
19240
|
+
model: model.id,
|
|
19241
|
+
provider: model.provider,
|
|
18869
19242
|
context: {
|
|
18870
|
-
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:
|
|
18902
|
-
group: getElementGroup(
|
|
18903
|
-
label: getElementLabel(
|
|
18904
|
-
html:
|
|
18905
|
-
sourceHint:
|
|
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
|
-
|
|
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
|
|
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
|
|
20087
|
+
let badge2 = "M";
|
|
19715
20088
|
let badgeClass = "mod";
|
|
19716
20089
|
if (code.includes("A")) {
|
|
19717
|
-
|
|
20090
|
+
badge2 = "A";
|
|
19718
20091
|
badgeClass = "add";
|
|
19719
20092
|
} else if (code.includes("D")) {
|
|
19720
|
-
|
|
20093
|
+
badge2 = "D";
|
|
19721
20094
|
badgeClass = "del";
|
|
19722
20095
|
} else if (code.includes("R")) {
|
|
19723
|
-
|
|
20096
|
+
badge2 = "R";
|
|
19724
20097
|
badgeClass = "ren";
|
|
19725
20098
|
} else if (code === "??" || code.includes("?")) {
|
|
19726
|
-
|
|
20099
|
+
badge2 = "U";
|
|
19727
20100
|
badgeClass = "unt";
|
|
19728
20101
|
} else if (code.includes("M")) {
|
|
19729
|
-
|
|
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
|
|
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 &&
|
|
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
|
-
|
|
19971
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20382
|
-
|
|
20383
|
-
|
|
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:
|
|
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
|
-
|
|
20393
|
-
|
|
20394
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20905
|
+
updateSelectedVisual();
|
|
20484
20906
|
openPromptForSelected(target);
|
|
20485
20907
|
},
|
|
20486
20908
|
true
|