@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 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
- for (const line of raw.split(/\r?\n/)) {
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, event.text, event.output_text]
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 = progressFromLine(line, provider);
394
- if (progress)
443
+ for (const progress of progressEventsFromRaw(line, provider)) {
395
444
  onProgress?.(progress.message, progress.detail);
396
- else if (stream === "stderr" && line.trim()) {
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 = progressFromLine(stdoutTail, provider);
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 = progressFromLine(stderrTail, provider);
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";
@@ -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 || "Starting agent…" }));
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: "Starting agent…" }));
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 = !hasLog;
19080
- agentStatusElement.dataset.status = hasLog ? "complete" : "idle";
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lasso-ai/cli",
3
- "version": "1.0.28",
3
+ "version": "1.0.29",
4
4
  "description": "Select any part of your running app, describe a change, and let AI edit the real source code.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/cli/index.js",