@lasso-ai/cli 1.0.28 → 1.0.29
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.js +64 -21
- package/dist/cli/bridge.js +2 -2
- package/dist/overlay.js +3 -12
- package/package.json +1 -1
package/dist/cli/agent.js
CHANGED
|
@@ -202,9 +202,36 @@ function extractLocalAgentText(raw, provider) {
|
|
|
202
202
|
}
|
|
203
203
|
return texts.at(-1) || raw;
|
|
204
204
|
}
|
|
205
|
+
function collectTextFields(value, fields = new Set(["text", "output_text", "result", "content", "message"]), depth = 0) {
|
|
206
|
+
if (depth > 8 || value == null)
|
|
207
|
+
return [];
|
|
208
|
+
if (typeof value === "string")
|
|
209
|
+
return [value];
|
|
210
|
+
if (Array.isArray(value))
|
|
211
|
+
return value.flatMap((entry) => collectTextFields(entry, fields, depth + 1));
|
|
212
|
+
if (typeof value !== "object")
|
|
213
|
+
return [];
|
|
214
|
+
const output = [];
|
|
215
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
216
|
+
if (fields.has(key) && typeof entry === "string")
|
|
217
|
+
output.push(entry);
|
|
218
|
+
if (typeof entry === "object" && entry !== null)
|
|
219
|
+
output.push(...collectTextFields(entry, fields, depth + 1));
|
|
220
|
+
}
|
|
221
|
+
return output;
|
|
222
|
+
}
|
|
205
223
|
function extractLocalAgentProposal(raw, provider) {
|
|
206
224
|
const outputs = [];
|
|
207
|
-
|
|
225
|
+
const lines = raw.split(/\r?\n/).filter((line) => line.trim());
|
|
226
|
+
const streamObjects = jsonObjectCandidates(raw).flatMap((candidate) => {
|
|
227
|
+
try {
|
|
228
|
+
return [JSON.parse(candidate)];
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
for (const line of lines) {
|
|
208
235
|
try {
|
|
209
236
|
const event = JSON.parse(line);
|
|
210
237
|
const item = event.item;
|
|
@@ -212,7 +239,7 @@ function extractLocalAgentProposal(raw, provider) {
|
|
|
212
239
|
const values = provider === "claude-code"
|
|
213
240
|
? [event.result, ...(event.message?.content || []).map((entry) => entry.text)]
|
|
214
241
|
: provider === "opencode"
|
|
215
|
-
? [part?.type === "text" ? part.text : undefined
|
|
242
|
+
? [...collectTextFields(event), part?.type === "text" ? part.text : undefined]
|
|
216
243
|
: [event.text, event.output_text, item?.text, item?.output_text, item?.message];
|
|
217
244
|
for (const value of values)
|
|
218
245
|
if (typeof value === "string" && value.trim())
|
|
@@ -223,6 +250,15 @@ function extractLocalAgentProposal(raw, provider) {
|
|
|
223
250
|
outputs.push(line);
|
|
224
251
|
}
|
|
225
252
|
}
|
|
253
|
+
// OpenCode can emit multiple JSON records on one physical line. The
|
|
254
|
+
// line-based pass above cannot see those records, so inspect each balanced
|
|
255
|
+
// JSON object as well and collect text nested in its final response event.
|
|
256
|
+
for (const event of streamObjects) {
|
|
257
|
+
outputs.push(...collectTextFields(event));
|
|
258
|
+
const part = event.part;
|
|
259
|
+
if (part?.type === "text" && typeof part.text === "string")
|
|
260
|
+
outputs.push(part.text);
|
|
261
|
+
}
|
|
226
262
|
// Tool events are interleaved with the final answer. Try text events in
|
|
227
263
|
// reverse order, then the complete stream as a final fallback.
|
|
228
264
|
for (const output of [...outputs.reverse(), raw]) {
|
|
@@ -329,6 +365,21 @@ function progressFromLine(raw, provider) {
|
|
|
329
365
|
}
|
|
330
366
|
return null;
|
|
331
367
|
}
|
|
368
|
+
function progressEventsFromRaw(raw, provider) {
|
|
369
|
+
const records = (() => {
|
|
370
|
+
try {
|
|
371
|
+
JSON.parse(raw);
|
|
372
|
+
return [raw];
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
return jsonObjectCandidates(raw);
|
|
376
|
+
}
|
|
377
|
+
})();
|
|
378
|
+
return records.flatMap((record) => {
|
|
379
|
+
const progress = progressFromLine(record, provider);
|
|
380
|
+
return progress ? [progress] : [];
|
|
381
|
+
});
|
|
382
|
+
}
|
|
332
383
|
function localAgentError(command, stderr, exitCode) {
|
|
333
384
|
const output = stderr.trim();
|
|
334
385
|
if (command === "codex" && output.includes("missing field `base_instructions`")) {
|
|
@@ -351,7 +402,7 @@ function localCommand(provider, model, prompt) {
|
|
|
351
402
|
return { command: "claude", args: ["-p", prompt || "", "--output-format", "stream-json", "--verbose", "--permission-mode", "plan", "--max-turns", "3", ...(selectedModel ? ["--model", selectedModel] : [])] };
|
|
352
403
|
}
|
|
353
404
|
if (provider === "opencode") {
|
|
354
|
-
return { command: "opencode", args: ["run", "--format", "json", "--agent", "plan", ...(selectedModel ? ["--model", selectedModel] : []), prompt || ""] };
|
|
405
|
+
return { command: "opencode", args: ["run", "--format", "json", "--print-logs", "--log-level", "INFO", "--agent", "plan", ...(selectedModel ? ["--model", selectedModel] : []), prompt || ""] };
|
|
355
406
|
}
|
|
356
407
|
return { command: "codex", args: ["exec", "--json", "--sandbox", "read-only", "--skip-git-repo-check", ...(selectedModel ? ["--model", selectedModel] : []), prompt || ""] };
|
|
357
408
|
}
|
|
@@ -367,7 +418,6 @@ function runLocalCommand(cwd, command, args, provider, signal, onProgress) {
|
|
|
367
418
|
let timedOut = false;
|
|
368
419
|
const timeout = setTimeout(() => {
|
|
369
420
|
timedOut = true;
|
|
370
|
-
onProgress?.(`${label} did not respond; stopping this task…`);
|
|
371
421
|
child.kill("SIGTERM");
|
|
372
422
|
setTimeout(() => child.kill("SIGKILL"), 3000).unref();
|
|
373
423
|
}, 5 * 60 * 1000);
|
|
@@ -390,10 +440,10 @@ function runLocalCommand(cwd, command, args, provider, signal, onProgress) {
|
|
|
390
440
|
else
|
|
391
441
|
pendingStderr = remainder;
|
|
392
442
|
for (const line of lines) {
|
|
393
|
-
const progress
|
|
394
|
-
if (progress)
|
|
443
|
+
for (const progress of progressEventsFromRaw(line, provider)) {
|
|
395
444
|
onProgress?.(progress.message, progress.detail);
|
|
396
|
-
|
|
445
|
+
}
|
|
446
|
+
if (stream === "stderr" && line.trim()) {
|
|
397
447
|
if (provider === "opencode" && /quota exceeded|authentication failed|invalid api key|unauthorized|forbidden/i.test(line)) {
|
|
398
448
|
onProgress?.("OpenCode · provider rejected the request; stopping this task…");
|
|
399
449
|
child.kill("SIGTERM");
|
|
@@ -403,12 +453,6 @@ function runLocalCommand(cwd, command, args, provider, signal, onProgress) {
|
|
|
403
453
|
};
|
|
404
454
|
child.stdout.on("data", (chunk) => consume(chunk, "stdout"));
|
|
405
455
|
child.stderr.on("data", (chunk) => consume(chunk, "stderr"));
|
|
406
|
-
child.once("spawn", () => onProgress?.(`${label} process started${child.pid ? ` · PID ${child.pid}` : ""}`));
|
|
407
|
-
const heartbeat = setInterval(() => {
|
|
408
|
-
if (!signal?.aborted)
|
|
409
|
-
onProgress?.(`${label} is still running${child.pid ? ` · PID ${child.pid}` : ""}…`);
|
|
410
|
-
}, 15000);
|
|
411
|
-
heartbeat.unref();
|
|
412
456
|
const abort = () => child.kill("SIGTERM");
|
|
413
457
|
if (signal?.aborted)
|
|
414
458
|
abort();
|
|
@@ -419,7 +463,6 @@ function runLocalCommand(cwd, command, args, provider, signal, onProgress) {
|
|
|
419
463
|
return;
|
|
420
464
|
settled = true;
|
|
421
465
|
clearTimeout(timeout);
|
|
422
|
-
clearInterval(heartbeat);
|
|
423
466
|
signal?.removeEventListener("abort", abort);
|
|
424
467
|
reject(error);
|
|
425
468
|
});
|
|
@@ -428,7 +471,6 @@ function runLocalCommand(cwd, command, args, provider, signal, onProgress) {
|
|
|
428
471
|
return;
|
|
429
472
|
settled = true;
|
|
430
473
|
clearTimeout(timeout);
|
|
431
|
-
clearInterval(heartbeat);
|
|
432
474
|
signal?.removeEventListener("abort", abort);
|
|
433
475
|
if (timedOut) {
|
|
434
476
|
reject(new Error(`${label} did not finish within 5 minutes. Check its login or approval prompt, then retry.`));
|
|
@@ -438,14 +480,17 @@ function runLocalCommand(cwd, command, args, provider, signal, onProgress) {
|
|
|
438
480
|
const stderrTail = pendingStderr.trim();
|
|
439
481
|
if (stdoutTail) {
|
|
440
482
|
stdout += pendingStdout;
|
|
441
|
-
const progress
|
|
442
|
-
if (progress)
|
|
483
|
+
for (const progress of progressEventsFromRaw(stdoutTail, provider)) {
|
|
443
484
|
onProgress?.(progress.message, progress.detail);
|
|
485
|
+
}
|
|
444
486
|
}
|
|
445
487
|
if (stderrTail) {
|
|
446
|
-
const progress
|
|
447
|
-
if (progress)
|
|
488
|
+
for (const progress of progressEventsFromRaw(stderrTail, provider)) {
|
|
448
489
|
onProgress?.(progress.message, progress.detail);
|
|
490
|
+
}
|
|
491
|
+
if (provider === "opencode" && /quota exceeded|authentication failed|invalid api key|unauthorized|forbidden/i.test(stderrTail)) {
|
|
492
|
+
onProgress?.("OpenCode · provider rejected the request; stopping this task…");
|
|
493
|
+
}
|
|
449
494
|
}
|
|
450
495
|
resolve({ stdout, stderr, exitCode: code ?? 1 });
|
|
451
496
|
});
|
|
@@ -463,9 +508,7 @@ async function proposeWithLocalAgent(cwd, instruction, context, config, signal,
|
|
|
463
508
|
return extractLocalAgentProposal(result.stdout, config.provider);
|
|
464
509
|
}
|
|
465
510
|
async function proposeChanges(cwd, input, config, signal, onProgress) {
|
|
466
|
-
onProgress?.("Reading source context…");
|
|
467
511
|
const context = await contextFor(cwd, input.element);
|
|
468
|
-
onProgress?.("Starting the coding agent…");
|
|
469
512
|
const visualContext = input.context ? { ...input.context, screenshots: undefined } : undefined;
|
|
470
513
|
const history = input.messages?.map((message) => `${message.role}: ${message.content}`).join("\n") || input.instruction;
|
|
471
514
|
const priorChanges = input.changesHistory?.length ? JSON.stringify(input.changesHistory, null, 2) : "None";
|
package/dist/cli/bridge.js
CHANGED
|
@@ -278,7 +278,7 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
278
278
|
const controller = new AbortController();
|
|
279
279
|
taskControllers.set(request.taskId, controller);
|
|
280
280
|
if (socket.readyState === socket.OPEN) {
|
|
281
|
-
socket.send(JSON.stringify({ type: "agent_status", taskId: request.taskId, status: "working", message: statusMessage || "
|
|
281
|
+
socket.send(JSON.stringify({ type: "agent_status", taskId: request.taskId, status: "working", message: statusMessage || "Working…" }));
|
|
282
282
|
}
|
|
283
283
|
void (0, agent_1.proposeChanges)(cwd, request, config, controller.signal, (message, detail) => {
|
|
284
284
|
if (!controller.signal.aborted && socket.readyState === socket.OPEN) {
|
|
@@ -336,7 +336,7 @@ function startBridge(cwd = process.cwd(), collabConfig = null, bridgePort = expo
|
|
|
336
336
|
const selectedConfig = localProvider
|
|
337
337
|
? { provider: cliProvider, model: msg.model }
|
|
338
338
|
: { ...agentConfig, provider: (msg.provider || agentConfig.provider), model: msg.model };
|
|
339
|
-
socket.send(JSON.stringify({ type: "agent_status", taskId: msg.taskId, status: "working", message: "
|
|
339
|
+
socket.send(JSON.stringify({ type: "agent_status", taskId: msg.taskId, status: "working", message: "Working…" }));
|
|
340
340
|
void (0, agent_1.answerQuestion)(cwd, { question: msg.question, context: msg.context, element: msg.element, messages: msg.messages }, selectedConfig, controller.signal, (message, detail) => {
|
|
341
341
|
if (!controller.signal.aborted && socket.readyState === socket.OPEN)
|
|
342
342
|
socket.send(JSON.stringify({ type: "agent_status", taskId: msg.taskId, status: "working", message, detail }));
|
package/dist/overlay.js
CHANGED
|
@@ -19043,7 +19043,7 @@
|
|
|
19043
19043
|
appendAgentLog(logMessage);
|
|
19044
19044
|
if (taskId2) recordTaskActivity(taskId2, logMessage);
|
|
19045
19045
|
const running = status === "thinking" || status === "working";
|
|
19046
|
-
agentStatusElement.hidden = status === "review";
|
|
19046
|
+
agentStatusElement.hidden = status === "review" || status === "complete";
|
|
19047
19047
|
agentStatusElement.dataset.status = status;
|
|
19048
19048
|
agentStatusMessage.textContent = message;
|
|
19049
19049
|
const badgeEl = agentStatusElement.querySelector(".lasso-agent-status-badge");
|
|
@@ -19074,18 +19074,9 @@
|
|
|
19074
19074
|
}
|
|
19075
19075
|
function resetAgentState() {
|
|
19076
19076
|
state.agentRunning = false;
|
|
19077
|
-
const hasLog = agentLogLines.length > 0;
|
|
19078
19077
|
if (agentStatusElement) {
|
|
19079
|
-
agentStatusElement.hidden =
|
|
19080
|
-
agentStatusElement.dataset.status =
|
|
19081
|
-
}
|
|
19082
|
-
if (hasLog && agentStatusMessage) agentStatusMessage.textContent = "Agent complete";
|
|
19083
|
-
if (hasLog) {
|
|
19084
|
-
const badgeEl = agentStatusElement?.querySelector(".lasso-agent-status-badge");
|
|
19085
|
-
if (badgeEl) {
|
|
19086
|
-
badgeEl.textContent = "done";
|
|
19087
|
-
badgeEl.className = "lasso-agent-status-badge complete";
|
|
19088
|
-
}
|
|
19078
|
+
agentStatusElement.hidden = true;
|
|
19079
|
+
agentStatusElement.dataset.status = "idle";
|
|
19089
19080
|
}
|
|
19090
19081
|
if (sendButton) {
|
|
19091
19082
|
sendButton.classList.remove("loading");
|
package/package.json
CHANGED