@lasso-ai/cli 1.5.0 → 2.0.1
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,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lasso Agentic Runtime - Core Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* This file defines all the core contracts for the Lasso-owned agentic runtime.
|
|
5
|
+
* The model is an interchangeable intelligence provider; Lasso owns the loop.
|
|
6
|
+
*/
|
|
7
|
+
export type AgentPhase = "understand" | "inspect" | "plan" | "execute" | "verify" | "recover" | "complete";
|
|
8
|
+
export type AgentStatus = "running" | "waiting" | "completed" | "failed" | "cancelled";
|
|
9
|
+
export interface LassoAgentSession {
|
|
10
|
+
id: string;
|
|
11
|
+
phase: AgentPhase;
|
|
12
|
+
status: AgentStatus;
|
|
13
|
+
iteration: number;
|
|
14
|
+
userPrompt: string;
|
|
15
|
+
conversation: AgentMessage[];
|
|
16
|
+
observations: Observation[];
|
|
17
|
+
actions: AgentAction[];
|
|
18
|
+
completedActions: AgentAction[];
|
|
19
|
+
errors: AgentError[];
|
|
20
|
+
plan?: AgentPlan;
|
|
21
|
+
selection?: SelectionContext;
|
|
22
|
+
project: ProjectContext;
|
|
23
|
+
model: ModelMetadata;
|
|
24
|
+
createdAt: Date;
|
|
25
|
+
updatedAt: Date;
|
|
26
|
+
conversationUid?: string;
|
|
27
|
+
metadata?: Record<string, unknown>;
|
|
28
|
+
}
|
|
29
|
+
export interface AgentMessage {
|
|
30
|
+
id: string;
|
|
31
|
+
role: "user" | "assistant" | "system" | "tool";
|
|
32
|
+
content: string;
|
|
33
|
+
timestamp: number;
|
|
34
|
+
metadata?: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
export interface SelectionContext {
|
|
37
|
+
element?: {
|
|
38
|
+
tag: string;
|
|
39
|
+
text?: string;
|
|
40
|
+
source?: string;
|
|
41
|
+
attributes?: Record<string, string>;
|
|
42
|
+
};
|
|
43
|
+
viewport?: {
|
|
44
|
+
width: number;
|
|
45
|
+
height: number;
|
|
46
|
+
scrollY?: number;
|
|
47
|
+
scrollHeight?: number;
|
|
48
|
+
};
|
|
49
|
+
route?: string;
|
|
50
|
+
screenshot?: string;
|
|
51
|
+
}
|
|
52
|
+
export interface ProjectContext {
|
|
53
|
+
projectId: string;
|
|
54
|
+
framework?: string;
|
|
55
|
+
hasTypeScript: boolean;
|
|
56
|
+
hasTailwind: boolean;
|
|
57
|
+
hasReact: boolean;
|
|
58
|
+
hasNext: boolean;
|
|
59
|
+
hasVite: boolean;
|
|
60
|
+
files: string[];
|
|
61
|
+
dependencies?: string[];
|
|
62
|
+
devDependencies?: string[];
|
|
63
|
+
currentRoute?: string;
|
|
64
|
+
sourceHints?: Record<string, string>;
|
|
65
|
+
pageContext?: {
|
|
66
|
+
route?: string;
|
|
67
|
+
title?: string;
|
|
68
|
+
viewport?: {
|
|
69
|
+
width: number;
|
|
70
|
+
height: number;
|
|
71
|
+
scrollY?: number;
|
|
72
|
+
scrollHeight?: number;
|
|
73
|
+
};
|
|
74
|
+
domSummary?: {
|
|
75
|
+
headings?: Array<{
|
|
76
|
+
level: number;
|
|
77
|
+
text: string;
|
|
78
|
+
}>;
|
|
79
|
+
sections?: Array<{
|
|
80
|
+
name: string;
|
|
81
|
+
textPreview: string;
|
|
82
|
+
}>;
|
|
83
|
+
buttons?: string[];
|
|
84
|
+
links?: string[];
|
|
85
|
+
visibleTextSnippet?: string;
|
|
86
|
+
};
|
|
87
|
+
runtimeErrors?: string[];
|
|
88
|
+
selectedElement?: SelectionContext["element"];
|
|
89
|
+
screenshot?: string;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export interface AgentPlan {
|
|
93
|
+
id: string;
|
|
94
|
+
goal: string;
|
|
95
|
+
steps: PlanStep[];
|
|
96
|
+
estimatedActions?: number;
|
|
97
|
+
createdAt: Date;
|
|
98
|
+
}
|
|
99
|
+
export interface PlanStep {
|
|
100
|
+
id: string;
|
|
101
|
+
description: string;
|
|
102
|
+
status: "pending" | "in_progress" | "completed" | "failed";
|
|
103
|
+
actionCount?: number;
|
|
104
|
+
}
|
|
105
|
+
export type ObservationType = "file" | "search" | "command" | "browser" | "build" | "runtime" | "error" | "verification" | "tool";
|
|
106
|
+
export interface Observation {
|
|
107
|
+
id: string;
|
|
108
|
+
type: ObservationType;
|
|
109
|
+
summary: string;
|
|
110
|
+
data?: unknown;
|
|
111
|
+
timestamp: number;
|
|
112
|
+
phase?: AgentPhase;
|
|
113
|
+
actionId?: string;
|
|
114
|
+
}
|
|
115
|
+
export type AgentAction = ReadFileAction | SearchCodeAction | InspectProjectAction | EditFileAction | CreateFileAction | DeleteFileAction | RunCommandAction | BrowserCheckAction;
|
|
116
|
+
export interface BaseAction {
|
|
117
|
+
id: string;
|
|
118
|
+
data: Record<string, unknown>;
|
|
119
|
+
}
|
|
120
|
+
export interface ReadFileAction extends BaseAction {
|
|
121
|
+
type: "read_file";
|
|
122
|
+
}
|
|
123
|
+
export interface SearchCodeAction extends BaseAction {
|
|
124
|
+
type: "search_code";
|
|
125
|
+
}
|
|
126
|
+
export interface InspectProjectAction extends BaseAction {
|
|
127
|
+
type: "inspect_project";
|
|
128
|
+
}
|
|
129
|
+
export interface EditFileAction extends BaseAction {
|
|
130
|
+
type: "edit_file";
|
|
131
|
+
}
|
|
132
|
+
export interface CreateFileAction extends BaseAction {
|
|
133
|
+
type: "create_file";
|
|
134
|
+
}
|
|
135
|
+
export interface DeleteFileAction extends BaseAction {
|
|
136
|
+
type: "delete_file";
|
|
137
|
+
}
|
|
138
|
+
export interface RunCommandAction extends BaseAction {
|
|
139
|
+
type: "run_command";
|
|
140
|
+
}
|
|
141
|
+
export interface BrowserCheckAction extends BaseAction {
|
|
142
|
+
type: "browser_check";
|
|
143
|
+
}
|
|
144
|
+
export interface AgentError {
|
|
145
|
+
id: string;
|
|
146
|
+
type: "validation" | "execution" | "verification" | "timeout" | "permission" | "unknown";
|
|
147
|
+
message: string;
|
|
148
|
+
phase: AgentPhase;
|
|
149
|
+
actionId?: string;
|
|
150
|
+
timestamp: number;
|
|
151
|
+
details?: Record<string, unknown>;
|
|
152
|
+
recoverable: boolean;
|
|
153
|
+
}
|
|
154
|
+
export interface ModelMetadata {
|
|
155
|
+
provider: "anthropic" | "openai" | "google" | "nvidia" | "ollama" | "claude_code" | "opencode" | "codex_cli" | "cursor";
|
|
156
|
+
id: string;
|
|
157
|
+
name?: string;
|
|
158
|
+
}
|
|
159
|
+
export interface LassoAgentContext {
|
|
160
|
+
session: {
|
|
161
|
+
id: string;
|
|
162
|
+
phase: AgentPhase;
|
|
163
|
+
iteration: number;
|
|
164
|
+
};
|
|
165
|
+
userPrompt: string;
|
|
166
|
+
conversation: AgentMessage[];
|
|
167
|
+
selection?: SelectionContext;
|
|
168
|
+
project: ProjectContext;
|
|
169
|
+
observations: Observation[];
|
|
170
|
+
previousActions: AgentAction[];
|
|
171
|
+
errors: AgentError[];
|
|
172
|
+
currentPlan?: AgentPlan;
|
|
173
|
+
availableTools: ToolDefinition[];
|
|
174
|
+
constraints: AgentConstraints;
|
|
175
|
+
}
|
|
176
|
+
export interface ToolDefinition {
|
|
177
|
+
name: string;
|
|
178
|
+
description: string;
|
|
179
|
+
allowedPhases: AgentPhase[];
|
|
180
|
+
parameters?: Record<string, unknown>;
|
|
181
|
+
}
|
|
182
|
+
export interface AgentConstraints {
|
|
183
|
+
maxActionsPerTurn: number;
|
|
184
|
+
allowedPhases: AgentPhase[];
|
|
185
|
+
maxFileSize?: number;
|
|
186
|
+
allowedCommands?: string[];
|
|
187
|
+
forbiddenPaths?: string[];
|
|
188
|
+
safetyLevel?: "strict" | "standard" | "permissive";
|
|
189
|
+
}
|
|
190
|
+
export interface LassoModelResponse {
|
|
191
|
+
status: "continue" | "waiting" | "complete" | "failed";
|
|
192
|
+
reasoning: {
|
|
193
|
+
summary: string;
|
|
194
|
+
};
|
|
195
|
+
decision: {
|
|
196
|
+
type: "inspect" | "plan" | "execute" | "verify" | "recover" | "ask_user";
|
|
197
|
+
goal: string;
|
|
198
|
+
};
|
|
199
|
+
actions: AgentAction[];
|
|
200
|
+
expectedOutcome?: string;
|
|
201
|
+
}
|
|
202
|
+
export type AgentEventType = "agent_started" | "agent_reasoning" | "agent_completed" | "agent_failed" | "agent_cancelled" | "phase_changed" | "tool_started" | "tool_completed" | "observation" | "plan_created" | "file_changed" | "verification_started" | "verification_completed" | "error" | "model_changed" | "waiting_for_user";
|
|
203
|
+
export interface AgentEvent {
|
|
204
|
+
type: AgentEventType;
|
|
205
|
+
sessionId: string;
|
|
206
|
+
timestamp: number;
|
|
207
|
+
data?: Record<string, unknown>;
|
|
208
|
+
}
|
|
209
|
+
export interface AgentReasoningEvent extends AgentEvent {
|
|
210
|
+
type: "agent_reasoning";
|
|
211
|
+
data: {
|
|
212
|
+
summary: string;
|
|
213
|
+
decision?: string;
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
export interface PhaseChangedEvent extends AgentEvent {
|
|
217
|
+
type: "phase_changed";
|
|
218
|
+
data: {
|
|
219
|
+
from: AgentPhase;
|
|
220
|
+
to: AgentPhase;
|
|
221
|
+
reason?: string;
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
export interface ToolStartedEvent extends AgentEvent {
|
|
225
|
+
type: "tool_started";
|
|
226
|
+
data: {
|
|
227
|
+
actionType: string;
|
|
228
|
+
target?: string;
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
export interface ToolCompletedEvent extends AgentEvent {
|
|
232
|
+
type: "tool_completed";
|
|
233
|
+
data: {
|
|
234
|
+
actionType: string;
|
|
235
|
+
target?: string;
|
|
236
|
+
success: boolean;
|
|
237
|
+
duration?: number;
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
export interface ObservationEvent extends AgentEvent {
|
|
241
|
+
type: "observation";
|
|
242
|
+
data: {
|
|
243
|
+
observationType: ObservationType;
|
|
244
|
+
summary: string;
|
|
245
|
+
details?: unknown;
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
export interface VerificationStartedEvent extends AgentEvent {
|
|
249
|
+
type: "verification_started";
|
|
250
|
+
data: {
|
|
251
|
+
verificationType: "typecheck" | "build" | "runtime" | "browser";
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
export interface VerificationCompletedEvent extends AgentEvent {
|
|
255
|
+
type: "verification_completed";
|
|
256
|
+
data: {
|
|
257
|
+
verificationType: "typecheck" | "build" | "runtime" | "browser";
|
|
258
|
+
success: boolean;
|
|
259
|
+
duration?: number;
|
|
260
|
+
errors?: string[];
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
export interface ErrorEvent extends AgentEvent {
|
|
264
|
+
type: "error";
|
|
265
|
+
data: {
|
|
266
|
+
errorType: string;
|
|
267
|
+
message: string;
|
|
268
|
+
phase: AgentPhase;
|
|
269
|
+
recoverable: boolean;
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
export interface ModelChangedEvent extends AgentEvent {
|
|
273
|
+
type: "model_changed";
|
|
274
|
+
data: {
|
|
275
|
+
from: ModelMetadata;
|
|
276
|
+
to: ModelMetadata;
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
export interface WaitingForUserEvent extends AgentEvent {
|
|
280
|
+
type: "waiting_for_user";
|
|
281
|
+
data: {
|
|
282
|
+
question?: string;
|
|
283
|
+
options?: string[];
|
|
284
|
+
kind?: "input" | "single_select" | "multiselect";
|
|
285
|
+
};
|
|
286
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Lasso Agentic Runtime - Core Type Definitions
|
|
4
|
+
*
|
|
5
|
+
* This file defines all the core contracts for the Lasso-owned agentic runtime.
|
|
6
|
+
* The model is an interchangeable intelligence provider; Lasso owns the loop.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { AgentPhase, LassoAgentSession, AgentAction } from "./agent-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Lasso Agent State Machine
|
|
4
|
+
*
|
|
5
|
+
* Controls phase transitions and validates state changes.
|
|
6
|
+
* Lasso owns the transitions; the model can only suggest decisions.
|
|
7
|
+
*/
|
|
8
|
+
export declare class AgentStateMachine {
|
|
9
|
+
/**
|
|
10
|
+
* Valid phase transitions based on the current state
|
|
11
|
+
*/
|
|
12
|
+
private static readonly TRANSITIONS;
|
|
13
|
+
/**
|
|
14
|
+
* Actions allowed in each phase
|
|
15
|
+
*/
|
|
16
|
+
private static readonly ALLOWED_ACTIONS;
|
|
17
|
+
/**
|
|
18
|
+
* Check if a phase transition is valid
|
|
19
|
+
*/
|
|
20
|
+
static canTransition(session: LassoAgentSession, toPhase: AgentPhase): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Attempt to transition to a new phase
|
|
23
|
+
* Returns success status and error message if invalid
|
|
24
|
+
*/
|
|
25
|
+
static transition(session: LassoAgentSession, toPhase: AgentPhase, reason?: string): {
|
|
26
|
+
success: boolean;
|
|
27
|
+
error?: string;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Validate that an action is allowed in the current phase
|
|
31
|
+
*/
|
|
32
|
+
static validateAction(session: LassoAgentSession, action: AgentAction): {
|
|
33
|
+
valid: boolean;
|
|
34
|
+
error?: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Determine the next phase based on current state and model decision
|
|
38
|
+
*/
|
|
39
|
+
static determineNextPhase(session: LassoAgentSession, modelDecision: {
|
|
40
|
+
type: string;
|
|
41
|
+
goal: string;
|
|
42
|
+
}): AgentPhase;
|
|
43
|
+
/**
|
|
44
|
+
* Check if the session is in a terminal state
|
|
45
|
+
*/
|
|
46
|
+
static isTerminal(session: LassoAgentSession): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Check if the session can accept new actions
|
|
49
|
+
*/
|
|
50
|
+
static canAcceptActions(session: LassoAgentSession): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Check if the session should trigger verification
|
|
53
|
+
*/
|
|
54
|
+
static shouldVerify(session: LassoAgentSession): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Check if the session should trigger recovery
|
|
57
|
+
*/
|
|
58
|
+
static shouldRecover(session: LassoAgentSession): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Check if the session can switch models
|
|
61
|
+
*/
|
|
62
|
+
static canSwitchModel(session: LassoAgentSession): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Calculate the estimated number of remaining turns
|
|
65
|
+
*/
|
|
66
|
+
static estimateRemainingTurns(session: LassoAgentSession): number;
|
|
67
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AgentStateMachine = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Lasso Agent State Machine
|
|
6
|
+
*
|
|
7
|
+
* Controls phase transitions and validates state changes.
|
|
8
|
+
* Lasso owns the transitions; the model can only suggest decisions.
|
|
9
|
+
*/
|
|
10
|
+
class AgentStateMachine {
|
|
11
|
+
/**
|
|
12
|
+
* Valid phase transitions based on the current state
|
|
13
|
+
*/
|
|
14
|
+
static TRANSITIONS = {
|
|
15
|
+
understand: ["inspect", "complete"],
|
|
16
|
+
inspect: ["plan", "understand", "complete"],
|
|
17
|
+
plan: ["execute", "inspect", "understand", "complete"],
|
|
18
|
+
execute: ["verify", "plan", "inspect", "understand"],
|
|
19
|
+
verify: ["complete", "recover", "execute", "plan"],
|
|
20
|
+
recover: ["inspect", "plan", "execute", "verify", "understand"],
|
|
21
|
+
complete: [], // Terminal state
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Actions allowed in each phase
|
|
25
|
+
*/
|
|
26
|
+
static ALLOWED_ACTIONS = {
|
|
27
|
+
understand: [],
|
|
28
|
+
inspect: ["read_file", "search_code", "inspect_project"],
|
|
29
|
+
plan: [],
|
|
30
|
+
execute: ["edit_file", "create_file", "delete_file", "run_command"],
|
|
31
|
+
verify: ["browser_check", "run_command"],
|
|
32
|
+
recover: ["edit_file", "read_file", "run_command"],
|
|
33
|
+
complete: [],
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Check if a phase transition is valid
|
|
37
|
+
*/
|
|
38
|
+
static canTransition(session, toPhase) {
|
|
39
|
+
const currentPhase = session.phase;
|
|
40
|
+
const allowed = this.TRANSITIONS[currentPhase] || [];
|
|
41
|
+
return allowed.includes(toPhase);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Attempt to transition to a new phase
|
|
45
|
+
* Returns success status and error message if invalid
|
|
46
|
+
*/
|
|
47
|
+
static transition(session, toPhase, reason) {
|
|
48
|
+
if (!this.canTransition(session, toPhase)) {
|
|
49
|
+
return {
|
|
50
|
+
success: false,
|
|
51
|
+
error: `Invalid phase transition from ${session.phase} to ${toPhase}`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Special checks for specific transitions
|
|
55
|
+
if (toPhase === "complete") {
|
|
56
|
+
if (session.actions.length === 0 && session.completedActions.length === 0) {
|
|
57
|
+
return {
|
|
58
|
+
success: false,
|
|
59
|
+
error: "Cannot complete without executing any actions",
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (session.status === "failed") {
|
|
63
|
+
return {
|
|
64
|
+
success: false,
|
|
65
|
+
error: "Cannot complete a failed session",
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (toPhase === "recover") {
|
|
70
|
+
if (session.errors.length === 0) {
|
|
71
|
+
return {
|
|
72
|
+
success: false,
|
|
73
|
+
error: "Cannot enter recovery phase without errors",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { success: true };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Validate that an action is allowed in the current phase
|
|
81
|
+
*/
|
|
82
|
+
static validateAction(session, action) {
|
|
83
|
+
const allowedActions = this.ALLOWED_ACTIONS[session.phase] || [];
|
|
84
|
+
const actionType = action.type;
|
|
85
|
+
if (!allowedActions.includes(actionType)) {
|
|
86
|
+
return {
|
|
87
|
+
valid: false,
|
|
88
|
+
error: `Action type "${actionType}" is not allowed in phase "${session.phase}"`,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
// Additional validation for specific actions
|
|
92
|
+
if (action.type === "edit_file") {
|
|
93
|
+
const data = action.data;
|
|
94
|
+
if (!data.oldString && !data.newString) {
|
|
95
|
+
return {
|
|
96
|
+
valid: false,
|
|
97
|
+
error: "Edit action must have oldString or newString",
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (action.type === "run_command") {
|
|
102
|
+
const data = action.data;
|
|
103
|
+
if (!data.command || !data.command.trim()) {
|
|
104
|
+
return {
|
|
105
|
+
valid: false,
|
|
106
|
+
error: "Run command action must have a command",
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { valid: true };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Determine the next phase based on current state and model decision
|
|
114
|
+
*/
|
|
115
|
+
static determineNextPhase(session, modelDecision) {
|
|
116
|
+
const currentPhase = session.phase;
|
|
117
|
+
const decisionType = modelDecision.type;
|
|
118
|
+
// If model explicitly asks for a phase, validate it
|
|
119
|
+
if (decisionType === "inspect" && this.canTransition(session, "inspect")) {
|
|
120
|
+
return "inspect";
|
|
121
|
+
}
|
|
122
|
+
if (decisionType === "plan" && this.canTransition(session, "plan")) {
|
|
123
|
+
return "plan";
|
|
124
|
+
}
|
|
125
|
+
if (decisionType === "execute" && this.canTransition(session, "execute")) {
|
|
126
|
+
return "execute";
|
|
127
|
+
}
|
|
128
|
+
if (decisionType === "verify" && this.canTransition(session, "verify")) {
|
|
129
|
+
return "verify";
|
|
130
|
+
}
|
|
131
|
+
if (decisionType === "recover" && this.canTransition(session, "recover")) {
|
|
132
|
+
return "recover";
|
|
133
|
+
}
|
|
134
|
+
// Default progression based on current phase
|
|
135
|
+
switch (currentPhase) {
|
|
136
|
+
case "understand":
|
|
137
|
+
return "inspect";
|
|
138
|
+
case "inspect":
|
|
139
|
+
// If we have observations, move to plan
|
|
140
|
+
if (session.observations.length > 0) {
|
|
141
|
+
return "plan";
|
|
142
|
+
}
|
|
143
|
+
return "inspect";
|
|
144
|
+
case "plan":
|
|
145
|
+
// If we have a plan, move to execute
|
|
146
|
+
if (session.plan) {
|
|
147
|
+
return "execute";
|
|
148
|
+
}
|
|
149
|
+
return "plan";
|
|
150
|
+
case "execute":
|
|
151
|
+
// If we have completed actions, move to verify
|
|
152
|
+
if (session.completedActions.length > 0) {
|
|
153
|
+
return "verify";
|
|
154
|
+
}
|
|
155
|
+
return "execute";
|
|
156
|
+
case "verify":
|
|
157
|
+
// If verification succeeded, complete; if failed, recover
|
|
158
|
+
const hasErrors = session.errors.some((e) => e.phase === "verify");
|
|
159
|
+
if (hasErrors) {
|
|
160
|
+
return "recover";
|
|
161
|
+
}
|
|
162
|
+
return "complete";
|
|
163
|
+
case "recover":
|
|
164
|
+
// After recovery attempt, go back to verify
|
|
165
|
+
return "verify";
|
|
166
|
+
case "complete":
|
|
167
|
+
return "complete";
|
|
168
|
+
default:
|
|
169
|
+
return "understand";
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Check if the session is in a terminal state
|
|
174
|
+
*/
|
|
175
|
+
static isTerminal(session) {
|
|
176
|
+
return session.phase === "complete" || session.status === "failed" || session.status === "cancelled";
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Check if the session can accept new actions
|
|
180
|
+
*/
|
|
181
|
+
static canAcceptActions(session) {
|
|
182
|
+
return (session.status === "running" &&
|
|
183
|
+
session.phase !== "complete" &&
|
|
184
|
+
session.phase !== "understand");
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Check if the session should trigger verification
|
|
188
|
+
*/
|
|
189
|
+
static shouldVerify(session) {
|
|
190
|
+
return (session.phase === "execute" &&
|
|
191
|
+
session.completedActions.length > 0 &&
|
|
192
|
+
session.errors.filter((e) => e.phase === "execute").length === 0);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Check if the session should trigger recovery
|
|
196
|
+
*/
|
|
197
|
+
static shouldRecover(session) {
|
|
198
|
+
return (session.phase === "verify" &&
|
|
199
|
+
session.errors.filter((e) => e.phase === "verify").length > 0);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Check if the session can switch models
|
|
203
|
+
*/
|
|
204
|
+
static canSwitchModel(session) {
|
|
205
|
+
return session.status === "running" && session.phase !== "complete";
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Calculate the estimated number of remaining turns
|
|
209
|
+
*/
|
|
210
|
+
static estimateRemainingTurns(session) {
|
|
211
|
+
const { phase, plan, completedActions, errors } = session;
|
|
212
|
+
if (phase === "complete")
|
|
213
|
+
return 0;
|
|
214
|
+
if (phase === "understand")
|
|
215
|
+
return 5; // understand → inspect → plan → execute → verify
|
|
216
|
+
if (phase === "inspect")
|
|
217
|
+
return 4; // inspect → plan → execute → verify
|
|
218
|
+
if (phase === "plan")
|
|
219
|
+
return 3; // plan → execute → verify
|
|
220
|
+
if (phase === "execute") {
|
|
221
|
+
const remaining = (plan?.estimatedActions || 5) - completedActions.length;
|
|
222
|
+
return Math.max(remaining + 1, 2); // execute → verify
|
|
223
|
+
}
|
|
224
|
+
if (phase === "verify") {
|
|
225
|
+
if (errors.length > 0)
|
|
226
|
+
return 3; // verify → recover → verify
|
|
227
|
+
return 1; // verify → complete
|
|
228
|
+
}
|
|
229
|
+
if (phase === "recover")
|
|
230
|
+
return 2; // recover → verify
|
|
231
|
+
return 5;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
exports.AgentStateMachine = AgentStateMachine;
|