@lasso-ai/cli 2.1.3 → 2.1.6

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.
@@ -72,7 +72,7 @@ class CodexCLIAdapter extends local_agent_adapter_1.LocalAgentAdapter {
72
72
  const args = ["exec"];
73
73
  if (resumeId) {
74
74
  // `resume` does not accept -s; sandbox policy goes through a config override.
75
- args.push("resume", resumeId, "--json", "--skip-git-repo-check", "-c", 'sandbox_mode="workspace-write"');
75
+ args.push("resume", resumeId, "--json", "--skip-git-repo-check", "-c", `sandbox_mode="${context.mode === "conversation" ? "read-only" : "workspace-write"}"`);
76
76
  if (this.modelId)
77
77
  args.push("-m", this.modelId);
78
78
  }
@@ -81,6 +81,11 @@ class CodexCLIAdapter extends local_agent_adapter_1.LocalAgentAdapter {
81
81
  if (this.modelId)
82
82
  args.push("-m", this.modelId);
83
83
  }
84
+ if (context.mode === "conversation") {
85
+ const sandboxIndex = args.indexOf("workspace-write");
86
+ if (sandboxIndex >= 0)
87
+ args[sandboxIndex] = "read-only";
88
+ }
84
89
  args.push(prompt);
85
90
  return { bin: "codex", args, cwd: cfg.projectPath };
86
91
  }
@@ -119,12 +124,12 @@ class CodexCLIAdapter extends local_agent_adapter_1.LocalAgentAdapter {
119
124
  return true;
120
125
  }
121
126
  case "error": {
122
- const message = event.message || event.error?.message || "Codex reported an error";
127
+ const message = event.message || this.describeError(event.error) || "Codex reported an error";
123
128
  this.recordFailure(sink, message);
124
129
  return true;
125
130
  }
126
131
  case "turn.failed": {
127
- const message = event.error?.message || "Codex turn failed";
132
+ const message = this.describeError(event.error) || "Codex turn failed";
128
133
  this.recordFailure(sink, message);
129
134
  return true;
130
135
  }
@@ -168,7 +173,7 @@ class CodexCLIAdapter extends local_agent_adapter_1.LocalAgentAdapter {
168
173
  break;
169
174
  }
170
175
  case "error": {
171
- const message = item.error?.message || item.message || "Codex item error";
176
+ const message = this.describeError(item.error) || item.message || "Codex item error";
172
177
  this.recordFailure(sink, message);
173
178
  break;
174
179
  }
@@ -187,6 +192,29 @@ class CodexCLIAdapter extends local_agent_adapter_1.LocalAgentAdapter {
187
192
  sink.error(message, { recoverable: false, authFailure: sink.state.authFailure });
188
193
  }
189
194
  }
