agent-state-machine 2.0.15 → 2.1.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/bin/cli.js +1 -1
- package/lib/index.js +33 -0
- package/lib/remote/client.js +7 -2
- package/lib/runtime/agent.js +102 -67
- package/lib/runtime/index.js +13 -0
- package/lib/runtime/interaction.js +304 -0
- package/lib/runtime/prompt.js +39 -12
- package/lib/runtime/runtime.js +11 -10
- package/package.json +1 -1
- package/templates/project-builder/agents/assumptions-clarifier.md +0 -1
- package/templates/project-builder/agents/code-reviewer.md +0 -1
- package/templates/project-builder/agents/code-writer.md +0 -1
- package/templates/project-builder/agents/requirements-clarifier.md +0 -1
- package/templates/project-builder/agents/response-interpreter.md +25 -0
- package/templates/project-builder/agents/roadmap-generator.md +0 -1
- package/templates/project-builder/agents/sanity-checker.md +45 -0
- package/templates/project-builder/agents/sanity-runner.js +161 -0
- package/templates/project-builder/agents/scope-clarifier.md +0 -1
- package/templates/project-builder/agents/security-clarifier.md +0 -1
- package/templates/project-builder/agents/security-reviewer.md +0 -1
- package/templates/project-builder/agents/task-planner.md +0 -1
- package/templates/project-builder/agents/test-planner.md +0 -1
- package/templates/project-builder/scripts/interaction-helpers.js +33 -0
- package/templates/project-builder/scripts/workflow-helpers.js +2 -47
- package/templates/project-builder/workflow.js +214 -54
- package/vercel-server/api/session/[token].js +3 -3
- package/vercel-server/api/submit/[token].js +5 -3
- package/vercel-server/local-server.js +33 -6
- package/vercel-server/public/remote/index.html +17 -0
- package/vercel-server/ui/index.html +9 -1012
- package/vercel-server/ui/package-lock.json +2650 -0
- package/vercel-server/ui/package.json +25 -0
- package/vercel-server/ui/postcss.config.js +6 -0
- package/vercel-server/ui/src/App.jsx +236 -0
- package/vercel-server/ui/src/components/ChoiceInteraction.jsx +127 -0
- package/vercel-server/ui/src/components/ConfirmInteraction.jsx +51 -0
- package/vercel-server/ui/src/components/ContentCard.jsx +161 -0
- package/vercel-server/ui/src/components/CopyButton.jsx +27 -0
- package/vercel-server/ui/src/components/EventsLog.jsx +82 -0
- package/vercel-server/ui/src/components/Footer.jsx +66 -0
- package/vercel-server/ui/src/components/Header.jsx +38 -0
- package/vercel-server/ui/src/components/InteractionForm.jsx +42 -0
- package/vercel-server/ui/src/components/TextInteraction.jsx +72 -0
- package/vercel-server/ui/src/index.css +145 -0
- package/vercel-server/ui/src/main.jsx +8 -0
- package/vercel-server/ui/tailwind.config.js +19 -0
- package/vercel-server/ui/vite.config.js +11 -0
package/bin/cli.js
CHANGED
|
@@ -394,7 +394,7 @@ async function runOrResume(
|
|
|
394
394
|
// Enable remote follow mode if we have a URL
|
|
395
395
|
if (remoteUrl) {
|
|
396
396
|
const sessionToken = ensureRemotePath(configFile, { forceNew: forceNewRemotePath });
|
|
397
|
-
await runtime.enableRemote(remoteUrl, { sessionToken });
|
|
397
|
+
await runtime.enableRemote(remoteUrl, { sessionToken, uiBaseUrl: useLocalServer });
|
|
398
398
|
}
|
|
399
399
|
|
|
400
400
|
try {
|
package/lib/index.js
CHANGED
|
@@ -12,6 +12,17 @@ import {
|
|
|
12
12
|
agent,
|
|
13
13
|
executeAgent,
|
|
14
14
|
askHuman,
|
|
15
|
+
InteractionSchema,
|
|
16
|
+
InteractionResponseSchema,
|
|
17
|
+
normalizeInteraction,
|
|
18
|
+
validateInteraction,
|
|
19
|
+
normalizeInteractionResponse,
|
|
20
|
+
validateInteractionResponse,
|
|
21
|
+
createInteraction,
|
|
22
|
+
formatInteractionPrompt,
|
|
23
|
+
matchSingleSelect,
|
|
24
|
+
matchMultiSelect,
|
|
25
|
+
parseInteractionResponse,
|
|
15
26
|
parallel,
|
|
16
27
|
parallelLimit,
|
|
17
28
|
getMemory,
|
|
@@ -83,6 +94,17 @@ export {
|
|
|
83
94
|
agent,
|
|
84
95
|
executeAgent,
|
|
85
96
|
askHuman,
|
|
97
|
+
InteractionSchema,
|
|
98
|
+
InteractionResponseSchema,
|
|
99
|
+
normalizeInteraction,
|
|
100
|
+
validateInteraction,
|
|
101
|
+
normalizeInteractionResponse,
|
|
102
|
+
validateInteractionResponse,
|
|
103
|
+
createInteraction,
|
|
104
|
+
formatInteractionPrompt,
|
|
105
|
+
matchSingleSelect,
|
|
106
|
+
matchMultiSelect,
|
|
107
|
+
parseInteractionResponse,
|
|
86
108
|
parallel,
|
|
87
109
|
parallelLimit,
|
|
88
110
|
getCurrentRuntime,
|
|
@@ -100,6 +122,17 @@ const api = {
|
|
|
100
122
|
agent,
|
|
101
123
|
executeAgent,
|
|
102
124
|
askHuman,
|
|
125
|
+
InteractionSchema,
|
|
126
|
+
InteractionResponseSchema,
|
|
127
|
+
normalizeInteraction,
|
|
128
|
+
validateInteraction,
|
|
129
|
+
normalizeInteractionResponse,
|
|
130
|
+
validateInteractionResponse,
|
|
131
|
+
createInteraction,
|
|
132
|
+
formatInteractionPrompt,
|
|
133
|
+
matchSingleSelect,
|
|
134
|
+
matchMultiSelect,
|
|
135
|
+
parseInteractionResponse,
|
|
103
136
|
parallel,
|
|
104
137
|
parallelLimit,
|
|
105
138
|
getCurrentRuntime,
|
package/lib/remote/client.js
CHANGED
|
@@ -81,12 +81,14 @@ export class RemoteClient {
|
|
|
81
81
|
* @param {function} options.onInteractionResponse - Callback when interaction response received
|
|
82
82
|
* @param {function} [options.onStatusChange] - Callback when connection status changes
|
|
83
83
|
* @param {string} [options.sessionToken] - Optional session token to reuse
|
|
84
|
+
* @param {boolean} [options.uiBaseUrl] - If true, return base URL for UI instead of /s/{token}
|
|
84
85
|
*/
|
|
85
86
|
constructor(options) {
|
|
86
87
|
this.serverUrl = options.serverUrl.replace(/\/$/, ''); // Remove trailing slash
|
|
87
88
|
this.workflowName = options.workflowName;
|
|
88
89
|
this.onInteractionResponse = options.onInteractionResponse;
|
|
89
90
|
this.onStatusChange = options.onStatusChange || (() => {});
|
|
91
|
+
this.uiBaseUrl = Boolean(options.uiBaseUrl);
|
|
90
92
|
|
|
91
93
|
this.sessionToken = options.sessionToken || generateSessionToken();
|
|
92
94
|
this.connected = false;
|
|
@@ -99,6 +101,9 @@ export class RemoteClient {
|
|
|
99
101
|
* Get the full remote URL for browser access
|
|
100
102
|
*/
|
|
101
103
|
getRemoteUrl() {
|
|
104
|
+
if (this.uiBaseUrl) {
|
|
105
|
+
return this.serverUrl;
|
|
106
|
+
}
|
|
102
107
|
return `${this.serverUrl}/s/${this.sessionToken}`;
|
|
103
108
|
}
|
|
104
109
|
|
|
@@ -187,9 +192,9 @@ export class RemoteClient {
|
|
|
187
192
|
}
|
|
188
193
|
|
|
189
194
|
await this.send({
|
|
190
|
-
type: 'event',
|
|
191
|
-
sessionToken: this.sessionToken,
|
|
192
195
|
...event,
|
|
196
|
+
type: 'event', // Must come after spread to not be overwritten by event.type
|
|
197
|
+
sessionToken: this.sessionToken,
|
|
193
198
|
});
|
|
194
199
|
}
|
|
195
200
|
|
package/lib/runtime/agent.js
CHANGED
|
@@ -11,6 +11,7 @@ import path from 'path';
|
|
|
11
11
|
import { createRequire } from 'module';
|
|
12
12
|
import { pathToFileURL } from 'url';
|
|
13
13
|
import { getCurrentRuntime } from './runtime.js';
|
|
14
|
+
import { formatInteractionPrompt } from './interaction.js';
|
|
14
15
|
|
|
15
16
|
const require = createRequire(import.meta.url);
|
|
16
17
|
|
|
@@ -48,12 +49,7 @@ export async function agent(name, params = {}, options = {}) {
|
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
console.log(` [Agent: ${name}] Completed`);
|
|
51
|
-
|
|
52
|
-
runtime._agentSuppressCompletion.delete(name);
|
|
53
|
-
return result;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
runtime.prependHistory({
|
|
52
|
+
await runtime.prependHistory({
|
|
57
53
|
event: 'AGENT_COMPLETED',
|
|
58
54
|
agent: name,
|
|
59
55
|
output: result,
|
|
@@ -66,7 +62,7 @@ export async function agent(name, params = {}, options = {}) {
|
|
|
66
62
|
|
|
67
63
|
if (attempt < retryCount) {
|
|
68
64
|
console.error(` [Agent: ${name}] Error (attempt ${attempt + 1}/${retryCount + 1}): ${error.message}`);
|
|
69
|
-
runtime.prependHistory({
|
|
65
|
+
await runtime.prependHistory({
|
|
70
66
|
event: 'AGENT_RETRY',
|
|
71
67
|
agent: name,
|
|
72
68
|
attempt: attempt + 1,
|
|
@@ -77,7 +73,7 @@ export async function agent(name, params = {}, options = {}) {
|
|
|
77
73
|
}
|
|
78
74
|
|
|
79
75
|
// All retries exhausted - record failure
|
|
80
|
-
runtime.prependHistory({
|
|
76
|
+
await runtime.prependHistory({
|
|
81
77
|
event: 'AGENT_FAILED',
|
|
82
78
|
agent: name,
|
|
83
79
|
error: lastError.message,
|
|
@@ -154,7 +150,7 @@ async function executeJSAgent(runtime, agentPath, name, params, options = {}) {
|
|
|
154
150
|
throw new Error(`Agent ${name} does not export a function`);
|
|
155
151
|
}
|
|
156
152
|
|
|
157
|
-
logAgentStart(runtime, name);
|
|
153
|
+
await logAgentStart(runtime, name);
|
|
158
154
|
|
|
159
155
|
// Build steering context (global + any additional files from options)
|
|
160
156
|
const steeringContext = options.steering
|
|
@@ -172,25 +168,22 @@ async function executeJSAgent(runtime, agentPath, name, params, options = {}) {
|
|
|
172
168
|
}
|
|
173
169
|
};
|
|
174
170
|
|
|
175
|
-
|
|
171
|
+
let result = await handler(context);
|
|
172
|
+
let interactionDepth = 0;
|
|
176
173
|
|
|
177
|
-
// Handle interaction response from JS agent
|
|
178
|
-
|
|
174
|
+
// Handle interaction response from JS agent (support multiple rounds)
|
|
175
|
+
while (result && result._interaction) {
|
|
179
176
|
const interactionResponse = await handleInteraction(runtime, result._interaction, name);
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
// but JS agents are more flexible.
|
|
187
|
-
// Let's assume the agent wants the response mixed in or as the return.
|
|
188
|
-
// A safe bet is to return the response if the agent explicitly asked for interaction.
|
|
189
|
-
return { ...result, result: interactionResponse };
|
|
177
|
+
const resumedContext = { ...context, userResponse: interactionResponse };
|
|
178
|
+
await logAgentStart(runtime, name);
|
|
179
|
+
result = await handler(resumedContext);
|
|
180
|
+
interactionDepth += 1;
|
|
181
|
+
if (interactionDepth > 5) {
|
|
182
|
+
throw new Error(`Agent ${name} exceeded maximum interaction depth`);
|
|
190
183
|
}
|
|
191
|
-
return interactionResponse;
|
|
192
184
|
}
|
|
193
185
|
|
|
186
|
+
|
|
194
187
|
// Clean internal properties from result
|
|
195
188
|
if (result && typeof result === 'object') {
|
|
196
189
|
const cleanResult = { ...result };
|
|
@@ -222,8 +215,7 @@ async function executeMDAgent(runtime, agentPath, name, params, options = {}) {
|
|
|
222
215
|
const content = fs.readFileSync(agentPath, 'utf-8');
|
|
223
216
|
const { config, prompt } = parseMarkdownAgent(content);
|
|
224
217
|
|
|
225
|
-
const outputKey = config.output
|
|
226
|
-
const targetKey = config.interactionKey || outputKey;
|
|
218
|
+
const outputKey = config.output;
|
|
227
219
|
|
|
228
220
|
// Combine steering from options (runtime call) and frontmatter (static)
|
|
229
221
|
let steeringNames = [];
|
|
@@ -243,46 +235,65 @@ async function executeMDAgent(runtime, agentPath, name, params, options = {}) {
|
|
|
243
235
|
? runtime.loadSteeringFiles(steeringNames)
|
|
244
236
|
: runtime.steering;
|
|
245
237
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
238
|
+
let response = null;
|
|
239
|
+
let output = null;
|
|
240
|
+
let interactionDepth = 0;
|
|
241
|
+
let currentParams = params;
|
|
242
|
+
|
|
243
|
+
while (true) {
|
|
244
|
+
// Build context - only spread params, NOT memory (explicit context passing)
|
|
245
|
+
const context = {
|
|
246
|
+
...currentParams,
|
|
247
|
+
_steering: steeringContext,
|
|
248
|
+
_config: {
|
|
249
|
+
models: runtime.workflowConfig.models,
|
|
250
|
+
apiKeys: runtime.workflowConfig.apiKeys,
|
|
251
|
+
workflowDir: runtime.workflowDir
|
|
252
|
+
}
|
|
253
|
+
};
|
|
256
254
|
|
|
257
|
-
|
|
258
|
-
|
|
255
|
+
// Interpolate variables in prompt
|
|
256
|
+
const interpolatedPrompt = interpolatePrompt(prompt, context);
|
|
259
257
|
|
|
260
|
-
|
|
258
|
+
const model = config.model || 'fast';
|
|
261
259
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
260
|
+
const fullPrompt = buildPrompt(context, {
|
|
261
|
+
model,
|
|
262
|
+
prompt: interpolatedPrompt,
|
|
263
|
+
includeContext: config.includeContext !== 'false'
|
|
264
|
+
});
|
|
267
265
|
|
|
268
|
-
|
|
266
|
+
await logAgentStart(runtime, name, fullPrompt);
|
|
269
267
|
|
|
270
|
-
|
|
268
|
+
console.log(` Using model: ${model}`);
|
|
271
269
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
270
|
+
response = await llm(context, {
|
|
271
|
+
model: model,
|
|
272
|
+
prompt: interpolatedPrompt,
|
|
273
|
+
includeContext: config.includeContext !== 'false'
|
|
274
|
+
});
|
|
277
275
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
276
|
+
// Parse output based on format
|
|
277
|
+
output = response.text;
|
|
278
|
+
if (config.format === 'json') {
|
|
279
|
+
try {
|
|
280
|
+
output = parseJSON(response.text);
|
|
281
|
+
} catch {
|
|
282
|
+
console.warn(` Warning: Failed to parse JSON output`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (output && typeof output === 'object' && output._interaction) {
|
|
287
|
+
const interactionResponse = await handleInteraction(runtime, output._interaction, name);
|
|
288
|
+
currentParams = { ...params, userResponse: interactionResponse };
|
|
289
|
+
interactionDepth += 1;
|
|
290
|
+
if (interactionDepth > 5) {
|
|
291
|
+
throw new Error(`Agent ${name} exceeded maximum interaction depth`);
|
|
292
|
+
}
|
|
293
|
+
continue;
|
|
285
294
|
}
|
|
295
|
+
|
|
296
|
+
break;
|
|
286
297
|
}
|
|
287
298
|
|
|
288
299
|
// Check for interaction request
|
|
@@ -302,7 +313,6 @@ async function executeMDAgent(runtime, agentPath, name, params, options = {}) {
|
|
|
302
313
|
: null) ||
|
|
303
314
|
config.interactionSlug ||
|
|
304
315
|
config.interactionKey ||
|
|
305
|
-
outputKey ||
|
|
306
316
|
name;
|
|
307
317
|
|
|
308
318
|
const slug = sanitizeSlug(slugRaw);
|
|
@@ -316,11 +326,19 @@ async function executeMDAgent(runtime, agentPath, name, params, options = {}) {
|
|
|
316
326
|
}, name);
|
|
317
327
|
|
|
318
328
|
// Return the user's response as the agent result
|
|
319
|
-
|
|
329
|
+
if (outputKey) {
|
|
330
|
+
return { [outputKey]: userResponse, _debug_prompt: response.fullPrompt };
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return userResponse;
|
|
320
334
|
}
|
|
321
335
|
|
|
322
336
|
// Return result object
|
|
323
|
-
|
|
337
|
+
if (outputKey) {
|
|
338
|
+
return { [outputKey]: output, _debug_prompt: response.fullPrompt };
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return output;
|
|
324
342
|
}
|
|
325
343
|
|
|
326
344
|
/**
|
|
@@ -435,7 +453,16 @@ async function handleInteraction(runtime, interaction, agentName) {
|
|
|
435
453
|
|
|
436
454
|
const slug = sanitizeSlug(interaction.slug);
|
|
437
455
|
const targetKey = String(interaction.targetKey || slug);
|
|
438
|
-
const
|
|
456
|
+
const prompt = String(
|
|
457
|
+
interaction.prompt ??
|
|
458
|
+
interaction.content ??
|
|
459
|
+
interaction.question ??
|
|
460
|
+
''
|
|
461
|
+
).trim();
|
|
462
|
+
const content = formatInteractionPrompt({
|
|
463
|
+
...interaction,
|
|
464
|
+
prompt
|
|
465
|
+
});
|
|
439
466
|
|
|
440
467
|
const filePath = path.join(runtime.interactionsDir, `${slug}.md`);
|
|
441
468
|
|
|
@@ -448,15 +475,23 @@ ${content}
|
|
|
448
475
|
|
|
449
476
|
fs.writeFileSync(filePath, fileContent);
|
|
450
477
|
|
|
451
|
-
runtime.prependHistory({
|
|
478
|
+
await runtime.prependHistory({
|
|
452
479
|
event: 'INTERACTION_REQUESTED',
|
|
453
480
|
slug,
|
|
454
481
|
targetKey,
|
|
455
|
-
question: content
|
|
482
|
+
question: prompt || content,
|
|
483
|
+
type: interaction.type || 'text',
|
|
484
|
+
prompt,
|
|
485
|
+
options: interaction.options,
|
|
486
|
+
allowCustom: interaction.allowCustom,
|
|
487
|
+
multiSelect: interaction.multiSelect,
|
|
488
|
+
validation: interaction.validation,
|
|
489
|
+
confirmLabel: interaction.confirmLabel,
|
|
490
|
+
cancelLabel: interaction.cancelLabel,
|
|
491
|
+
context: interaction.context
|
|
456
492
|
});
|
|
457
493
|
|
|
458
494
|
if (effectiveAgentName) {
|
|
459
|
-
runtime._agentSuppressCompletion?.add(effectiveAgentName);
|
|
460
495
|
runtime._agentResumeFlags?.add(effectiveAgentName);
|
|
461
496
|
}
|
|
462
497
|
|
|
@@ -466,10 +501,10 @@ ${content}
|
|
|
466
501
|
return response;
|
|
467
502
|
}
|
|
468
503
|
|
|
469
|
-
function logAgentStart(runtime, name, prompt) {
|
|
504
|
+
async function logAgentStart(runtime, name, prompt) {
|
|
470
505
|
if (runtime._agentResumeFlags?.has(name)) {
|
|
471
506
|
runtime._agentResumeFlags.delete(name);
|
|
472
|
-
runtime.prependHistory({
|
|
507
|
+
await runtime.prependHistory({
|
|
473
508
|
event: 'AGENT_RESUMED',
|
|
474
509
|
agent: name
|
|
475
510
|
});
|
|
@@ -485,5 +520,5 @@ function logAgentStart(runtime, name, prompt) {
|
|
|
485
520
|
entry.prompt = prompt;
|
|
486
521
|
}
|
|
487
522
|
|
|
488
|
-
runtime.prependHistory(entry);
|
|
523
|
+
await runtime.prependHistory(entry);
|
|
489
524
|
}
|
package/lib/runtime/index.js
CHANGED
|
@@ -15,6 +15,19 @@ export {
|
|
|
15
15
|
|
|
16
16
|
export { agent, executeAgent } from './agent.js';
|
|
17
17
|
export { askHuman } from './prompt.js';
|
|
18
|
+
export {
|
|
19
|
+
InteractionSchema,
|
|
20
|
+
InteractionResponseSchema,
|
|
21
|
+
normalizeInteraction,
|
|
22
|
+
validateInteraction,
|
|
23
|
+
normalizeInteractionResponse,
|
|
24
|
+
validateInteractionResponse,
|
|
25
|
+
createInteraction,
|
|
26
|
+
formatInteractionPrompt,
|
|
27
|
+
matchSingleSelect,
|
|
28
|
+
matchMultiSelect,
|
|
29
|
+
parseInteractionResponse
|
|
30
|
+
} from './interaction.js';
|
|
18
31
|
export { parallel, parallelLimit } from './parallel.js';
|
|
19
32
|
export { createMemoryProxy } from './memory.js';
|
|
20
33
|
|