@lasso-ai/cli 1.0.12 → 1.0.14
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/agent.d.ts +1 -1
- package/dist/cli/agent.js +74 -35
- package/dist/cli/bridge.d.ts +1 -0
- package/dist/cli/bridge.js +43 -10
- package/dist/overlay.js +240 -57
- package/package.json +1 -1
package/dist/cli/agent.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ export type AgentConfig = {
|
|
|
44
44
|
baseUrl?: string;
|
|
45
45
|
};
|
|
46
46
|
export type LocalAgent = "claude-code" | "codex" | "opencode";
|
|
47
|
-
export type AgentProgress = (message: string) => void;
|
|
47
|
+
export type AgentProgress = (message: string, detail?: string) => void;
|
|
48
48
|
export type AgentAnswer = {
|
|
49
49
|
question: string;
|
|
50
50
|
context?: AgentInput["context"];
|
package/dist/cli/agent.js
CHANGED
|
@@ -27,7 +27,11 @@ async function detectLocalAgents() {
|
|
|
27
27
|
}
|
|
28
28
|
const ignored = new Set(["node_modules", ".git", ".next", "dist", "build", ".turbo"]);
|
|
29
29
|
const sourceExtensions = /\.(tsx?|jsx?|vue|svelte|css|scss|html)$/i;
|
|
30
|
+
const sourceFileCache = new Map();
|
|
30
31
|
async function sourceFiles(directory) {
|
|
32
|
+
const cached = sourceFileCache.get(directory);
|
|
33
|
+
if (cached && cached.expiresAt > Date.now())
|
|
34
|
+
return cached.files;
|
|
31
35
|
const entries = await promises_1.default.readdir(directory, { withFileTypes: true });
|
|
32
36
|
const files = [];
|
|
33
37
|
for (const entry of entries) {
|
|
@@ -38,28 +42,52 @@ async function sourceFiles(directory) {
|
|
|
38
42
|
files.push(...(await sourceFiles(fullPath)));
|
|
39
43
|
else if (sourceExtensions.test(entry.name))
|
|
40
44
|
files.push(fullPath);
|
|
41
|
-
if (files.length >=
|
|
45
|
+
if (files.length >= 40)
|
|
42
46
|
break;
|
|
43
47
|
}
|
|
48
|
+
sourceFileCache.set(directory, { expiresAt: Date.now() + 5000, files });
|
|
44
49
|
return files;
|
|
45
50
|
}
|
|
46
51
|
async function contextFor(cwd, element) {
|
|
52
|
+
const needle = element.label.replace(/^[^.#]+[.#]?/, "");
|
|
53
|
+
const hintedPath = element.sourceHint?.split(":")[0];
|
|
54
|
+
const sourceFile = hintedPath ? node_path_1.default.basename(hintedPath) : "";
|
|
55
|
+
if (hintedPath) {
|
|
56
|
+
const candidates = [
|
|
57
|
+
node_path_1.default.isAbsolute(hintedPath) ? hintedPath : node_path_1.default.resolve(cwd, hintedPath),
|
|
58
|
+
];
|
|
59
|
+
const normalizedHint = hintedPath.replace(/\\/g, "/");
|
|
60
|
+
const srcMarker = "/src/";
|
|
61
|
+
const srcIndex = normalizedHint.lastIndexOf(srcMarker);
|
|
62
|
+
if (srcIndex >= 0)
|
|
63
|
+
candidates.push(node_path_1.default.join(cwd, normalizedHint.slice(srcIndex + 1)));
|
|
64
|
+
for (const candidate of candidates) {
|
|
65
|
+
try {
|
|
66
|
+
const content = await promises_1.default.readFile(candidate, "utf8");
|
|
67
|
+
return `FILE: ${node_path_1.default.relative(cwd, candidate)}\n${content.slice(0, 16000)}`;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// The runtime source hint can point to a different checkout.
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
47
74
|
const files = await sourceFiles(cwd);
|
|
48
|
-
const needle = element.sourceHint || element.label.replace(/^[^.#]+[.#]?/, "");
|
|
49
|
-
const sourceFile = element.sourceHint ? node_path_1.default.basename(element.sourceHint.split(":")[0]) : "";
|
|
50
75
|
const snippets = [];
|
|
51
|
-
|
|
52
|
-
if (snippets.length >= 8)
|
|
53
|
-
break;
|
|
76
|
+
const results = await Promise.all(files.map(async (file) => {
|
|
54
77
|
try {
|
|
55
78
|
const content = await promises_1.default.readFile(file, "utf8");
|
|
56
79
|
if (!needle || (sourceFile && file.endsWith(sourceFile)) || content.includes(needle) || content.includes(element.label)) {
|
|
57
|
-
|
|
80
|
+
return `FILE: ${node_path_1.default.relative(cwd, file)}\n${content.slice(0, 8000)}`;
|
|
58
81
|
}
|
|
59
82
|
}
|
|
60
83
|
catch {
|
|
61
84
|
// A file can disappear while a dev server is rebuilding; skip it.
|
|
62
85
|
}
|
|
86
|
+
return null;
|
|
87
|
+
}));
|
|
88
|
+
for (const result of results) {
|
|
89
|
+
if (result && snippets.length < 6)
|
|
90
|
+
snippets.push(result);
|
|
63
91
|
}
|
|
64
92
|
return snippets.join("\n\n---\n\n");
|
|
65
93
|
}
|
|
@@ -122,86 +150,93 @@ function extractLocalAgentText(raw, provider) {
|
|
|
122
150
|
}
|
|
123
151
|
return texts.at(-1) || raw;
|
|
124
152
|
}
|
|
125
|
-
/** Truncate a detail string to a readable length. */
|
|
126
153
|
function snippet(value, max = 80) {
|
|
127
154
|
const s = String(value ?? "").trim().replace(/\s+/g, " ");
|
|
128
155
|
return s.length > max ? `${s.slice(0, max)}…` : s;
|
|
129
156
|
}
|
|
157
|
+
function progressEvent(message, detail) {
|
|
158
|
+
const value = detail == null ? "" : String(detail).trim();
|
|
159
|
+
return value ? { message, detail: value } : { message };
|
|
160
|
+
}
|
|
130
161
|
function progressFromLine(raw, provider) {
|
|
131
162
|
try {
|
|
132
163
|
const event = JSON.parse(raw);
|
|
133
164
|
const item = event.item;
|
|
134
165
|
if (provider === "claude-code") {
|
|
135
166
|
if (event.type === "system")
|
|
136
|
-
return "Claude Code connected";
|
|
167
|
+
return progressEvent("Claude Code connected");
|
|
137
168
|
// tool_use blocks inside assistant messages
|
|
138
169
|
const toolBlock = event.message?.content?.find?.((p) => p.type === "tool_use");
|
|
139
170
|
if (toolBlock) {
|
|
140
171
|
const toolName = toolBlock.name || "tool";
|
|
141
172
|
const inp = toolBlock.input;
|
|
142
173
|
const detail = inp?.file_path ?? inp?.path ?? inp?.command ?? inp?.query ?? inp?.url ?? "";
|
|
143
|
-
return detail
|
|
174
|
+
return progressEvent(detail
|
|
144
175
|
? `Claude Code · ${toolName} ${snippet(detail)}`
|
|
145
|
-
: `Claude Code · ${toolName}
|
|
176
|
+
: `Claude Code · ${toolName}`, detail);
|
|
146
177
|
}
|
|
147
178
|
// thinking blocks inside assistant messages
|
|
148
179
|
const thinkBlock = event.message?.content?.find?.((p) => p.type === "thinking");
|
|
149
180
|
if (thinkBlock?.thinking) {
|
|
150
|
-
return `Claude Code · ${snippet(thinkBlock.thinking
|
|
181
|
+
return progressEvent(`Claude Code · ${snippet(thinkBlock.thinking)}`, thinkBlock.thinking);
|
|
151
182
|
}
|
|
152
183
|
// top-level tool event fields (stream-json verbose format)
|
|
153
184
|
const tool = event.tool_name || event.name || event.tool?.name;
|
|
154
185
|
if (tool) {
|
|
155
186
|
const inp = event.tool_input;
|
|
156
187
|
const detail = inp?.file_path ?? inp?.path ?? inp?.command ?? inp?.query ?? inp?.url ?? "";
|
|
157
|
-
return detail
|
|
188
|
+
return progressEvent(detail
|
|
158
189
|
? `Claude Code · ${tool} ${snippet(detail)}`
|
|
159
|
-
: `Claude Code · ${tool}
|
|
190
|
+
: `Claude Code · ${tool}`, detail);
|
|
191
|
+
}
|
|
192
|
+
if (event.type === "result" || event.result) {
|
|
193
|
+
return progressEvent("Claude Code · preparing the proposal");
|
|
160
194
|
}
|
|
161
|
-
if (event.type === "result" || event.result)
|
|
162
|
-
return "Claude Code · preparing the proposal";
|
|
163
195
|
if (event.type === "assistant")
|
|
164
|
-
return "Claude Code · reasoning about the change";
|
|
196
|
+
return progressEvent("Claude Code · reasoning about the change");
|
|
165
197
|
}
|
|
166
198
|
else if (provider === "codex") {
|
|
167
199
|
const type = item?.type || event.type || "";
|
|
168
200
|
if (type === "command_execution" || type === "command_execution_output") {
|
|
169
201
|
const cmd = item?.command || event.command || "";
|
|
170
|
-
return cmd ? `Codex · Bash ${snippet(cmd)}` : "Codex · running a command";
|
|
202
|
+
return progressEvent(cmd ? `Codex · Bash ${snippet(cmd)}` : "Codex · running a command", cmd);
|
|
171
203
|
}
|
|
172
204
|
if (type === "file_read" || type === "read_file") {
|
|
173
205
|
const fp = item?.path || event.path || "";
|
|
174
|
-
return fp ? `Codex · Read ${snippet(fp)}` : "Codex · reading a file";
|
|
206
|
+
return progressEvent(fp ? `Codex · Read ${snippet(fp)}` : "Codex · reading a file", fp);
|
|
207
|
+
}
|
|
208
|
+
if (type === "agent_message" || type === "message") {
|
|
209
|
+
return progressEvent("Codex · drafting the proposal");
|
|
175
210
|
}
|
|
176
|
-
if (type === "agent_message" || type === "message")
|
|
177
|
-
return "Codex · drafting the proposal";
|
|
178
211
|
if (type === "reasoning") {
|
|
179
212
|
const text = item?.content || event.content || "";
|
|
180
|
-
return text ? `Codex · ${snippet(text
|
|
213
|
+
return progressEvent(text ? `Codex · ${snippet(text)}` : "Codex · reasoning", text);
|
|
214
|
+
}
|
|
215
|
+
if (type === "turn.started" || type === "turn_start") {
|
|
216
|
+
return progressEvent("Codex · starting a turn");
|
|
217
|
+
}
|
|
218
|
+
if (type === "turn.completed" || type === "turn_complete") {
|
|
219
|
+
return progressEvent("Codex · preparing the proposal");
|
|
181
220
|
}
|
|
182
|
-
if (type === "turn.started" || type === "turn_start")
|
|
183
|
-
return "Codex · starting a turn";
|
|
184
|
-
if (type === "turn.completed" || type === "turn_complete")
|
|
185
|
-
return "Codex · preparing the proposal";
|
|
186
221
|
}
|
|
187
222
|
else if (provider === "opencode") {
|
|
188
223
|
const part = event.part;
|
|
189
224
|
if (event.type === "step-start")
|
|
190
|
-
return "OpenCode · starting a step";
|
|
225
|
+
return progressEvent("OpenCode · starting a step");
|
|
191
226
|
if (part?.type === "tool") {
|
|
192
227
|
const toolName = part.tool || "tool";
|
|
193
228
|
const inp = part.input;
|
|
194
229
|
const detail = inp?.file_path ?? inp?.path ?? inp?.command ?? inp?.query ?? inp?.url ?? "";
|
|
195
|
-
return detail
|
|
230
|
+
return progressEvent(detail
|
|
196
231
|
? `OpenCode · ${toolName} ${snippet(detail)}`
|
|
197
|
-
: `OpenCode · ${toolName}
|
|
232
|
+
: `OpenCode · ${toolName}`, detail);
|
|
198
233
|
}
|
|
199
234
|
if (part?.type === "text") {
|
|
200
235
|
const text = part.text || "";
|
|
201
|
-
return text ? `OpenCode · ${snippet(text
|
|
236
|
+
return progressEvent(text ? `OpenCode · ${snippet(text)}` : "OpenCode · drafting the response", text);
|
|
202
237
|
}
|
|
203
238
|
if (event.type === "step-finish")
|
|
204
|
-
return "OpenCode · finalizing the response";
|
|
239
|
+
return progressEvent("OpenCode · finalizing the response");
|
|
205
240
|
}
|
|
206
241
|
}
|
|
207
242
|
catch {
|
|
@@ -253,7 +288,7 @@ async function proposeWithLocalAgent(cwd, instruction, context, config, signal,
|
|
|
253
288
|
stdout += `${line}\n`;
|
|
254
289
|
const progress = progressFromLine(line, config.provider);
|
|
255
290
|
if (progress)
|
|
256
|
-
onProgress?.(progress);
|
|
291
|
+
onProgress?.(progress.message, progress.detail);
|
|
257
292
|
}
|
|
258
293
|
};
|
|
259
294
|
child.stdout.on("data", consume);
|
|
@@ -273,7 +308,7 @@ async function proposeWithLocalAgent(cwd, instruction, context, config, signal,
|
|
|
273
308
|
stdout += pending;
|
|
274
309
|
const progress = progressFromLine(pending, config.provider);
|
|
275
310
|
if (progress)
|
|
276
|
-
onProgress?.(progress);
|
|
311
|
+
onProgress?.(progress.message, progress.detail);
|
|
277
312
|
}
|
|
278
313
|
if (exitCode !== 0)
|
|
279
314
|
throw new Error(localAgentError(command, stderr, exitCode));
|
|
@@ -360,7 +395,7 @@ async function answerQuestion(cwd, input, config, signal, onProgress) {
|
|
|
360
395
|
output += `${line}\n`;
|
|
361
396
|
const progress = progressFromLine(line, config.provider);
|
|
362
397
|
if (progress)
|
|
363
|
-
onProgress?.(progress);
|
|
398
|
+
onProgress?.(progress.message, progress.detail);
|
|
364
399
|
}
|
|
365
400
|
};
|
|
366
401
|
child.stdout.on("data", consume);
|
|
@@ -371,8 +406,12 @@ async function answerQuestion(cwd, input, config, signal, onProgress) {
|
|
|
371
406
|
child.once("error", reject);
|
|
372
407
|
child.once("close", (status) => resolve(status ?? 1));
|
|
373
408
|
});
|
|
374
|
-
if (pending.trim())
|
|
409
|
+
if (pending.trim()) {
|
|
375
410
|
output += pending;
|
|
411
|
+
const progress = progressFromLine(pending, config.provider);
|
|
412
|
+
if (progress)
|
|
413
|
+
onProgress?.(progress.message, progress.detail);
|
|
414
|
+
}
|
|
376
415
|
if (code !== 0)
|
|
377
416
|
throw new Error(localAgentError(command, stderr, code));
|
|
378
417
|
return extractLocalAgentText(output, config.provider).trim();
|
package/dist/cli/bridge.d.ts
CHANGED
package/dist/cli/bridge.js
CHANGED
|
@@ -52,6 +52,24 @@ function fileLineEnding(content) {
|
|
|
52
52
|
function withLineEnding(value, lineEnding) {
|
|
53
53
|
return value.replace(/\r\n?|\n/g, lineEnding);
|
|
54
54
|
}
|
|
55
|
+
function resolveProposedFile(cwd, proposedPath) {
|
|
56
|
+
const root = node_path_1.default.resolve(cwd);
|
|
57
|
+
const candidate = node_path_1.default.resolve(root, proposedPath);
|
|
58
|
+
if (candidate.startsWith(`${root}${node_path_1.default.sep}`) && node_fs_1.default.existsSync(candidate))
|
|
59
|
+
return candidate;
|
|
60
|
+
// Some React/Next source maps contain an absolute path from an older
|
|
61
|
+
// checkout. Rebase only the recognizable src/... suffix into this project.
|
|
62
|
+
const normalized = proposedPath.replace(/\\/g, "/");
|
|
63
|
+
const srcIndex = normalized.lastIndexOf("/src/");
|
|
64
|
+
if (srcIndex >= 0) {
|
|
65
|
+
const rebased = node_path_1.default.join(root, normalized.slice(srcIndex + 1));
|
|
66
|
+
if (rebased.startsWith(`${root}${node_path_1.default.sep}`) && node_fs_1.default.existsSync(rebased))
|
|
67
|
+
return rebased;
|
|
68
|
+
}
|
|
69
|
+
if (!candidate.startsWith(`${root}${node_path_1.default.sep}`))
|
|
70
|
+
throw new Error(`The proposed file is outside the current project: ${proposedPath}`);
|
|
71
|
+
throw new Error(`Could not find the proposed file in the current project: ${proposedPath}`);
|
|
72
|
+
}
|
|
55
73
|
function occurrenceCount(content, needle) {
|
|
56
74
|
if (!needle)
|
|
57
75
|
return 0;
|
|
@@ -65,13 +83,30 @@ function occurrenceCount(content, needle) {
|
|
|
65
83
|
from = index + needle.length;
|
|
66
84
|
}
|
|
67
85
|
}
|
|
86
|
+
function escapeRegExp(value) {
|
|
87
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
88
|
+
}
|
|
89
|
+
function whitespaceEquivalentRange(content, oldString) {
|
|
90
|
+
const trimmed = oldString.trim();
|
|
91
|
+
if (!trimmed)
|
|
92
|
+
return null;
|
|
93
|
+
const pattern = trimmed.split(/\s+/).map(escapeRegExp).join("\\s+");
|
|
94
|
+
const matches = Array.from(content.matchAll(new RegExp(pattern, "g")));
|
|
95
|
+
if (matches.length !== 1 || matches[0].index === undefined)
|
|
96
|
+
return null;
|
|
97
|
+
const start = matches[0].index;
|
|
98
|
+
return { start, end: start + matches[0][0].length };
|
|
99
|
+
}
|
|
68
100
|
function prepareChange(content, change) {
|
|
69
101
|
const lineEnding = fileLineEnding(content);
|
|
70
102
|
const oldString = withLineEnding(change.oldString, lineEnding);
|
|
71
103
|
const newString = withLineEnding(change.newString, lineEnding);
|
|
72
104
|
const matches = occurrenceCount(content, oldString);
|
|
73
105
|
if (matches === 0) {
|
|
74
|
-
|
|
106
|
+
const range = whitespaceEquivalentRange(content, oldString);
|
|
107
|
+
if (range)
|
|
108
|
+
return { ...range, oldString: content.slice(range.start, range.end), newString };
|
|
109
|
+
throw new Error(`Could not safely apply ${change.filePath}. The source changed after the suggestion was generated. Regenerate the review so it uses the current source.`);
|
|
75
110
|
}
|
|
76
111
|
if (matches > 1) {
|
|
77
112
|
throw new Error(`Could not safely apply ${change.filePath}. The selected code is not unique (${matches} matches).`);
|
|
@@ -244,9 +279,9 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
244
279
|
const selectedConfig = localProvider
|
|
245
280
|
? { provider: cliProvider, model: msg.model }
|
|
246
281
|
: { ...agentConfig, provider: (msg.provider || agentConfig.provider), model: msg.model };
|
|
247
|
-
void (0, agent_1.answerQuestion)(cwd, { question: msg.question, context: msg.context, element: msg.element, messages: msg.messages }, selectedConfig, controller.signal, (message) => {
|
|
282
|
+
void (0, agent_1.answerQuestion)(cwd, { question: msg.question, context: msg.context, element: msg.element, messages: msg.messages }, selectedConfig, controller.signal, (message, detail) => {
|
|
248
283
|
if (!controller.signal.aborted && socket.readyState === socket.OPEN)
|
|
249
|
-
socket.send(JSON.stringify({ type: "agent_status", status: "working", message }));
|
|
284
|
+
socket.send(JSON.stringify({ type: "agent_status", status: "working", message, detail }));
|
|
250
285
|
}).then((answer) => {
|
|
251
286
|
if (!controller.signal.aborted)
|
|
252
287
|
socket.send(JSON.stringify({ type: "assistant_message", message: answer }));
|
|
@@ -275,9 +310,9 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
275
310
|
const selectedConfig = localProvider
|
|
276
311
|
? { provider: cliProvider, model: msg.model }
|
|
277
312
|
: { ...agentConfig, provider: (msg.provider || agentConfig.provider), model: msg.model };
|
|
278
|
-
void (0, agent_1.proposeChanges)(cwd, msg, selectedConfig, controller.signal, (message) => {
|
|
313
|
+
void (0, agent_1.proposeChanges)(cwd, msg, selectedConfig, controller.signal, (message, detail) => {
|
|
279
314
|
if (!controller.signal.aborted && socket.readyState === socket.OPEN) {
|
|
280
|
-
socket.send(JSON.stringify({ type: "agent_status", status: "working", message }));
|
|
315
|
+
socket.send(JSON.stringify({ type: "agent_status", status: "working", message, detail }));
|
|
281
316
|
}
|
|
282
317
|
})
|
|
283
318
|
.then((proposal) => {
|
|
@@ -320,9 +355,9 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
320
355
|
activeAgentController?.abort();
|
|
321
356
|
activeAgentController = controller;
|
|
322
357
|
socket.send(JSON.stringify({ type: "agent_status", status: "thinking", message: "Generating a commit message…" }));
|
|
323
|
-
void getGitState(cwd).then((git) => (0, agent_1.generateCommitMessage)(cwd, git.status || [], selectedConfig, controller.signal, (message) => {
|
|
358
|
+
void getGitState(cwd).then((git) => (0, agent_1.generateCommitMessage)(cwd, git.status || [], selectedConfig, controller.signal, (message, detail) => {
|
|
324
359
|
if (!controller.signal.aborted && socket.readyState === socket.OPEN)
|
|
325
|
-
socket.send(JSON.stringify({ type: "agent_status", status: "working", message }));
|
|
360
|
+
socket.send(JSON.stringify({ type: "agent_status", status: "working", message, detail }));
|
|
326
361
|
})).then((message) => {
|
|
327
362
|
if (!controller.signal.aborted)
|
|
328
363
|
socket.send(JSON.stringify({ type: "git_commit_message", message }));
|
|
@@ -402,9 +437,7 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
402
437
|
lastSnapshot = [];
|
|
403
438
|
const planned = new Map();
|
|
404
439
|
for (const change of msg.changes) {
|
|
405
|
-
const filePath =
|
|
406
|
-
if (!filePath.startsWith(`${node_path_1.default.resolve(cwd)}${node_path_1.default.sep}`))
|
|
407
|
-
throw new Error("A proposed file was outside the project.");
|
|
440
|
+
const filePath = resolveProposedFile(cwd, change.filePath);
|
|
408
441
|
const content = node_fs_1.default.readFileSync(filePath, "utf8");
|
|
409
442
|
const prepared = prepareChange(content, change);
|
|
410
443
|
const fileChanges = planned.get(filePath) || [];
|
package/dist/overlay.js
CHANGED
|
@@ -9830,6 +9830,16 @@
|
|
|
9830
9830
|
background: rgba(242, 139, 130, 0.12);
|
|
9831
9831
|
}
|
|
9832
9832
|
|
|
9833
|
+
.lasso-chat-message code {
|
|
9834
|
+
padding: 1px 4px;
|
|
9835
|
+
border-radius: 3px;
|
|
9836
|
+
background: var(--lo-surface-2);
|
|
9837
|
+
color: #a5b4fc;
|
|
9838
|
+
font-family: var(--lo-font-mono);
|
|
9839
|
+
font-size: 0.92em;
|
|
9840
|
+
white-space: pre-wrap;
|
|
9841
|
+
}
|
|
9842
|
+
|
|
9833
9843
|
/* CLI / Terminal Thinking Mode */
|
|
9834
9844
|
.lasso-agent-status {
|
|
9835
9845
|
display: flex;
|
|
@@ -9906,6 +9916,43 @@
|
|
|
9906
9916
|
color: #f87171;
|
|
9907
9917
|
}
|
|
9908
9918
|
|
|
9919
|
+
.lasso-agent-status-badge.complete {
|
|
9920
|
+
background: rgba(16, 185, 129, 0.16);
|
|
9921
|
+
color: #34d399;
|
|
9922
|
+
}
|
|
9923
|
+
|
|
9924
|
+
.lasso-agent-log-toggle {
|
|
9925
|
+
display: inline-flex;
|
|
9926
|
+
align-items: center;
|
|
9927
|
+
gap: 3px;
|
|
9928
|
+
padding: 2px 5px;
|
|
9929
|
+
border: 0;
|
|
9930
|
+
border-radius: 4px;
|
|
9931
|
+
background: transparent;
|
|
9932
|
+
color: #64748b;
|
|
9933
|
+
font: inherit;
|
|
9934
|
+
font-size: 9.5px;
|
|
9935
|
+
cursor: pointer;
|
|
9936
|
+
}
|
|
9937
|
+
|
|
9938
|
+
.lasso-agent-log-toggle:hover {
|
|
9939
|
+
background: rgba(255, 255, 255, 0.06);
|
|
9940
|
+
color: #cbd5e1;
|
|
9941
|
+
}
|
|
9942
|
+
|
|
9943
|
+
.lasso-agent-log-toggle[hidden] {
|
|
9944
|
+
display: none;
|
|
9945
|
+
}
|
|
9946
|
+
|
|
9947
|
+
.lasso-agent-log-chevron {
|
|
9948
|
+
display: inline-block;
|
|
9949
|
+
transition: transform 140ms ease;
|
|
9950
|
+
}
|
|
9951
|
+
|
|
9952
|
+
.lasso-agent-log-toggle[aria-expanded="true"] .lasso-agent-log-chevron {
|
|
9953
|
+
transform: rotate(180deg);
|
|
9954
|
+
}
|
|
9955
|
+
|
|
9909
9956
|
.lasso-agent-status-line {
|
|
9910
9957
|
display: flex;
|
|
9911
9958
|
align-items: center;
|
|
@@ -9921,13 +9968,52 @@
|
|
|
9921
9968
|
}
|
|
9922
9969
|
|
|
9923
9970
|
.lasso-agent-status-message {
|
|
9971
|
+
flex: 1;
|
|
9972
|
+
min-width: 0;
|
|
9924
9973
|
overflow: hidden;
|
|
9925
9974
|
text-overflow: ellipsis;
|
|
9926
9975
|
white-space: nowrap;
|
|
9927
9976
|
}
|
|
9928
9977
|
|
|
9929
|
-
/* The status line is the single latest activity message. */
|
|
9930
9978
|
.lasso-agent-log {
|
|
9979
|
+
display: flex;
|
|
9980
|
+
flex-direction: column;
|
|
9981
|
+
gap: 5px;
|
|
9982
|
+
max-height: 180px;
|
|
9983
|
+
margin-top: 8px;
|
|
9984
|
+
padding-top: 7px;
|
|
9985
|
+
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
|
9986
|
+
overflow: auto;
|
|
9987
|
+
color: #64748b;
|
|
9988
|
+
font-size: 10px;
|
|
9989
|
+
}
|
|
9990
|
+
|
|
9991
|
+
.lasso-agent-log[hidden] {
|
|
9992
|
+
display: none;
|
|
9993
|
+
}
|
|
9994
|
+
|
|
9995
|
+
.lasso-agent-log-line {
|
|
9996
|
+
display: flex;
|
|
9997
|
+
gap: 5px;
|
|
9998
|
+
line-height: 1.45;
|
|
9999
|
+
}
|
|
10000
|
+
|
|
10001
|
+
.lasso-agent-log-prefix {
|
|
10002
|
+
flex: 0 0 auto;
|
|
10003
|
+
color: #475569;
|
|
10004
|
+
}
|
|
10005
|
+
|
|
10006
|
+
.lasso-agent-log-text {
|
|
10007
|
+
min-width: 0;
|
|
10008
|
+
white-space: pre-wrap;
|
|
10009
|
+
overflow-wrap: anywhere;
|
|
10010
|
+
}
|
|
10011
|
+
|
|
10012
|
+
.lasso-agent-status[data-status="complete"] .lasso-agent-status-line {
|
|
10013
|
+
animation: none;
|
|
10014
|
+
}
|
|
10015
|
+
|
|
10016
|
+
.lasso-agent-status[data-status="complete"] .lasso-agent-cursor {
|
|
9931
10017
|
display: none;
|
|
9932
10018
|
}
|
|
9933
10019
|
|
|
@@ -9945,40 +10031,11 @@
|
|
|
9945
10031
|
50% { opacity: 0; }
|
|
9946
10032
|
}
|
|
9947
10033
|
|
|
9948
|
-
.lasso-agent-log {
|
|
9949
|
-
display: flex;
|
|
9950
|
-
flex-direction: column;
|
|
9951
|
-
gap: 2px;
|
|
9952
|
-
margin-top: 6px;
|
|
9953
|
-
max-height: 80px;
|
|
9954
|
-
overflow-y: auto;
|
|
9955
|
-
font-size: 10px;
|
|
9956
|
-
color: #64748b;
|
|
9957
|
-
}
|
|
9958
|
-
|
|
9959
|
-
.lasso-agent-log-line {
|
|
9960
|
-
display: flex;
|
|
9961
|
-
gap: 5px;
|
|
9962
|
-
overflow: hidden;
|
|
9963
|
-
text-overflow: ellipsis;
|
|
9964
|
-
white-space: nowrap;
|
|
9965
|
-
animation: lasso-agent-log-in 180ms ease-out both;
|
|
9966
|
-
}
|
|
9967
|
-
|
|
9968
|
-
.lasso-agent-log-prefix {
|
|
9969
|
-
color: #475569;
|
|
9970
|
-
}
|
|
9971
|
-
|
|
9972
10034
|
@keyframes lasso-agent-line-pulse {
|
|
9973
10035
|
0%, 100% { opacity: .72; }
|
|
9974
10036
|
50% { opacity: 1; }
|
|
9975
10037
|
}
|
|
9976
10038
|
|
|
9977
|
-
@keyframes lasso-agent-log-in {
|
|
9978
|
-
from { opacity: 0; transform: translateY(3px); }
|
|
9979
|
-
to { opacity: 1; transform: translateY(0); }
|
|
9980
|
-
}
|
|
9981
|
-
|
|
9982
10039
|
.lasso-prompt-actions {
|
|
9983
10040
|
display: flex;
|
|
9984
10041
|
align-items: center;
|
|
@@ -12002,6 +12059,30 @@
|
|
|
12002
12059
|
};
|
|
12003
12060
|
var terminal_default = data4;
|
|
12004
12061
|
|
|
12062
|
+
// src/overlay/notifications.ts
|
|
12063
|
+
var permissionRequest = null;
|
|
12064
|
+
function requestAgentNotificationPermission() {
|
|
12065
|
+
if (typeof window === "undefined" || !("Notification" in window)) return;
|
|
12066
|
+
if (Notification.permission !== "default" || permissionRequest) return;
|
|
12067
|
+
permissionRequest = Notification.requestPermission().finally(() => {
|
|
12068
|
+
permissionRequest = null;
|
|
12069
|
+
});
|
|
12070
|
+
}
|
|
12071
|
+
async function notifyAgent(title, body) {
|
|
12072
|
+
if (typeof window === "undefined" || !("Notification" in window)) return;
|
|
12073
|
+
let permission = Notification.permission;
|
|
12074
|
+
if (permission === "default") permission = await (permissionRequest || Notification.requestPermission());
|
|
12075
|
+
if (permission !== "granted") return;
|
|
12076
|
+
try {
|
|
12077
|
+
const notification = new Notification(title, { body: body.slice(0, 240) });
|
|
12078
|
+
notification.onclick = () => {
|
|
12079
|
+
window.focus();
|
|
12080
|
+
notification.close();
|
|
12081
|
+
};
|
|
12082
|
+
} catch {
|
|
12083
|
+
}
|
|
12084
|
+
}
|
|
12085
|
+
|
|
12005
12086
|
// node_modules/.pnpm/engine.io-parser@5.2.3/node_modules/engine.io-parser/build/esm/commons.js
|
|
12006
12087
|
var PACKET_TYPES = /* @__PURE__ */ Object.create(null);
|
|
12007
12088
|
PACKET_TYPES["open"] = "0";
|
|
@@ -18095,6 +18176,9 @@
|
|
|
18095
18176
|
var agentStatusElement = null;
|
|
18096
18177
|
var agentStatusMessage = null;
|
|
18097
18178
|
var agentLogElement = null;
|
|
18179
|
+
var agentLogToggle = null;
|
|
18180
|
+
var agentLogLines = [];
|
|
18181
|
+
var agentLogExpanded = false;
|
|
18098
18182
|
var reviewPanel = null;
|
|
18099
18183
|
var modelBtn = null;
|
|
18100
18184
|
var modelName = null;
|
|
@@ -18153,13 +18237,17 @@
|
|
|
18153
18237
|
</div>
|
|
18154
18238
|
<span class="lasso-agent-status-kicker">lasso-agent</span>
|
|
18155
18239
|
<span class="lasso-agent-status-badge">active</span>
|
|
18240
|
+
<button class="lasso-agent-log-toggle" type="button" aria-label="Show agent activity" aria-expanded="false" hidden>
|
|
18241
|
+
<span>Activity</span>
|
|
18242
|
+
<span class="lasso-agent-log-chevron">\u2304</span>
|
|
18243
|
+
</button>
|
|
18156
18244
|
</div>
|
|
18157
18245
|
<div class="lasso-agent-status-line">
|
|
18158
18246
|
<span class="lasso-agent-terminal-prompt">\u276F</span>
|
|
18159
18247
|
<span class="lasso-agent-status-message"></span>
|
|
18160
18248
|
<span class="lasso-agent-cursor"></span>
|
|
18161
18249
|
</div>
|
|
18162
|
-
<div class="lasso-agent-log" aria-label="Agent activity"></div>
|
|
18250
|
+
<div class="lasso-agent-log" aria-label="Agent activity" hidden></div>
|
|
18163
18251
|
</div>
|
|
18164
18252
|
|
|
18165
18253
|
<textarea class="lasso-prompt-input" placeholder="Ask anything about this element\u2026" rows="1"></textarea>
|
|
@@ -18213,9 +18301,15 @@
|
|
|
18213
18301
|
agentStatusElement = el.querySelector(".lasso-agent-status");
|
|
18214
18302
|
agentStatusMessage = el.querySelector(".lasso-agent-status-message");
|
|
18215
18303
|
agentLogElement = el.querySelector(".lasso-agent-log");
|
|
18304
|
+
agentLogToggle = el.querySelector(".lasso-agent-log-toggle");
|
|
18216
18305
|
modelBtn = el.querySelector(".lasso-prompt-model");
|
|
18217
18306
|
modelName = el.querySelector(".lasso-prompt-model-name");
|
|
18218
18307
|
modelMenu = el.querySelector(".lasso-prompt-model-menu");
|
|
18308
|
+
agentLogToggle.addEventListener("click", (event) => {
|
|
18309
|
+
event.preventDefault();
|
|
18310
|
+
event.stopPropagation();
|
|
18311
|
+
setAgentLogExpanded(!agentLogExpanded);
|
|
18312
|
+
});
|
|
18219
18313
|
const rev = document.createElement("div");
|
|
18220
18314
|
rev.className = "lasso-review";
|
|
18221
18315
|
rev.hidden = true;
|
|
@@ -18367,6 +18461,7 @@
|
|
|
18367
18461
|
});
|
|
18368
18462
|
rev.querySelector(".lasso-review-apply").addEventListener("click", () => {
|
|
18369
18463
|
if (!state.bridgeSocket || state.bridgeSocket.readyState !== WebSocket.OPEN || !state.pendingChanges.length) return;
|
|
18464
|
+
requestAgentNotificationPermission();
|
|
18370
18465
|
state.bridgeSocket.send(JSON.stringify({ type: "apply", changes: state.pendingChanges }));
|
|
18371
18466
|
appendChat("assistant", "Applying the reviewed change\u2026");
|
|
18372
18467
|
});
|
|
@@ -18478,11 +18573,74 @@
|
|
|
18478
18573
|
return {};
|
|
18479
18574
|
}
|
|
18480
18575
|
}
|
|
18576
|
+
async function captureElementScreenshot(el) {
|
|
18577
|
+
const options = {
|
|
18578
|
+
backgroundColor: null,
|
|
18579
|
+
useCORS: true,
|
|
18580
|
+
logging: false,
|
|
18581
|
+
scale: Math.min(window.devicePixelRatio || 1, 1),
|
|
18582
|
+
ignoreElements: (node) => node.id === "lasso-root" || Boolean(node.closest?.("#lasso-root"))
|
|
18583
|
+
};
|
|
18584
|
+
try {
|
|
18585
|
+
const canvas = await (0, import_html2canvas.default)(el, options);
|
|
18586
|
+
return { element: canvas.toDataURL("image/jpeg", 0.78) };
|
|
18587
|
+
} catch (error) {
|
|
18588
|
+
console.warn("[lasso] element screenshot unavailable", error);
|
|
18589
|
+
return {};
|
|
18590
|
+
}
|
|
18591
|
+
}
|
|
18592
|
+
function setAgentLogExpanded(expanded) {
|
|
18593
|
+
agentLogExpanded = expanded && agentLogLines.length > 0;
|
|
18594
|
+
if (agentLogElement) agentLogElement.hidden = !agentLogExpanded;
|
|
18595
|
+
if (agentLogToggle) {
|
|
18596
|
+
agentLogToggle.hidden = agentLogLines.length === 0;
|
|
18597
|
+
agentLogToggle.setAttribute("aria-expanded", String(agentLogExpanded));
|
|
18598
|
+
agentLogToggle.setAttribute("aria-label", agentLogExpanded ? "Hide agent activity" : "Show agent activity");
|
|
18599
|
+
}
|
|
18600
|
+
}
|
|
18601
|
+
function clearAgentLog() {
|
|
18602
|
+
agentLogLines = [];
|
|
18603
|
+
agentLogExpanded = false;
|
|
18604
|
+
if (agentLogElement) {
|
|
18605
|
+
agentLogElement.replaceChildren();
|
|
18606
|
+
agentLogElement.hidden = true;
|
|
18607
|
+
}
|
|
18608
|
+
if (agentLogToggle) {
|
|
18609
|
+
agentLogToggle.hidden = true;
|
|
18610
|
+
agentLogToggle.setAttribute("aria-expanded", "false");
|
|
18611
|
+
agentLogToggle.setAttribute("aria-label", "Show agent activity");
|
|
18612
|
+
}
|
|
18613
|
+
if (agentStatusElement) {
|
|
18614
|
+
agentStatusElement.hidden = true;
|
|
18615
|
+
agentStatusElement.dataset.status = "idle";
|
|
18616
|
+
}
|
|
18617
|
+
}
|
|
18618
|
+
function appendAgentLog(message) {
|
|
18619
|
+
const value2 = message.trim();
|
|
18620
|
+
if (!value2) return;
|
|
18621
|
+
const previous = agentLogLines[agentLogLines.length - 1];
|
|
18622
|
+
if (previous === value2) return;
|
|
18623
|
+
agentLogLines.push(value2);
|
|
18624
|
+
if (agentLogElement) {
|
|
18625
|
+
const line = document.createElement("div");
|
|
18626
|
+
line.className = "lasso-agent-log-line";
|
|
18627
|
+
const prefix = document.createElement("span");
|
|
18628
|
+
prefix.className = "lasso-agent-log-prefix";
|
|
18629
|
+
prefix.textContent = "\u203A";
|
|
18630
|
+
const text = document.createElement("span");
|
|
18631
|
+
text.className = "lasso-agent-log-text";
|
|
18632
|
+
text.textContent = value2;
|
|
18633
|
+
line.append(prefix, text);
|
|
18634
|
+
agentLogElement.append(line);
|
|
18635
|
+
if (agentLogExpanded) agentLogElement.scrollTop = agentLogElement.scrollHeight;
|
|
18636
|
+
}
|
|
18637
|
+
if (agentLogToggle) agentLogToggle.hidden = false;
|
|
18638
|
+
}
|
|
18481
18639
|
function appendChat(role, text) {
|
|
18482
18640
|
const thread = promptEl?.querySelector(".lasso-chat-thread");
|
|
18483
18641
|
if (!thread || !text.trim()) return;
|
|
18484
18642
|
const previous = thread.lastElementChild;
|
|
18485
|
-
if (previous?.
|
|
18643
|
+
if (previous?.classList.contains(role) && previous.dataset.rawText === text) return;
|
|
18486
18644
|
state.chatHistory.push({
|
|
18487
18645
|
role,
|
|
18488
18646
|
content: text,
|
|
@@ -18491,15 +18649,36 @@
|
|
|
18491
18649
|
});
|
|
18492
18650
|
const item = document.createElement("div");
|
|
18493
18651
|
item.className = `lasso-chat-message ${role}`;
|
|
18494
|
-
item.
|
|
18652
|
+
item.dataset.rawText = text;
|
|
18653
|
+
renderChatText(item, text);
|
|
18495
18654
|
thread.appendChild(item);
|
|
18496
18655
|
while (thread.children.length > 6) thread.firstElementChild?.remove();
|
|
18497
18656
|
thread.scrollTop = thread.scrollHeight;
|
|
18498
18657
|
}
|
|
18499
|
-
function
|
|
18658
|
+
function renderChatText(container, text) {
|
|
18659
|
+
const codePattern = /(`+)([\s\S]*?)\1/g;
|
|
18660
|
+
let lastIndex = 0;
|
|
18661
|
+
for (const match of text.matchAll(codePattern)) {
|
|
18662
|
+
const index = match.index ?? 0;
|
|
18663
|
+
if (index > lastIndex) {
|
|
18664
|
+
container.append(document.createTextNode(text.slice(lastIndex, index)));
|
|
18665
|
+
}
|
|
18666
|
+
const code = document.createElement("code");
|
|
18667
|
+
code.textContent = match[2] ?? "";
|
|
18668
|
+
container.append(code);
|
|
18669
|
+
lastIndex = index + match[0].length;
|
|
18670
|
+
}
|
|
18671
|
+
if (lastIndex < text.length) {
|
|
18672
|
+
container.append(document.createTextNode(text.slice(lastIndex)));
|
|
18673
|
+
}
|
|
18674
|
+
}
|
|
18675
|
+
function setAgentStatus(status, message, detail) {
|
|
18500
18676
|
if (!agentStatusElement || !agentStatusMessage || !sendButton) return;
|
|
18501
|
-
|
|
18502
|
-
|
|
18677
|
+
const logMessage = detail?.trim() || message.trim();
|
|
18678
|
+
const lastLog = agentLogLines[agentLogLines.length - 1];
|
|
18679
|
+
if (agentStatusElement.dataset.status === status && agentStatusMessage.textContent === message && (status === "thinking" || status === "working") && (!logMessage || lastLog === logMessage)) return;
|
|
18680
|
+
appendAgentLog(logMessage);
|
|
18681
|
+
agentStatusElement.hidden = status === "review";
|
|
18503
18682
|
agentStatusElement.dataset.status = status;
|
|
18504
18683
|
agentStatusMessage.textContent = message;
|
|
18505
18684
|
const badgeEl = agentStatusElement.querySelector(".lasso-agent-status-badge");
|
|
@@ -18508,12 +18687,6 @@
|
|
|
18508
18687
|
badgeEl.className = `lasso-agent-status-badge ${status}`;
|
|
18509
18688
|
}
|
|
18510
18689
|
state.agentRunning = status === "thinking" || status === "working";
|
|
18511
|
-
if (state.agentRunning && agentLogElement) {
|
|
18512
|
-
const line = document.createElement("div");
|
|
18513
|
-
line.className = "lasso-agent-log-line";
|
|
18514
|
-
line.innerHTML = `<span class="lasso-agent-log-prefix">\u203A</span> <span class="lasso-agent-log-text">${escapeHtml4(message)}</span>`;
|
|
18515
|
-
agentLogElement.replaceChildren(line);
|
|
18516
|
-
}
|
|
18517
18690
|
sendButton.classList.toggle("loading", state.agentRunning);
|
|
18518
18691
|
if (stopButton) stopButton.hidden = !state.agentRunning;
|
|
18519
18692
|
if (promptInput) promptInput.disabled = state.agentRunning;
|
|
@@ -18528,11 +18701,6 @@
|
|
|
18528
18701
|
appendChat(status === "error" ? "error" : "assistant", message);
|
|
18529
18702
|
}
|
|
18530
18703
|
}
|
|
18531
|
-
function escapeHtml4(text) {
|
|
18532
|
-
const div = document.createElement("div");
|
|
18533
|
-
div.textContent = text;
|
|
18534
|
-
return div.innerHTML;
|
|
18535
|
-
}
|
|
18536
18704
|
function syncSendButtonState() {
|
|
18537
18705
|
if (!promptInput || !sendButton) return;
|
|
18538
18706
|
const hasInstruction = Boolean(promptInput.value.trim()) || sendButton.dataset.state === "retry" && Boolean(state.lastInstruction?.trim());
|
|
@@ -18541,11 +18709,19 @@
|
|
|
18541
18709
|
}
|
|
18542
18710
|
function resetAgentState() {
|
|
18543
18711
|
state.agentRunning = false;
|
|
18712
|
+
const hasLog = agentLogLines.length > 0;
|
|
18544
18713
|
if (agentStatusElement) {
|
|
18545
|
-
agentStatusElement.hidden =
|
|
18546
|
-
agentStatusElement.dataset.status = "idle";
|
|
18714
|
+
agentStatusElement.hidden = !hasLog;
|
|
18715
|
+
agentStatusElement.dataset.status = hasLog ? "complete" : "idle";
|
|
18716
|
+
}
|
|
18717
|
+
if (hasLog && agentStatusMessage) agentStatusMessage.textContent = "Agent complete";
|
|
18718
|
+
if (hasLog) {
|
|
18719
|
+
const badgeEl = agentStatusElement?.querySelector(".lasso-agent-status-badge");
|
|
18720
|
+
if (badgeEl) {
|
|
18721
|
+
badgeEl.textContent = "done";
|
|
18722
|
+
badgeEl.className = "lasso-agent-status-badge complete";
|
|
18723
|
+
}
|
|
18547
18724
|
}
|
|
18548
|
-
if (agentLogElement) agentLogElement.replaceChildren();
|
|
18549
18725
|
if (sendButton) {
|
|
18550
18726
|
sendButton.classList.remove("loading");
|
|
18551
18727
|
sendButton.dataset.state = "idle";
|
|
@@ -18641,6 +18817,8 @@
|
|
|
18641
18817
|
promptInput.focus();
|
|
18642
18818
|
return;
|
|
18643
18819
|
}
|
|
18820
|
+
clearAgentLog();
|
|
18821
|
+
requestAgentNotificationPermission();
|
|
18644
18822
|
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);
|
|
18645
18823
|
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);
|
|
18646
18824
|
const wantsAnswer = isQuestion && !/\b(can|could|would|please)\s+you\s+(change|edit|update|add|fix|make)\b/i.test(instruction) || !isExplicitEdit && !isQuestion;
|
|
@@ -18672,7 +18850,8 @@
|
|
|
18672
18850
|
const attributes = Object.fromEntries(
|
|
18673
18851
|
Array.from(state.selected.attributes).map((attr) => [attr.name, attr.value])
|
|
18674
18852
|
);
|
|
18675
|
-
const
|
|
18853
|
+
const needsVisualContext = /\b(look|visual|appearance|color|colour|background|image|icon|spacing|layout|position|align|responsive|style|restyle|font|size)\b/i.test(instruction);
|
|
18854
|
+
const screenshots = needsVisualContext && state.selected ? await captureElementScreenshot(state.selected) : await state.screenshotPromise;
|
|
18676
18855
|
state.bridgeSocket.send(
|
|
18677
18856
|
JSON.stringify({
|
|
18678
18857
|
type: wantsAnswer ? "ask" : "edit",
|
|
@@ -18725,6 +18904,7 @@
|
|
|
18725
18904
|
}
|
|
18726
18905
|
function openPromptForSelected(selected) {
|
|
18727
18906
|
if (!promptEl || !promptElement) return;
|
|
18907
|
+
clearAgentLog();
|
|
18728
18908
|
promptElement.textContent = getElementLabel(selected);
|
|
18729
18909
|
positionPrompt(selected);
|
|
18730
18910
|
promptEl.classList.add("visible");
|
|
@@ -19547,7 +19727,7 @@
|
|
|
19547
19727
|
return { code, badge, badgeClass, path: filePath, dir, name };
|
|
19548
19728
|
});
|
|
19549
19729
|
}
|
|
19550
|
-
function
|
|
19730
|
+
function escapeHtml4(text) {
|
|
19551
19731
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
19552
19732
|
}
|
|
19553
19733
|
function buildGitPanel() {
|
|
@@ -19802,9 +19982,9 @@
|
|
|
19802
19982
|
(f) => `
|
|
19803
19983
|
<div class="lasso-git-file-row">
|
|
19804
19984
|
<span class="lasso-git-file-badge ${f.badgeClass}" title="${f.code}">${f.badge}</span>
|
|
19805
|
-
<span class="lasso-git-file-path" title="${
|
|
19806
|
-
${f.dir ? `<span class="lasso-git-file-dir">${
|
|
19807
|
-
<span class="lasso-git-file-name">${
|
|
19985
|
+
<span class="lasso-git-file-path" title="${escapeHtml4(f.path)}">
|
|
19986
|
+
${f.dir ? `<span class="lasso-git-file-dir">${escapeHtml4(f.dir)}</span>` : ""}
|
|
19987
|
+
<span class="lasso-git-file-name">${escapeHtml4(f.name)}</span>
|
|
19808
19988
|
</span>
|
|
19809
19989
|
</div>
|
|
19810
19990
|
`
|
|
@@ -20174,9 +20354,10 @@
|
|
|
20174
20354
|
setGeneratedCommitMessage(message.message || "", message.error);
|
|
20175
20355
|
}
|
|
20176
20356
|
if (message.type === "agent_status" && message.status && message.message) {
|
|
20177
|
-
setAgentStatus(message.status, message.message);
|
|
20357
|
+
setAgentStatus(message.status, message.message, message.detail);
|
|
20178
20358
|
setDragCardStatus(message.status, message.message);
|
|
20179
20359
|
if (message.status === "review" && message.changes?.length) {
|
|
20360
|
+
void notifyAgent("Review requested", message.message);
|
|
20180
20361
|
state.pendingChanges = message.changes;
|
|
20181
20362
|
state.changesHistory.push({
|
|
20182
20363
|
summary: message.message,
|
|
@@ -20187,10 +20368,12 @@
|
|
|
20187
20368
|
}
|
|
20188
20369
|
}
|
|
20189
20370
|
if (message.type === "assistant_message" && message.message) {
|
|
20371
|
+
void notifyAgent("Agent complete", message.message);
|
|
20190
20372
|
appendChat("assistant", message.message);
|
|
20191
20373
|
resetAgentState();
|
|
20192
20374
|
}
|
|
20193
20375
|
if (message.type === "applied" || message.type === "undone") {
|
|
20376
|
+
void notifyAgent(message.type === "applied" ? "Changes applied" : "Change undone", message.message || "Done.");
|
|
20194
20377
|
appendChat("assistant", message.message || "Done.");
|
|
20195
20378
|
releaseHeldLock();
|
|
20196
20379
|
if (state.collabSocket?.connected && state.collabJoined) {
|
|
@@ -20290,7 +20473,7 @@
|
|
|
20290
20473
|
state.selectionId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
20291
20474
|
state.chatHistory = [];
|
|
20292
20475
|
state.changesHistory = [];
|
|
20293
|
-
state.screenshotPromise =
|
|
20476
|
+
state.screenshotPromise = Promise.resolve({});
|
|
20294
20477
|
dom.hoverBox.style.display = "none";
|
|
20295
20478
|
updateSelectedVisual2();
|
|
20296
20479
|
openPromptForSelected(target);
|
package/package.json
CHANGED