195
+ describeError(error) {
196
+ if (!error)
197
+ return undefined;
198
+ if (typeof error === "string")
199
+ return error;
200
+ if (typeof error === "object") {
201
+ const value = error;
202
+ const nested = [
203
+ value.message,
204
+ value.data?.message,
205
+ value.cause?.message,
206
+ ].find((item) => typeof item === "string" && item.trim().length > 0);
207
+ if (nested)
208
+ return nested;
209
+ try {
210
+ return JSON.stringify(error).slice(0, 800);
211
+ }
212
+ catch {
213
+ return undefined;
214
+ }
215
+ }
216
+ return String(error);
217
+ }
190
218
  inspectFailureText(text) {
191
219
  if (!text)
192
220
  return null;
@@ -103,7 +103,7 @@ class CursorAdapter extends local_agent_adapter_1.LocalAgentAdapter {
103
103
  }
104
104
  buildCommand(context) {
105
105
  const resumeId = this.resumeSessionId(context);
106
- const args = ["-p", "--output-format", "stream-json", "--trust"];
106
+ const args = ["-p", "--output-format", context.mode === "conversation" ? "json" : "stream-json", "--trust"];
107
107
  if (this.modelId)
108
108
  args.push("--model", this.modelId);
109
109
  if (resumeId)
@@ -301,10 +301,17 @@ class LocalAgentAdapter {
301
301
  /** Build the shared task prompt (project context + continuation context). */
302
302
  buildTaskPrompt(context) {
303
303
  const lines = [];
304
- lines.push("You are working inside a Lasso-managed review workflow.");
305
- lines.push("Complete the following task by editing this project directly:");
304
+ lines.push("You are Lasso, an agentic AI coding assistant embedded in this live project.");
306
305
  lines.push("");
307
- lines.push(context.userPrompt);
306
+ lines.push("Instructions:");
307
+ lines.push("1. Understand user intent:");
308
+ lines.push(" - If the user is greeting you, asking a question, seeking advice, feedback, explanations, or having a conversation (and NOT asking you to edit or write code): answer conversationally and concisely. Do NOT edit files or run build/modification commands.");
309
+ lines.push(" - If the user asks to build, modify, fix, refactor, add, style, or remove code/files: perform the necessary edits directly on the project.");
310
+ lines.push(" - If the request is ambiguous, provide a helpful answer and ask for clarification before editing files.");
311
+ lines.push("2. All file changes will be captured as a diff, reviewed by the user, and only applied if approved.");
312
+ lines.push("3. Do not commit, push, or perform operations outside this project directory.");
313
+ lines.push("");
314
+ lines.push(`User request: ${context.userPrompt}`);
308
315
  lines.push("");
309
316
  lines.push("Project context:");
310
317
  lines.push(`- Path: ${this.config.projectPath}`);
@@ -17,6 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.OpenCodeAdapter = void 0;
18
18
  const node_util_1 = require("node:util");
19
19
  const local_agent_adapter_1 = require("./local-agent-adapter");
20
+ const backends_1 = require("../../cli/backends");
20
21
  const execFileAsync = (0, node_util_1.promisify)(require("node:child_process").execFile);
21
22
  const FILE_INPUT_KEYS = ["filePath", "file_path", "path", "filename"];
22
23
  class OpenCodeAdapter extends local_agent_adapter_1.LocalAgentAdapter {
@@ -84,6 +85,9 @@ class OpenCodeAdapter extends local_agent_adapter_1.LocalAgentAdapter {
84
85
  }
85
86
  }
86
87
  buildCommand(context) {
88
+ if (this.modelId && (0, backends_1.isOpenCodeHeadlessRestrictedModel)(this.modelId)) {
89
+ throw new Error(`OpenCode model ${this.modelId} is restricted to OpenCode's own app and cannot run through Lasso. Choose a non-free OpenCode model or configure another provider.`);
90
+ }
87
91
  const resumeId = this.resumeSessionId(context);
88
92
  const args = ["run", "--format", "json"];
89
93
  if (this.modelId)
@@ -91,7 +95,10 @@ class OpenCodeAdapter extends local_agent_adapter_1.LocalAgentAdapter {
91
95
  if (resumeId)
92
96
  args.push("--session", resumeId);
93
97
  args.push(this.buildTaskPrompt(context));
94
- return { bin: "opencode", args, cwd: this.config.projectPath };
98
+ const env = context.mode === "conversation"
99
+ ? { OPENCODE_PERMISSION: JSON.stringify({ "*": "deny" }) }
100
+ : undefined;
101
+ return { bin: "opencode", args, cwd: this.config.projectPath, env };
95
102
  }
96
103
  handleLine(line, context, sink) {
97
104
  let event;
@@ -165,15 +172,20 @@ class OpenCodeAdapter extends local_agent_adapter_1.LocalAgentAdapter {
165
172
  return error;
166
173
  if (typeof error === "object") {
167
174
  const e = error;
168
- if (typeof e.message === "string")
169
- return e.message;
170
- if (typeof e.name === "string")
171
- return e.name;
175
+ const nested = [
176
+ e.message,
177
+ e.data?.message,
178
+ e.data?.error,
179
+ e.cause?.message,
180
+ ].find((item) => typeof item === "string" && item.trim().length > 0);
181
+ if (nested)
182
+ return nested;
172
183
  try {
173
- return JSON.stringify(error).slice(0, 400);
184
+ const serialized = JSON.stringify(error).slice(0, 800);
185
+ return typeof e.name === "string" ? `${e.name}: ${serialized}` : serialized;
174
186
  }
175
187
  catch {
176
- return undefined;
188
+ return typeof e.name === "string" ? e.name : undefined;
177
189
  }
178
190
  }
179
191
  return String(error);
@@ -191,6 +191,8 @@ export interface LassoModelResponse {
191
191
  status: "continue" | "waiting" | "complete" | "failed";
192
192
  reasoning: {
193
193
  summary: string;
194
+ /** Raw model thoughts / extended reasoning, when the provider exposes it. */
195
+ raw?: string;
194
196
  };
195
197
  decision: {
196
198
  type: "inspect" | "plan" | "execute" | "verify" | "recover" | "ask_user";
@@ -211,6 +213,8 @@ export interface AgentReasoningEvent extends AgentEvent {
211
213
  data: {
212
214
  summary: string;
213
215
  decision?: string;
216
+ /** Raw model thoughts for UI detail display. */
217
+ raw?: string;
214
218
  };
215
219
  }
216
220
  export interface PhaseChangedEvent extends AgentEvent {
@@ -14,6 +14,10 @@ export declare class AgentStateMachine {
14
14
  * Actions allowed in each phase
15
15
  */
16
16
  private static readonly ALLOWED_ACTIONS;
17
+ /**
18
+ * Action types allowed in a phase (public, e.g. for model prompts).
19
+ */
20
+ static allowedActions(phase: AgentPhase): string[];
17
21
  /**
18
22
  * Check if a phase transition is valid
19
23
  */
@@ -25,13 +25,19 @@ class AgentStateMachine {
25
25
  */
26
26
  static ALLOWED_ACTIONS = {
27
27
  understand: [],
28
- inspect: ["read_file", "search_code", "inspect_project"],
28
+ inspect: ["read_file", "search_code", "inspect_project", "browser_check"],
29
29
  plan: [],
30
30
  execute: ["edit_file", "create_file", "delete_file", "run_command"],
31
31
  verify: ["browser_check", "run_command"],
32
32
  recover: ["edit_file", "read_file", "run_command"],
33
33
  complete: [],
34
34
  };
35
+ /**
36
+ * Action types allowed in a phase (public, e.g. for model prompts).
37
+ */
38
+ static allowedActions(phase) {
39
+ return [...(this.ALLOWED_ACTIONS[phase] || [])];
40
+ }
35
41
  /**
36
42
  * Check if a phase transition is valid
37
43
  */
@@ -189,7 +195,9 @@ class AgentStateMachine {
189
195
  static shouldVerify(session) {
190
196
  return (session.phase === "execute" &&
191
197
  session.completedActions.length > 0 &&
192
- session.errors.filter((e) => e.phase === "execute").length === 0);
198
+ // Validation feedback (model proposed a rejected action) is teaching
199
+ // material for the next prompt — it must not block verification.
200
+ session.errors.filter((e) => e.phase === "execute" && e.type !== "validation").length === 0);
193
201
  }
194
202
  /**
195
203
  * Check if the session should trigger recovery
@@ -100,6 +100,8 @@ export interface TaskContext {
100
100
  * User's prompt or instruction
101
101
  */
102
102
  userPrompt: string;
103
+ /** Whether the backend should answer conversationally without project work. */
104
+ mode?: "conversation" | "task";
103
105
  /**
104
106
  * Project context
105
107
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lasso-ai/cli",
3
- "version": "2.1.3",
3
+ "version": "2.1.6",
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",