agent-state-machine 2.0.5 → 2.0.7
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/lib/llm.js +4 -4
- package/lib/runtime/agent.js +49 -12
- package/lib/runtime/runtime.js +4 -0
- package/package.json +1 -1
- package/vercel-server/api/submit/[token].js +3 -12
- package/vercel-server/ui/index.html +90 -34
package/lib/llm.js
CHANGED
|
@@ -52,6 +52,10 @@ export function buildPrompt(context, options) {
|
|
|
52
52
|
delete cleanContext._loop;
|
|
53
53
|
delete cleanContext._config;
|
|
54
54
|
|
|
55
|
+
// Add the actual prompt
|
|
56
|
+
parts.push('# Task\n\n');
|
|
57
|
+
parts.push(options.prompt);
|
|
58
|
+
|
|
55
59
|
if (Object.keys(cleanContext).length > 0) {
|
|
56
60
|
parts.push('# Current Context\n');
|
|
57
61
|
parts.push('```json\n');
|
|
@@ -73,10 +77,6 @@ export function buildPrompt(context, options) {
|
|
|
73
77
|
parts.push('\n---\n');
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
// Add the actual prompt
|
|
77
|
-
parts.push('# Task\n\n');
|
|
78
|
-
parts.push(options.prompt);
|
|
79
|
-
|
|
80
80
|
return parts.join('\n');
|
|
81
81
|
}
|
|
82
82
|
|
package/lib/runtime/agent.js
CHANGED
|
@@ -26,26 +26,24 @@ export async function agent(name, params = {}) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
console.log(` [Agent: ${name}] Starting...`);
|
|
29
|
-
runtime.prependHistory({
|
|
30
|
-
event: 'AGENT_STARTED',
|
|
31
|
-
agent: name
|
|
32
|
-
});
|
|
33
29
|
|
|
34
30
|
try {
|
|
35
31
|
const result = await executeAgent(runtime, name, params);
|
|
36
32
|
|
|
37
|
-
let prompt = undefined;
|
|
38
33
|
if (result && typeof result === 'object' && result._debug_prompt) {
|
|
39
|
-
prompt = result._debug_prompt;
|
|
40
34
|
delete result._debug_prompt;
|
|
41
35
|
}
|
|
42
36
|
|
|
43
37
|
console.log(` [Agent: ${name}] Completed`);
|
|
38
|
+
if (runtime._agentSuppressCompletion?.has(name)) {
|
|
39
|
+
runtime._agentSuppressCompletion.delete(name);
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
|
|
44
43
|
runtime.prependHistory({
|
|
45
44
|
event: 'AGENT_COMPLETED',
|
|
46
45
|
agent: name,
|
|
47
|
-
output: result
|
|
48
|
-
prompt: prompt
|
|
46
|
+
output: result
|
|
49
47
|
});
|
|
50
48
|
|
|
51
49
|
return result;
|
|
@@ -119,6 +117,8 @@ async function executeJSAgent(runtime, agentPath, name, params) {
|
|
|
119
117
|
throw new Error(`Agent ${name} does not export a function`);
|
|
120
118
|
}
|
|
121
119
|
|
|
120
|
+
logAgentStart(runtime, name);
|
|
121
|
+
|
|
122
122
|
// Build context
|
|
123
123
|
const context = {
|
|
124
124
|
...runtime._rawMemory,
|
|
@@ -135,7 +135,7 @@ async function executeJSAgent(runtime, agentPath, name, params) {
|
|
|
135
135
|
|
|
136
136
|
// Handle interaction response from JS agent
|
|
137
137
|
if (result && result._interaction) {
|
|
138
|
-
const interactionResponse = await handleInteraction(runtime, result._interaction);
|
|
138
|
+
const interactionResponse = await handleInteraction(runtime, result._interaction, name);
|
|
139
139
|
|
|
140
140
|
// Use the interaction response as the primary output if it exists
|
|
141
141
|
// This allows the workflow to receive the user's input directly
|
|
@@ -176,7 +176,7 @@ async function executeJSAgent(runtime, agentPath, name, params) {
|
|
|
176
176
|
* Execute a Markdown agent (prompt-based)
|
|
177
177
|
*/
|
|
178
178
|
async function executeMDAgent(runtime, agentPath, name, params) {
|
|
179
|
-
const { llm, parseJSON, parseInteractionRequest } = await import('../llm.js');
|
|
179
|
+
const { llm, buildPrompt, parseJSON, parseInteractionRequest } = await import('../llm.js');
|
|
180
180
|
|
|
181
181
|
const content = fs.readFileSync(agentPath, 'utf-8');
|
|
182
182
|
const { config, prompt } = parseMarkdownAgent(content);
|
|
@@ -201,6 +201,14 @@ async function executeMDAgent(runtime, agentPath, name, params) {
|
|
|
201
201
|
|
|
202
202
|
const model = config.model || 'fast';
|
|
203
203
|
|
|
204
|
+
const fullPrompt = buildPrompt(context, {
|
|
205
|
+
model,
|
|
206
|
+
prompt: interpolatedPrompt,
|
|
207
|
+
includeContext: config.includeContext !== 'false'
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
logAgentStart(runtime, name, fullPrompt);
|
|
211
|
+
|
|
204
212
|
console.log(` Using model: ${model}`);
|
|
205
213
|
|
|
206
214
|
const response = await llm(context, {
|
|
@@ -247,7 +255,7 @@ async function executeMDAgent(runtime, agentPath, name, params) {
|
|
|
247
255
|
slug,
|
|
248
256
|
targetKey,
|
|
249
257
|
content: interactionContent
|
|
250
|
-
});
|
|
258
|
+
}, name);
|
|
251
259
|
|
|
252
260
|
// Return the user's response as the agent result
|
|
253
261
|
return { [outputKey]: userResponse, _debug_prompt: response.fullPrompt };
|
|
@@ -334,7 +342,9 @@ function sanitizeSlug(input) {
|
|
|
334
342
|
/**
|
|
335
343
|
* Handle interaction (create file, wait for user, return response)
|
|
336
344
|
*/
|
|
337
|
-
async function handleInteraction(runtime, interaction) {
|
|
345
|
+
async function handleInteraction(runtime, interaction, agentName) {
|
|
346
|
+
const effectiveAgentName = typeof agentName === 'string' ? agentName : null;
|
|
347
|
+
|
|
338
348
|
const slug = sanitizeSlug(interaction.slug);
|
|
339
349
|
const targetKey = String(interaction.targetKey || slug);
|
|
340
350
|
const content = String(interaction.content || '').trim();
|
|
@@ -357,8 +367,35 @@ ${content}
|
|
|
357
367
|
question: content
|
|
358
368
|
});
|
|
359
369
|
|
|
370
|
+
if (effectiveAgentName) {
|
|
371
|
+
runtime._agentSuppressCompletion?.add(effectiveAgentName);
|
|
372
|
+
runtime._agentResumeFlags?.add(effectiveAgentName);
|
|
373
|
+
}
|
|
374
|
+
|
|
360
375
|
// Block and wait for user input (instead of throwing)
|
|
361
376
|
const response = await runtime.waitForInteraction(filePath, slug, targetKey);
|
|
362
377
|
|
|
363
378
|
return response;
|
|
364
379
|
}
|
|
380
|
+
|
|
381
|
+
function logAgentStart(runtime, name, prompt) {
|
|
382
|
+
if (runtime._agentResumeFlags?.has(name)) {
|
|
383
|
+
runtime._agentResumeFlags.delete(name);
|
|
384
|
+
runtime.prependHistory({
|
|
385
|
+
event: 'AGENT_RESUMED',
|
|
386
|
+
agent: name
|
|
387
|
+
});
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const entry = {
|
|
392
|
+
event: 'AGENT_STARTED',
|
|
393
|
+
agent: name
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
if (prompt) {
|
|
397
|
+
entry.prompt = prompt;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
runtime.prependHistory(entry);
|
|
401
|
+
}
|
package/lib/runtime/runtime.js
CHANGED
|
@@ -84,6 +84,10 @@ export class WorkflowRuntime {
|
|
|
84
84
|
this.remoteEnabled = false;
|
|
85
85
|
this.remoteUrl = null;
|
|
86
86
|
this.pendingRemoteInteraction = null; // { slug, targetKey, resolve, reject }
|
|
87
|
+
|
|
88
|
+
// Agent interaction tracking for history logging
|
|
89
|
+
this._agentResumeFlags = new Set();
|
|
90
|
+
this._agentSuppressCompletion = new Set();
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
ensureDirectories() {
|
package/package.json
CHANGED
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
getSession,
|
|
9
|
-
|
|
10
|
-
publishEvent,
|
|
9
|
+
addEvent,
|
|
11
10
|
redis,
|
|
12
11
|
KEYS,
|
|
13
12
|
} from '../../lib/redis.js';
|
|
@@ -68,22 +67,14 @@ export default async function handler(req, res) {
|
|
|
68
67
|
// Set TTL on pending list
|
|
69
68
|
await redis.expire(pendingKey, 300); // 5 minutes
|
|
70
69
|
|
|
71
|
-
// Log event to
|
|
72
|
-
|
|
70
|
+
// Log event to events list (single source of truth for UI)
|
|
71
|
+
await addEvent(token, {
|
|
73
72
|
timestamp: new Date().toISOString(),
|
|
74
73
|
event: 'INTERACTION_SUBMITTED',
|
|
75
74
|
slug,
|
|
76
75
|
targetKey: targetKey || `_interaction_${slug}`,
|
|
77
76
|
answer: response.substring(0, 200) + (response.length > 200 ? '...' : ''),
|
|
78
77
|
source: 'remote',
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
await addHistoryEvent(token, event);
|
|
82
|
-
|
|
83
|
-
// Notify other browsers
|
|
84
|
-
await publishEvent(token, {
|
|
85
|
-
type: 'event',
|
|
86
|
-
...event,
|
|
87
78
|
});
|
|
88
79
|
|
|
89
80
|
return res.status(200).json({ success: true });
|
|
@@ -107,6 +107,14 @@
|
|
|
107
107
|
white-space: pre-wrap;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
.agent-prompt summary {
|
|
111
|
+
list-style: none;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.agent-prompt summary::-webkit-details-marker {
|
|
115
|
+
display: none;
|
|
116
|
+
}
|
|
117
|
+
|
|
110
118
|
@keyframes blink {
|
|
111
119
|
|
|
112
120
|
0%,
|
|
@@ -674,11 +682,61 @@
|
|
|
674
682
|
|
|
675
683
|
// CENTERED inside card
|
|
676
684
|
if (item.event === "AGENT_STARTED") {
|
|
685
|
+
if (item.prompt) {
|
|
686
|
+
return wrapIO(
|
|
687
|
+
"left",
|
|
688
|
+
<section className="hairline rounded-2xl rounded-tl-none overflow-hidden io-in">
|
|
689
|
+
<details className="agent-prompt">
|
|
690
|
+
<summary className="rtl-safe px-5 py-4 divider cursor-pointer">
|
|
691
|
+
<div className="flex items-center justify-between gap-4">
|
|
692
|
+
<div>
|
|
693
|
+
<div className="text-[11px] tracking-[0.22em] font-semibold" style={{ color: "var(--muted)" }}>
|
|
694
|
+
AGENT STARTED
|
|
695
|
+
</div>
|
|
696
|
+
<div className="mt-1 text-[11px] tracking-[0.14em]" style={{ color: "var(--muted)" }}>
|
|
697
|
+
{(item.agent || "").toString()} • {time}
|
|
698
|
+
</div>
|
|
699
|
+
</div>
|
|
700
|
+
<CopyButton text={item.prompt} />
|
|
701
|
+
</div>
|
|
702
|
+
<div className="mt-3 text-[11px] tracking-[0.18em] font-semibold uppercase" style={{ color: "var(--muted)" }}>
|
|
703
|
+
Show Prompt ▼
|
|
704
|
+
</div>
|
|
705
|
+
</summary>
|
|
706
|
+
<div className="px-5 py-4">
|
|
707
|
+
<div className="markdown-body text-[13px] leading-relaxed overflow-x-auto">{item.prompt}</div>
|
|
708
|
+
</div>
|
|
709
|
+
</details>
|
|
710
|
+
</section>,
|
|
711
|
+
idx
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
return wrapIO(
|
|
716
|
+
"left",
|
|
717
|
+
<section className="hairline rounded-2xl rounded-tl-none px-6 py-5 io-in">
|
|
718
|
+
<div className="rtl-safe flex items-center justify-between gap-4">
|
|
719
|
+
<div className="text-[11px] tracking-[0.22em] font-semibold" style={{ color: "var(--muted)" }}>
|
|
720
|
+
AGENT STARTED
|
|
721
|
+
</div>
|
|
722
|
+
<span className="text-[11px] tracking-[0.14em]" style={{ color: "var(--muted)" }}>
|
|
723
|
+
{time}
|
|
724
|
+
</span>
|
|
725
|
+
</div>
|
|
726
|
+
<div className="mt-3 text-[13px] leading-relaxed">
|
|
727
|
+
<span className="font-semibold">{item.agent}</span>
|
|
728
|
+
</div>
|
|
729
|
+
</section>,
|
|
730
|
+
idx
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (item.event === "AGENT_RESUMED") {
|
|
677
735
|
return wrapIO(
|
|
678
736
|
"center",
|
|
679
737
|
<section className="meta-center">
|
|
680
738
|
<div className="text-[11px] tracking-[0.22em] font-semibold" style={{ color: "var(--muted)" }}>
|
|
681
|
-
AGENT
|
|
739
|
+
AGENT RESUMED
|
|
682
740
|
</div>
|
|
683
741
|
<div className="mt-2 text-[13px] leading-relaxed">
|
|
684
742
|
<span className="font-semibold">{item.agent}</span>
|
|
@@ -714,8 +772,8 @@
|
|
|
714
772
|
|
|
715
773
|
if (item.event === "INTERACTION_REQUESTED" || item.event === "PROMPT_REQUESTED") {
|
|
716
774
|
return wrapIO(
|
|
717
|
-
"
|
|
718
|
-
<section className="hairline rounded-2xl px-6 py-5
|
|
775
|
+
"center",
|
|
776
|
+
<section className="hairline rounded-2xl px-6 py-5">
|
|
719
777
|
<div className="rtl-safe flex items-center justify-between gap-4">
|
|
720
778
|
<div className="text-[11px] tracking-[0.22em] font-semibold" style={{ color: "var(--muted)" }}>
|
|
721
779
|
INTERVENTION REQUIRED
|
|
@@ -735,8 +793,8 @@
|
|
|
735
793
|
if (item.event === "PROMPT_ANSWERED" || item.event === "INTERACTION_SUBMITTED") {
|
|
736
794
|
const isManual = item.source === "remote";
|
|
737
795
|
return wrapIO(
|
|
738
|
-
"
|
|
739
|
-
<section className="hairline rounded-2xl px-6 py-5
|
|
796
|
+
"center",
|
|
797
|
+
<section className="hairline rounded-2xl px-6 py-5">
|
|
740
798
|
<div className="rtl-safe flex items-center justify-between gap-4">
|
|
741
799
|
<div className="text-[11px] tracking-[0.22em] font-semibold" style={{ color: "var(--muted)" }}>
|
|
742
800
|
{isManual ? "RESOLVED VIA BROWSER" : "USER ANSWERED"}
|
|
@@ -752,31 +810,37 @@
|
|
|
752
810
|
}
|
|
753
811
|
|
|
754
812
|
if (item.event === "AGENT_COMPLETED" || item.event === "INTERACTION_RESOLVED") {
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
<section className="meta-center">
|
|
813
|
+
if (item.event === "AGENT_COMPLETED") {
|
|
814
|
+
return wrapIO(
|
|
815
|
+
"right",
|
|
816
|
+
<section className="io-out">
|
|
817
|
+
<section className="meta-center mb-4">
|
|
760
818
|
<div className="text-[11px] tracking-[0.22em] font-semibold" style={{ color: "var(--muted)" }}>
|
|
761
819
|
DONE
|
|
762
820
|
</div>
|
|
763
821
|
<div className="mt-1 text-[11px] tracking-[0.14em]" style={{ color: "var(--muted)" }}>
|
|
764
|
-
{(item.agent ||
|
|
822
|
+
{(item.agent || "").toString()} • {time}
|
|
765
823
|
</div>
|
|
766
|
-
</section
|
|
767
|
-
|
|
768
|
-
)}
|
|
769
|
-
|
|
770
|
-
{(item.output || item.result) && wrapIO(
|
|
771
|
-
"right",
|
|
772
|
-
<section className="io-out">
|
|
824
|
+
</section>
|
|
825
|
+
{(item.output || item.result) && (
|
|
773
826
|
<JsonView data={item.output || item.result} label="OUTPUT / RESPONSE" align="right" />
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
827
|
+
)}
|
|
828
|
+
</section>,
|
|
829
|
+
idx
|
|
830
|
+
);
|
|
831
|
+
}
|
|
777
832
|
|
|
778
|
-
|
|
779
|
-
|
|
833
|
+
return wrapIO(
|
|
834
|
+
"center",
|
|
835
|
+
<section className="meta-center">
|
|
836
|
+
<div className="text-[11px] tracking-[0.22em] font-semibold" style={{ color: "var(--muted)" }}>
|
|
837
|
+
DONE
|
|
838
|
+
</div>
|
|
839
|
+
<div className="mt-1 text-[11px] tracking-[0.14em]" style={{ color: "var(--muted)" }}>
|
|
840
|
+
{(item.slug || "").toString()} • {time}
|
|
841
|
+
</div>
|
|
842
|
+
</section>,
|
|
843
|
+
idx
|
|
780
844
|
);
|
|
781
845
|
}
|
|
782
846
|
|
|
@@ -844,17 +908,9 @@
|
|
|
844
908
|
|
|
845
909
|
<div className="flex items-center justify-between gap-4">
|
|
846
910
|
<div className="min-w-0 flex items-center gap-3">
|
|
847
|
-
<div className="text-[12px] tracking-[0.24em] font-semibold whitespace-nowrap" style={{ color: "var(--muted)" }}>
|
|
848
|
-
{token ? "REMOTE FOLLOW" : "LOCAL TERMINAL"}
|
|
849
|
-
</div>
|
|
850
|
-
|
|
851
|
-
<span className="text-[12px] tracking-[0.24em] font-semibold" style={{ color: "var(--muted)" }}>•</span>
|
|
852
|
-
|
|
853
|
-
<h1 className="text-[14px] tracking-[0.06em] font-semibold truncate">
|
|
911
|
+
<div className="text-[12px] tracking-[0.24em] font-semibold uppercase whitespace-nowrap" style={{ color: "var(--muted)" }}>
|
|
854
912
|
{workflowName || "WORKFLOW"}
|
|
855
|
-
</
|
|
856
|
-
|
|
857
|
-
<span className="text-[12px] tracking-[0.24em] font-semibold" style={{ color: "var(--muted)" }}>•</span>
|
|
913
|
+
</div>
|
|
858
914
|
|
|
859
915
|
<StatusBadge status={status} />
|
|
860
916
|
</div>
|
|
@@ -936,4 +992,4 @@
|
|
|
936
992
|
</script>
|
|
937
993
|
</body>
|
|
938
994
|
|
|
939
|
-
</html>
|
|
995
|
+
</html>
|