@lasso-ai/cli 1.5.0 → 2.0.0
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 -0
- package/dist/cli/backends.d.ts +53 -0
- package/dist/cli/backends.js +267 -0
- package/dist/cli/bridge.d.ts +8 -5
- package/dist/cli/bridge.js +357 -151
- package/dist/cli/host/install.js +12 -1
- package/dist/cli/local-runtime.d.ts +28 -0
- package/dist/cli/local-runtime.js +294 -0
- package/dist/cli/oneshot.js +28 -10
- package/dist/cli/runtime-client.d.ts +91 -10
- package/dist/cli/runtime-client.js +38 -3
- package/dist/overlay.js +44 -28
- package/dist/runtime/__tests__/adapter-normalization.test.d.ts +1 -0
- package/dist/runtime/__tests__/adapter-normalization.test.js +392 -0
- package/dist/runtime/__tests__/state-machine.test.d.ts +1 -0
- package/dist/runtime/__tests__/state-machine.test.js +107 -0
- package/dist/runtime/__tests__/verification-engine.test.d.ts +1 -0
- package/dist/runtime/__tests__/verification-engine.test.js +91 -0
- package/dist/runtime/__tests__/workspace-guard.test.d.ts +1 -0
- package/dist/runtime/__tests__/workspace-guard.test.js +104 -0
- package/dist/runtime/adapters/claude-code-adapter.d.ts +46 -0
- package/dist/runtime/adapters/claude-code-adapter.js +230 -0
- package/dist/runtime/adapters/codex-cli-adapter.d.ts +44 -0
- package/dist/runtime/adapters/codex-cli-adapter.js +202 -0
- package/dist/runtime/adapters/cursor-adapter.d.ts +47 -0
- package/dist/runtime/adapters/cursor-adapter.js +214 -0
- package/dist/runtime/adapters/factory.d.ts +19 -0
- package/dist/runtime/adapters/factory.js +48 -0
- package/dist/runtime/adapters/local-agent-adapter.d.ts +143 -0
- package/dist/runtime/adapters/local-agent-adapter.js +408 -0
- package/dist/runtime/adapters/opencode-adapter.d.ts +43 -0
- package/dist/runtime/adapters/opencode-adapter.js +210 -0
- package/dist/runtime/agent-runtime.d.ts +286 -0
- package/dist/runtime/agent-runtime.js +8 -0
- package/dist/runtime/agent-state-machine.d.ts +67 -0
- package/dist/runtime/agent-state-machine.js +234 -0
- package/dist/runtime/backend-interface.d.ts +272 -0
- package/dist/runtime/backend-interface.js +38 -0
- package/dist/runtime/event-mapper.d.ts +11 -0
- package/dist/runtime/event-mapper.js +40 -0
- package/dist/runtime/index.d.ts +20 -0
- package/dist/runtime/index.js +43 -0
- package/dist/runtime/local-session-runner.d.ts +60 -0
- package/dist/runtime/local-session-runner.js +185 -0
- package/dist/runtime/review.d.ts +24 -0
- package/dist/runtime/review.js +30 -0
- package/dist/runtime/verification-engine.d.ts +71 -0
- package/dist/runtime/verification-engine.js +374 -0
- package/dist/runtime/workspace-guard.d.ts +70 -0
- package/dist/runtime/workspace-guard.js +321 -0
- package/docs/local-coding-agents.md +183 -0
- package/package.json +22 -3
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lasso Backend Interface - Unified Abstraction for Agent Backends
|
|
3
|
+
*
|
|
4
|
+
* This file defines the common interface for all agent backends:
|
|
5
|
+
* - Hosted model providers (Anthropic, OpenAI, Google, NVIDIA, Ollama)
|
|
6
|
+
* - Local coding agents (Claude Code, OpenCode, Codex CLI, Cursor)
|
|
7
|
+
*
|
|
8
|
+
* The Lasso Runtime uses this interface to delegate task execution while maintaining
|
|
9
|
+
* control over the overall workflow: Understand → Inspect → Plan → Execute → Verify → Recover → Complete
|
|
10
|
+
*/
|
|
11
|
+
import { AgentAction, Observation, AgentError, AgentPhase, ProjectContext, SelectionContext } from "./agent-runtime";
|
|
12
|
+
export type { AgentAction, Observation, AgentError, AgentPhase, ProjectContext, SelectionContext } from "./agent-runtime";
|
|
13
|
+
export type BackendType = "hosted_model" | "local_agent";
|
|
14
|
+
export type BackendProvider = "anthropic" | "openai" | "google" | "nvidia" | "ollama" | "claude_code" | "opencode" | "codex_cli" | "cursor";
|
|
15
|
+
export interface BackendCapabilities {
|
|
16
|
+
/**
|
|
17
|
+
* Whether the backend can stream progress events in real-time
|
|
18
|
+
*/
|
|
19
|
+
canStreamEvents: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Whether the backend can operate in an isolated workspace
|
|
22
|
+
* (required for safe pre-review execution)
|
|
23
|
+
*/
|
|
24
|
+
canIsolateWorkspace: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Whether the backend provides structured diff output
|
|
27
|
+
* (vs. requiring post-execution diff generation)
|
|
28
|
+
*/
|
|
29
|
+
providesStructuredDiffs: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the backend supports session continuation across calls
|
|
32
|
+
*/
|
|
33
|
+
supportsSessionContinuation: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Whether the backend can cancel running tasks
|
|
36
|
+
*/
|
|
37
|
+
supportsCancellation: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Whether the backend has its own internal agentic loop
|
|
40
|
+
* (if true, Lasso should not run a competing loop around it)
|
|
41
|
+
*/
|
|
42
|
+
hasInternalLoop: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* File operations the backend can perform directly
|
|
45
|
+
*/
|
|
46
|
+
supportedFileOperations: "read" | "write" | "both" | "none";
|
|
47
|
+
/**
|
|
48
|
+
* Whether the backend can run shell commands
|
|
49
|
+
*/
|
|
50
|
+
canRunCommands: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Authentication method
|
|
53
|
+
*/
|
|
54
|
+
authMethod: "api_key" | "cli_session" | "none" | "oauth";
|
|
55
|
+
/**
|
|
56
|
+
* Maximum file size for operations (in bytes), if any
|
|
57
|
+
*/
|
|
58
|
+
maxFileSize?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Commands that are forbidden (if any)
|
|
61
|
+
*/
|
|
62
|
+
forbiddenCommands?: string[];
|
|
63
|
+
}
|
|
64
|
+
export interface BackendConfig {
|
|
65
|
+
/**
|
|
66
|
+
* Backend type
|
|
67
|
+
*/
|
|
68
|
+
type: BackendType;
|
|
69
|
+
/**
|
|
70
|
+
* Provider identifier
|
|
71
|
+
*/
|
|
72
|
+
provider: BackendProvider;
|
|
73
|
+
/**
|
|
74
|
+
* Model ID (for hosted models) or agent version (for local agents)
|
|
75
|
+
*/
|
|
76
|
+
modelId?: string;
|
|
77
|
+
/**
|
|
78
|
+
* API key (for hosted models)
|
|
79
|
+
*/
|
|
80
|
+
apiKey?: string;
|
|
81
|
+
/**
|
|
82
|
+
* Base URL (for hosted models like Ollama)
|
|
83
|
+
*/
|
|
84
|
+
baseURL?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Project path (for local agents)
|
|
87
|
+
*/
|
|
88
|
+
projectPath?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Additional provider-specific configuration
|
|
91
|
+
*/
|
|
92
|
+
providerConfig?: Record<string, unknown>;
|
|
93
|
+
}
|
|
94
|
+
export interface TaskContext {
|
|
95
|
+
/**
|
|
96
|
+
* Unique task identifier
|
|
97
|
+
*/
|
|
98
|
+
taskId: string;
|
|
99
|
+
/**
|
|
100
|
+
* User's prompt or instruction
|
|
101
|
+
*/
|
|
102
|
+
userPrompt: string;
|
|
103
|
+
/**
|
|
104
|
+
* Project context
|
|
105
|
+
*/
|
|
106
|
+
project: ProjectContext;
|
|
107
|
+
/**
|
|
108
|
+
* Selection context (if user selected a UI element)
|
|
109
|
+
*/
|
|
110
|
+
selection?: SelectionContext;
|
|
111
|
+
/**
|
|
112
|
+
* Conversation history
|
|
113
|
+
*/
|
|
114
|
+
conversation: Array<{
|
|
115
|
+
role: "user" | "assistant" | "system";
|
|
116
|
+
content: string;
|
|
117
|
+
timestamp: number;
|
|
118
|
+
}>;
|
|
119
|
+
/**
|
|
120
|
+
* Previous observations (for context)
|
|
121
|
+
*/
|
|
122
|
+
previousObservations: Observation[];
|
|
123
|
+
/**
|
|
124
|
+
* Previous errors (for recovery)
|
|
125
|
+
*/
|
|
126
|
+
previousErrors: AgentError[];
|
|
127
|
+
/**
|
|
128
|
+
* Current phase in Lasso's workflow
|
|
129
|
+
*/
|
|
130
|
+
currentPhase: AgentPhase;
|
|
131
|
+
/**
|
|
132
|
+
* Constraints (max actions, safety level, etc.)
|
|
133
|
+
*/
|
|
134
|
+
constraints: {
|
|
135
|
+
maxActions?: number;
|
|
136
|
+
safetyLevel?: "strict" | "standard" | "permissive";
|
|
137
|
+
allowedCommands?: string[];
|
|
138
|
+
forbiddenPaths?: string[];
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Reference to the backend's own external session (e.g. Claude Code session id,
|
|
142
|
+
* Codex thread id, OpenCode ses_ id) for continuation when supported.
|
|
143
|
+
*/
|
|
144
|
+
sessionRef?: {
|
|
145
|
+
id?: string;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
export type BackendEventType = "task_started" | "task_progress" | "tool_started" | "tool_completed" | "file_read" | "file_modified" | "file_created" | "file_deleted" | "diff_available" | "verification_started" | "verification_completed" | "error" | "waiting_for_user" | "task_completed" | "task_cancelled" | "task_failed";
|
|
149
|
+
export interface BackendEvent {
|
|
150
|
+
type: BackendEventType;
|
|
151
|
+
taskId: string;
|
|
152
|
+
timestamp: number;
|
|
153
|
+
data?: Record<string, unknown>;
|
|
154
|
+
}
|
|
155
|
+
export interface BackendResult {
|
|
156
|
+
/**
|
|
157
|
+
* Whether the task completed successfully
|
|
158
|
+
*/
|
|
159
|
+
success: boolean;
|
|
160
|
+
/**
|
|
161
|
+
* Final status
|
|
162
|
+
*/
|
|
163
|
+
status: "completed" | "failed" | "cancelled" | "waiting";
|
|
164
|
+
/**
|
|
165
|
+
* Summary of what was done
|
|
166
|
+
*/
|
|
167
|
+
summary: string;
|
|
168
|
+
/**
|
|
169
|
+
* File changes detected (normalized format)
|
|
170
|
+
*/
|
|
171
|
+
fileChanges: Array<{
|
|
172
|
+
filePath: string;
|
|
173
|
+
operation: "created" | "modified" | "deleted";
|
|
174
|
+
oldContent?: string;
|
|
175
|
+
newContent?: string;
|
|
176
|
+
}>;
|
|
177
|
+
/**
|
|
178
|
+
* Actions taken (normalized to Lasso's AgentAction format where possible)
|
|
179
|
+
*/
|
|
180
|
+
actions: AgentAction[];
|
|
181
|
+
/**
|
|
182
|
+
* Observations collected
|
|
183
|
+
*/
|
|
184
|
+
observations: Observation[];
|
|
185
|
+
/**
|
|
186
|
+
* Errors encountered
|
|
187
|
+
*/
|
|
188
|
+
errors: AgentError[];
|
|
189
|
+
/**
|
|
190
|
+
* Final conversation state (for continuation)
|
|
191
|
+
*/
|
|
192
|
+
conversation?: Array<{
|
|
193
|
+
role: "user" | "assistant" | "system";
|
|
194
|
+
content: string;
|
|
195
|
+
timestamp: number;
|
|
196
|
+
}>;
|
|
197
|
+
/**
|
|
198
|
+
* Provider-specific metadata
|
|
199
|
+
*/
|
|
200
|
+
metadata?: Record<string, unknown>;
|
|
201
|
+
}
|
|
202
|
+
export interface IBackend {
|
|
203
|
+
/**
|
|
204
|
+
* Unique backend identifier
|
|
205
|
+
*/
|
|
206
|
+
id: string;
|
|
207
|
+
/**
|
|
208
|
+
* Backend type
|
|
209
|
+
*/
|
|
210
|
+
type: BackendType;
|
|
211
|
+
/**
|
|
212
|
+
* Provider name
|
|
213
|
+
*/
|
|
214
|
+
provider: BackendProvider;
|
|
215
|
+
/**
|
|
216
|
+
* Model/agent ID
|
|
217
|
+
*/
|
|
218
|
+
modelId: string;
|
|
219
|
+
/**
|
|
220
|
+
* Get backend capabilities
|
|
221
|
+
*/
|
|
222
|
+
getCapabilities(): BackendCapabilities;
|
|
223
|
+
/**
|
|
224
|
+
* Check if backend is available and properly configured
|
|
225
|
+
*
|
|
226
|
+
* Returns detailed availability info including any authentication issues
|
|
227
|
+
*/
|
|
228
|
+
checkAvailability(): Promise<{
|
|
229
|
+
available: boolean;
|
|
230
|
+
installed: boolean;
|
|
231
|
+
authenticated: boolean;
|
|
232
|
+
version?: string;
|
|
233
|
+
error?: string;
|
|
234
|
+
fixInstructions?: string;
|
|
235
|
+
}>;
|
|
236
|
+
/**
|
|
237
|
+
* Execute a task
|
|
238
|
+
*
|
|
239
|
+
* This is the main entry point for task execution.
|
|
240
|
+
* The backend should:
|
|
241
|
+
* 1. Start the task
|
|
242
|
+
* 2. Stream progress events via the callback
|
|
243
|
+
* 3. Return the final result
|
|
244
|
+
*
|
|
245
|
+
* For backends with internal loops (like Claude Code), this method
|
|
246
|
+
* should delegate to the agent and monitor its execution.
|
|
247
|
+
*
|
|
248
|
+
* For hosted models, this method should integrate with the existing
|
|
249
|
+
* ModelGateway and return structured responses.
|
|
250
|
+
*/
|
|
251
|
+
executeTask(context: TaskContext, onEvent: (event: BackendEvent) => void, signal?: AbortSignal): Promise<BackendResult>;
|
|
252
|
+
/**
|
|
253
|
+
* Cancel a running task
|
|
254
|
+
*
|
|
255
|
+
* Only called if supportsCancellation is true
|
|
256
|
+
*/
|
|
257
|
+
cancelTask(taskId: string): Promise<void>;
|
|
258
|
+
/**
|
|
259
|
+
* Switch to a different backend/model
|
|
260
|
+
*
|
|
261
|
+
* This is called when the user switches providers mid-session.
|
|
262
|
+
* The backend should:
|
|
263
|
+
* 1. Return any context that can be transferred
|
|
264
|
+
* 2. Report what cannot be transferred (e.g., internal state)
|
|
265
|
+
*/
|
|
266
|
+
switchBackend(newBackend: IBackend, context: TaskContext): Promise<{
|
|
267
|
+
transferableContext: Record<string, unknown>;
|
|
268
|
+
lostContext: string[];
|
|
269
|
+
}>;
|
|
270
|
+
}
|
|
271
|
+
export declare const DEFAULT_LOCAL_CAPABILITIES: BackendCapabilities;
|
|
272
|
+
export declare const DEFAULT_HOSTED_CAPABILITIES: BackendCapabilities;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Lasso Backend Interface - Unified Abstraction for Agent Backends
|
|
4
|
+
*
|
|
5
|
+
* This file defines the common interface for all agent backends:
|
|
6
|
+
* - Hosted model providers (Anthropic, OpenAI, Google, NVIDIA, Ollama)
|
|
7
|
+
* - Local coding agents (Claude Code, OpenCode, Codex CLI, Cursor)
|
|
8
|
+
*
|
|
9
|
+
* The Lasso Runtime uses this interface to delegate task execution while maintaining
|
|
10
|
+
* control over the overall workflow: Understand → Inspect → Plan → Execute → Verify → Recover → Complete
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.DEFAULT_HOSTED_CAPABILITIES = exports.DEFAULT_LOCAL_CAPABILITIES = void 0;
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Capability Defaults
|
|
16
|
+
// ============================================================================
|
|
17
|
+
exports.DEFAULT_LOCAL_CAPABILITIES = {
|
|
18
|
+
canStreamEvents: true,
|
|
19
|
+
canIsolateWorkspace: false,
|
|
20
|
+
providesStructuredDiffs: false,
|
|
21
|
+
supportsSessionContinuation: true,
|
|
22
|
+
supportsCancellation: true,
|
|
23
|
+
hasInternalLoop: true,
|
|
24
|
+
supportedFileOperations: "both",
|
|
25
|
+
canRunCommands: true,
|
|
26
|
+
authMethod: "cli_session",
|
|
27
|
+
};
|
|
28
|
+
exports.DEFAULT_HOSTED_CAPABILITIES = {
|
|
29
|
+
canStreamEvents: true,
|
|
30
|
+
canIsolateWorkspace: false,
|
|
31
|
+
providesStructuredDiffs: false,
|
|
32
|
+
supportsSessionContinuation: true,
|
|
33
|
+
supportsCancellation: true,
|
|
34
|
+
hasInternalLoop: false,
|
|
35
|
+
supportedFileOperations: "both",
|
|
36
|
+
canRunCommands: true,
|
|
37
|
+
authMethod: "api_key",
|
|
38
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BackendEvent → AgentEvent (wire format) mapping.
|
|
3
|
+
*
|
|
4
|
+
* Shared by the server orchestrator (SSE) and the CLI LocalRuntime so both
|
|
5
|
+
* execution paths speak the identical event vocabulary.
|
|
6
|
+
*/
|
|
7
|
+
import { BackendEvent } from "./backend-interface";
|
|
8
|
+
import { AgentEvent, AgentEventType } from "./agent-runtime";
|
|
9
|
+
export declare function mapBackendEventType(backendType: string): AgentEventType;
|
|
10
|
+
/** Convert one backend event into the runtime wire event. */
|
|
11
|
+
export declare function toAgentEvent(sessionId: string, event: BackendEvent): AgentEvent;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* BackendEvent → AgentEvent (wire format) mapping.
|
|
4
|
+
*
|
|
5
|
+
* Shared by the server orchestrator (SSE) and the CLI LocalRuntime so both
|
|
6
|
+
* execution paths speak the identical event vocabulary.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.mapBackendEventType = mapBackendEventType;
|
|
10
|
+
exports.toAgentEvent = toAgentEvent;
|
|
11
|
+
const BACKEND_TO_AGENT_TYPE = {
|
|
12
|
+
task_started: "agent_started",
|
|
13
|
+
task_progress: "agent_reasoning",
|
|
14
|
+
tool_started: "tool_started",
|
|
15
|
+
tool_completed: "tool_completed",
|
|
16
|
+
file_read: "observation",
|
|
17
|
+
file_modified: "file_changed",
|
|
18
|
+
file_created: "file_changed",
|
|
19
|
+
file_deleted: "file_changed",
|
|
20
|
+
diff_available: "file_changed",
|
|
21
|
+
verification_started: "verification_started",
|
|
22
|
+
verification_completed: "verification_completed",
|
|
23
|
+
error: "error",
|
|
24
|
+
waiting_for_user: "waiting_for_user",
|
|
25
|
+
task_completed: "agent_completed",
|
|
26
|
+
task_cancelled: "agent_cancelled",
|
|
27
|
+
task_failed: "agent_failed",
|
|
28
|
+
};
|
|
29
|
+
function mapBackendEventType(backendType) {
|
|
30
|
+
return BACKEND_TO_AGENT_TYPE[backendType] || "observation";
|
|
31
|
+
}
|
|
32
|
+
/** Convert one backend event into the runtime wire event. */
|
|
33
|
+
function toAgentEvent(sessionId, event) {
|
|
34
|
+
return {
|
|
35
|
+
type: mapBackendEventType(event.type),
|
|
36
|
+
sessionId,
|
|
37
|
+
timestamp: event.timestamp,
|
|
38
|
+
data: event.data,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lasso runtime core — shared by the server (hosted sessions, colocated
|
|
3
|
+
* self-host) and the CLI LocalRuntime (local coding agents on the user's
|
|
4
|
+
* machine). Single implementation of the review-first agent workflow.
|
|
5
|
+
*/
|
|
6
|
+
export * from "./agent-runtime";
|
|
7
|
+
export * from "./backend-interface";
|
|
8
|
+
export * from "./workspace-guard";
|
|
9
|
+
export * from "./verification-engine";
|
|
10
|
+
export * from "./agent-state-machine";
|
|
11
|
+
export * from "./event-mapper";
|
|
12
|
+
export * from "./review";
|
|
13
|
+
export * from "./local-session-runner";
|
|
14
|
+
export * from "./adapters/factory";
|
|
15
|
+
export { LocalAgentAdapter, EventSink, newRunState } from "./adapters/local-agent-adapter";
|
|
16
|
+
export type { LocalAgentConfig, CommandSpec, AgentRunState } from "./adapters/local-agent-adapter";
|
|
17
|
+
export { ClaudeCodeAdapter } from "./adapters/claude-code-adapter";
|
|
18
|
+
export { OpenCodeAdapter } from "./adapters/opencode-adapter";
|
|
19
|
+
export { CodexCLIAdapter } from "./adapters/codex-cli-adapter";
|
|
20
|
+
export { CursorAdapter } from "./adapters/cursor-adapter";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Lasso runtime core — shared by the server (hosted sessions, colocated
|
|
4
|
+
* self-host) and the CLI LocalRuntime (local coding agents on the user's
|
|
5
|
+
* machine). Single implementation of the review-first agent workflow.
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
19
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.CursorAdapter = exports.CodexCLIAdapter = exports.OpenCodeAdapter = exports.ClaudeCodeAdapter = exports.newRunState = exports.EventSink = exports.LocalAgentAdapter = void 0;
|
|
23
|
+
__exportStar(require("./agent-runtime"), exports);
|
|
24
|
+
__exportStar(require("./backend-interface"), exports);
|
|
25
|
+
__exportStar(require("./workspace-guard"), exports);
|
|
26
|
+
__exportStar(require("./verification-engine"), exports);
|
|
27
|
+
__exportStar(require("./agent-state-machine"), exports);
|
|
28
|
+
__exportStar(require("./event-mapper"), exports);
|
|
29
|
+
__exportStar(require("./review"), exports);
|
|
30
|
+
__exportStar(require("./local-session-runner"), exports);
|
|
31
|
+
__exportStar(require("./adapters/factory"), exports);
|
|
32
|
+
var local_agent_adapter_1 = require("./adapters/local-agent-adapter");
|
|
33
|
+
Object.defineProperty(exports, "LocalAgentAdapter", { enumerable: true, get: function () { return local_agent_adapter_1.LocalAgentAdapter; } });
|
|
34
|
+
Object.defineProperty(exports, "EventSink", { enumerable: true, get: function () { return local_agent_adapter_1.EventSink; } });
|
|
35
|
+
Object.defineProperty(exports, "newRunState", { enumerable: true, get: function () { return local_agent_adapter_1.newRunState; } });
|
|
36
|
+
var claude_code_adapter_1 = require("./adapters/claude-code-adapter");
|
|
37
|
+
Object.defineProperty(exports, "ClaudeCodeAdapter", { enumerable: true, get: function () { return claude_code_adapter_1.ClaudeCodeAdapter; } });
|
|
38
|
+
var opencode_adapter_1 = require("./adapters/opencode-adapter");
|
|
39
|
+
Object.defineProperty(exports, "OpenCodeAdapter", { enumerable: true, get: function () { return opencode_adapter_1.OpenCodeAdapter; } });
|
|
40
|
+
var codex_cli_adapter_1 = require("./adapters/codex-cli-adapter");
|
|
41
|
+
Object.defineProperty(exports, "CodexCLIAdapter", { enumerable: true, get: function () { return codex_cli_adapter_1.CodexCLIAdapter; } });
|
|
42
|
+
var cursor_adapter_1 = require("./adapters/cursor-adapter");
|
|
43
|
+
Object.defineProperty(exports, "CursorAdapter", { enumerable: true, get: function () { return cursor_adapter_1.CursorAdapter; } });
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LocalSessionRunner — the shared Mode-B workflow for local coding agents.
|
|
3
|
+
*
|
|
4
|
+
* Encodes the non-negotiable local-agent lifecycle exactly once:
|
|
5
|
+
* backend.executeTask (agent owns its internal loop)
|
|
6
|
+
* → guard.diff (authoritative change detection)
|
|
7
|
+
* → review normalization (pendingChanges contract)
|
|
8
|
+
* → workspace rollback BEFORE review (finally safety net)
|
|
9
|
+
* → wire events (agent_completed / error) identical to the hosted SSE flow.
|
|
10
|
+
*
|
|
11
|
+
* Hosts (server orchestrator for colocated self-host, CLI LocalRuntime for
|
|
12
|
+
* the npm CLI) provide: a pre-captured WorkspaceGuard, the backend instance,
|
|
13
|
+
* the TaskContext, and an event sink. Persistence stays host-side.
|
|
14
|
+
*/
|
|
15
|
+
import { IBackend, TaskContext } from "./backend-interface";
|
|
16
|
+
import { AgentEvent, AgentAction, AgentError, AgentPhase, Observation } from "./agent-runtime";
|
|
17
|
+
import { WorkspaceGuard } from "./workspace-guard";
|
|
18
|
+
import { PendingChange } from "./review";
|
|
19
|
+
export interface LocalSessionOutcome {
|
|
20
|
+
/** Backend-reported success (terminal success signal + clean exit). */
|
|
21
|
+
success: boolean;
|
|
22
|
+
/** Session status to persist: waiting (review) | completed | failed. */
|
|
23
|
+
status: "waiting" | "completed" | "failed";
|
|
24
|
+
summary?: string;
|
|
25
|
+
/** Reviewable text changes (workspace already rolled back). */
|
|
26
|
+
changes: PendingChange[];
|
|
27
|
+
binaryPaths: string[];
|
|
28
|
+
/** Normalized actions replacing any backend-reported ones. */
|
|
29
|
+
actions: AgentAction[];
|
|
30
|
+
observations: Observation[];
|
|
31
|
+
errors: AgentError[];
|
|
32
|
+
conversation: Array<{
|
|
33
|
+
role: "user" | "assistant" | "system";
|
|
34
|
+
content: string;
|
|
35
|
+
timestamp: number;
|
|
36
|
+
}>;
|
|
37
|
+
externalSessionId?: string;
|
|
38
|
+
/** Suggested session metadata updates (backend provider/model/session id). */
|
|
39
|
+
metadata: Record<string, unknown>;
|
|
40
|
+
}
|
|
41
|
+
export interface LocalSessionRunnerOptions {
|
|
42
|
+
sessionId: string;
|
|
43
|
+
backend: IBackend;
|
|
44
|
+
/** Workspace captured BEFORE the agent ran. Consumed (diffed + restored). */
|
|
45
|
+
guard: WorkspaceGuard;
|
|
46
|
+
/** Sink for wire-format AgentEvents (forward to SSE / UI / in-memory). */
|
|
47
|
+
emit: (event: AgentEvent) => void;
|
|
48
|
+
/** Current phase, recorded on observations/errors. */
|
|
49
|
+
phase?: AgentPhase;
|
|
50
|
+
}
|
|
51
|
+
export declare class LocalSessionRunner {
|
|
52
|
+
private readonly options;
|
|
53
|
+
private restored;
|
|
54
|
+
constructor(options: LocalSessionRunnerOptions);
|
|
55
|
+
run(context: TaskContext, signal?: AbortSignal): Promise<LocalSessionOutcome>;
|
|
56
|
+
/** Diff + rollback in one step (restore happens before any review event). */
|
|
57
|
+
private finalizeReview;
|
|
58
|
+
/** WorkspaceGuard.restore is single-use; flag guards the safety-net double call. */
|
|
59
|
+
private restoreGuard;
|
|
60
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* LocalSessionRunner — the shared Mode-B workflow for local coding agents.
|
|
4
|
+
*
|
|
5
|
+
* Encodes the non-negotiable local-agent lifecycle exactly once:
|
|
6
|
+
* backend.executeTask (agent owns its internal loop)
|
|
7
|
+
* → guard.diff (authoritative change detection)
|
|
8
|
+
* → review normalization (pendingChanges contract)
|
|
9
|
+
* → workspace rollback BEFORE review (finally safety net)
|
|
10
|
+
* → wire events (agent_completed / error) identical to the hosted SSE flow.
|
|
11
|
+
*
|
|
12
|
+
* Hosts (server orchestrator for colocated self-host, CLI LocalRuntime for
|
|
13
|
+
* the npm CLI) provide: a pre-captured WorkspaceGuard, the backend instance,
|
|
14
|
+
* the TaskContext, and an event sink. Persistence stays host-side.
|
|
15
|
+
*/
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.LocalSessionRunner = void 0;
|
|
21
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
22
|
+
const review_1 = require("./review");
|
|
23
|
+
const event_mapper_1 = require("./event-mapper");
|
|
24
|
+
class LocalSessionRunner {
|
|
25
|
+
options;
|
|
26
|
+
restored = false;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.options = options;
|
|
29
|
+
}
|
|
30
|
+
async run(context, signal) {
|
|
31
|
+
const { sessionId, backend, guard, emit } = this.options;
|
|
32
|
+
const phase = this.options.phase || "execute";
|
|
33
|
+
let outcome;
|
|
34
|
+
try {
|
|
35
|
+
const result = await backend.executeTask(context, (event) => emit((0, event_mapper_1.toAgentEvent)(sessionId, event)), signal);
|
|
36
|
+
// Authoritative diff against the captured state, then rollback so the
|
|
37
|
+
// review happens against an untouched project.
|
|
38
|
+
const review = await this.finalizeReview();
|
|
39
|
+
const externalSessionId = result.metadata?.externalSessionId;
|
|
40
|
+
const metadata = {
|
|
41
|
+
backendProvider: backend.provider,
|
|
42
|
+
backendModel: backend.modelId,
|
|
43
|
+
...(typeof externalSessionId === "string" ? { backendSessionId: externalSessionId } : {}),
|
|
44
|
+
};
|
|
45
|
+
let status;
|
|
46
|
+
if (review.changes.length > 0) {
|
|
47
|
+
// Changes await user approval — nothing has been applied.
|
|
48
|
+
status = "waiting";
|
|
49
|
+
emit({
|
|
50
|
+
type: "agent_completed",
|
|
51
|
+
sessionId,
|
|
52
|
+
timestamp: Date.now(),
|
|
53
|
+
data: {
|
|
54
|
+
requiresReview: true,
|
|
55
|
+
pendingChanges: review.changes.length,
|
|
56
|
+
backend: backend.provider,
|
|
57
|
+
success: result.success,
|
|
58
|
+
summary: result.summary,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
else if (!result.success) {
|
|
63
|
+
status = "failed";
|
|
64
|
+
emit({
|
|
65
|
+
type: "error",
|
|
66
|
+
sessionId,
|
|
67
|
+
timestamp: Date.now(),
|
|
68
|
+
data: { error: result.summary || `${backend.provider} failed without producing changes` },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
status = "completed";
|
|
73
|
+
emit({
|
|
74
|
+
type: "agent_completed",
|
|
75
|
+
sessionId,
|
|
76
|
+
timestamp: Date.now(),
|
|
77
|
+
data: { requiresReview: false, summary: result.summary },
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (review.binaryPaths.length > 0) {
|
|
81
|
+
const observation = {
|
|
82
|
+
id: node_crypto_1.default.randomUUID(),
|
|
83
|
+
type: "file",
|
|
84
|
+
summary: `${review.binaryPaths.length} binary file change${review.binaryPaths.length === 1 ? "" : "s"} detected (not reviewable as text)`,
|
|
85
|
+
data: { binaryPaths: review.binaryPaths },
|
|
86
|
+
timestamp: Date.now(),
|
|
87
|
+
phase,
|
|
88
|
+
};
|
|
89
|
+
emit({ type: "observation", sessionId, timestamp: Date.now(), data: { summary: observation.summary } });
|
|
90
|
+
outcome = {
|
|
91
|
+
success: result.success,
|
|
92
|
+
status,
|
|
93
|
+
summary: result.summary,
|
|
94
|
+
changes: review.changes,
|
|
95
|
+
binaryPaths: review.binaryPaths,
|
|
96
|
+
actions: review.actions,
|
|
97
|
+
observations: [...result.observations, observation],
|
|
98
|
+
errors: [...result.errors],
|
|
99
|
+
conversation: result.conversation || [],
|
|
100
|
+
externalSessionId: typeof externalSessionId === "string" ? externalSessionId : undefined,
|
|
101
|
+
metadata,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
outcome = {
|
|
106
|
+
success: result.success,
|
|
107
|
+
status,
|
|
108
|
+
summary: result.summary,
|
|
109
|
+
changes: review.changes,
|
|
110
|
+
binaryPaths: [],
|
|
111
|
+
actions: review.actions,
|
|
112
|
+
observations: [...result.observations],
|
|
113
|
+
errors: [...result.errors],
|
|
114
|
+
conversation: result.conversation || [],
|
|
115
|
+
externalSessionId: typeof externalSessionId === "string" ? externalSessionId : undefined,
|
|
116
|
+
metadata,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
const message = error instanceof Error ? error.message : "Backend execution failed";
|
|
122
|
+
await this.restoreGuard();
|
|
123
|
+
outcome = {
|
|
124
|
+
success: false,
|
|
125
|
+
status: "failed",
|
|
126
|
+
changes: [],
|
|
127
|
+
binaryPaths: [],
|
|
128
|
+
actions: [],
|
|
129
|
+
observations: [],
|
|
130
|
+
errors: [
|
|
131
|
+
{
|
|
132
|
+
id: node_crypto_1.default.randomUUID(),
|
|
133
|
+
type: "execution",
|
|
134
|
+
message,
|
|
135
|
+
phase,
|
|
136
|
+
timestamp: Date.now(),
|
|
137
|
+
recoverable: false,
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
conversation: [],
|
|
141
|
+
metadata: {},
|
|
142
|
+
};
|
|
143
|
+
emit({
|
|
144
|
+
type: "error",
|
|
145
|
+
sessionId,
|
|
146
|
+
timestamp: Date.now(),
|
|
147
|
+
data: { error: message },
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
// Safety net: never leave un-reviewed changes on disk, even when the
|
|
152
|
+
// review path above already restored.
|
|
153
|
+
await this.restoreGuard();
|
|
154
|
+
}
|
|
155
|
+
return outcome;
|
|
156
|
+
}
|
|
157
|
+
/** Diff + rollback in one step (restore happens before any review event). */
|
|
158
|
+
async finalizeReview() {
|
|
159
|
+
const rawChanges = this.options.guard.diff();
|
|
160
|
+
await this.restoreGuard();
|
|
161
|
+
const review = (0, review_1.diffToReview)(rawChanges);
|
|
162
|
+
if (review.changes.length > 0) {
|
|
163
|
+
this.options.emit({
|
|
164
|
+
type: "file_changed",
|
|
165
|
+
sessionId: this.options.sessionId,
|
|
166
|
+
timestamp: Date.now(),
|
|
167
|
+
data: { changes: review.changes },
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return review;
|
|
171
|
+
}
|
|
172
|
+
/** WorkspaceGuard.restore is single-use; flag guards the safety-net double call. */
|
|
173
|
+
async restoreGuard() {
|
|
174
|
+
if (this.restored)
|
|
175
|
+
return;
|
|
176
|
+
this.restored = true;
|
|
177
|
+
try {
|
|
178
|
+
await this.options.guard.restore();
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
console.error("WorkspaceGuard restore failed:", error);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
exports.LocalSessionRunner = LocalSessionRunner;
|