@minded-ai/mindedjs 1.0.103-beta-1 → 1.0.103-beta-2
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/agent.d.ts.map +1 -1
- package/dist/agent.js +29 -16
- package/dist/agent.js.map +1 -1
- package/dist/browserTask/executeBrowserTask.d.ts +12 -0
- package/dist/browserTask/executeBrowserTask.d.ts.map +1 -0
- package/dist/browserTask/executeBrowserTask.js +181 -0
- package/dist/browserTask/executeBrowserTask.js.map +1 -0
- package/dist/checkpointer/checkpointSaverFactory.js +1 -1
- package/dist/checkpointer/checkpointSaverFactory.js.map +1 -1
- package/dist/cli/index.js +14 -14
- package/dist/cli/index.js.map +1 -1
- package/dist/edges/createDirectEdge.d.ts +2 -1
- package/dist/edges/createDirectEdge.d.ts.map +1 -1
- package/dist/edges/createDirectEdge.js +6 -2
- package/dist/edges/createDirectEdge.js.map +1 -1
- package/dist/edges/createLogicalRouter.d.ts.map +1 -1
- package/dist/edges/createLogicalRouter.js +23 -6
- package/dist/edges/createLogicalRouter.js.map +1 -1
- package/dist/edges/createPromptRouter.d.ts.map +1 -1
- package/dist/edges/createPromptRouter.js +12 -6
- package/dist/edges/createPromptRouter.js.map +1 -1
- package/dist/edges/edgeFactory.d.ts.map +1 -1
- package/dist/edges/edgeFactory.js +8 -3
- package/dist/edges/edgeFactory.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/interrupts/BaseInterruptSessionManager.js +1 -1
- package/dist/interrupts/BaseInterruptSessionManager.js.map +1 -1
- package/dist/interrupts/interruptSessionManagerFactory.js +2 -2
- package/dist/interrupts/interruptSessionManagerFactory.js.map +1 -1
- package/dist/llm/createLlmInstance.d.ts +1 -1
- package/dist/llm/createLlmInstance.d.ts.map +1 -1
- package/dist/llm/createLlmInstance.js +18 -1
- package/dist/llm/createLlmInstance.js.map +1 -1
- package/dist/nodes/addAppToolNode.d.ts.map +1 -1
- package/dist/nodes/addAppToolNode.js +5 -4
- package/dist/nodes/addAppToolNode.js.map +1 -1
- package/dist/nodes/addBrowserTaskNode.d.ts +13 -0
- package/dist/nodes/addBrowserTaskNode.d.ts.map +1 -0
- package/dist/nodes/addBrowserTaskNode.js +232 -0
- package/dist/nodes/addBrowserTaskNode.js.map +1 -0
- package/dist/nodes/addBrowserTaskRunNode.d.ts +13 -0
- package/dist/nodes/addBrowserTaskRunNode.d.ts.map +1 -0
- package/dist/nodes/addBrowserTaskRunNode.js +130 -0
- package/dist/nodes/addBrowserTaskRunNode.js.map +1 -0
- package/dist/nodes/addHumanInTheLoopNode.d.ts.map +1 -1
- package/dist/nodes/addHumanInTheLoopNode.js +1 -1
- package/dist/nodes/addHumanInTheLoopNode.js.map +1 -1
- package/dist/nodes/addJumpToNode.d.ts.map +1 -1
- package/dist/nodes/addJumpToNode.js +2 -1
- package/dist/nodes/addJumpToNode.js.map +1 -1
- package/dist/nodes/addJunctionNode.d.ts.map +1 -1
- package/dist/nodes/addJunctionNode.js +1 -0
- package/dist/nodes/addJunctionNode.js.map +1 -1
- package/dist/nodes/addPromptNode.d.ts.map +1 -1
- package/dist/nodes/addPromptNode.js +8 -18
- package/dist/nodes/addPromptNode.js.map +1 -1
- package/dist/nodes/addToolNode.js +2 -4
- package/dist/nodes/addToolNode.js.map +1 -1
- package/dist/nodes/addToolRunNode.d.ts.map +1 -1
- package/dist/nodes/addToolRunNode.js +2 -1
- package/dist/nodes/addToolRunNode.js.map +1 -1
- package/dist/nodes/addTriggerNode.d.ts.map +1 -1
- package/dist/nodes/addTriggerNode.js +2 -1
- package/dist/nodes/addTriggerNode.js.map +1 -1
- package/dist/nodes/nodeFactory.d.ts.map +1 -1
- package/dist/nodes/nodeFactory.js +4 -0
- package/dist/nodes/nodeFactory.js.map +1 -1
- package/dist/platform/mindedConnection.js +13 -13
- package/dist/platform/mindedConnection.js.map +1 -1
- package/dist/platform/models/mindedChatOpenAI.d.ts +20 -0
- package/dist/platform/models/mindedChatOpenAI.d.ts.map +1 -0
- package/dist/platform/models/mindedChatOpenAI.js +32 -0
- package/dist/platform/models/mindedChatOpenAI.js.map +1 -0
- package/dist/platform/models/parallelWrapper.d.ts +17 -0
- package/dist/platform/models/parallelWrapper.d.ts.map +1 -0
- package/dist/platform/models/parallelWrapper.js +105 -0
- package/dist/platform/models/parallelWrapper.js.map +1 -0
- package/dist/playbooks/playbooks.js +6 -6
- package/dist/playbooks/playbooks.js.map +1 -1
- package/dist/types/Flows.types.d.ts +18 -3
- package/dist/types/Flows.types.d.ts.map +1 -1
- package/dist/types/Flows.types.js +2 -0
- package/dist/types/Flows.types.js.map +1 -1
- package/dist/types/LLM.types.d.ts.map +1 -1
- package/dist/types/LLM.types.js +1 -1
- package/dist/types/LLM.types.js.map +1 -1
- package/dist/types/LangGraph.types.d.ts +2 -0
- package/dist/types/LangGraph.types.d.ts.map +1 -1
- package/dist/types/LangGraph.types.js +5 -0
- package/dist/types/LangGraph.types.js.map +1 -1
- package/dist/utils/logger.js +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/voice/voiceSession.d.ts.map +1 -1
- package/dist/voice/voiceSession.js +16 -17
- package/dist/voice/voiceSession.js.map +1 -1
- package/docs/SUMMARY.md +1 -0
- package/docs/low-code-editor/nodes.md +27 -0
- package/docs/low-code-editor/tools.md +32 -0
- package/docs/platform/parallel-llm.md +242 -0
- package/package.json +2 -1
- package/src/agent.ts +30 -18
- package/src/browserTask/executeBrowserTask.ts +213 -0
- package/src/checkpointer/checkpointSaverFactory.ts +1 -1
- package/src/cli/index.ts +14 -14
- package/src/edges/createDirectEdge.ts +7 -2
- package/src/edges/createLogicalRouter.ts +23 -6
- package/src/edges/createPromptRouter.ts +13 -6
- package/src/edges/edgeFactory.ts +20 -4
- package/src/index.ts +6 -0
- package/src/interrupts/BaseInterruptSessionManager.ts +1 -1
- package/src/interrupts/interruptSessionManagerFactory.ts +2 -2
- package/src/llm/createLlmInstance.ts +25 -2
- package/src/nodes/addAppToolNode.ts +5 -4
- package/src/nodes/addBrowserTaskNode.ts +231 -0
- package/src/nodes/addBrowserTaskRunNode.ts +144 -0
- package/src/nodes/addHumanInTheLoopNode.ts +2 -1
- package/src/nodes/addJumpToNode.ts +2 -1
- package/src/nodes/addJunctionNode.ts +1 -0
- package/src/nodes/addPromptNode.ts +8 -19
- package/src/nodes/addToolNode.ts +4 -4
- package/src/nodes/addToolRunNode.ts +3 -1
- package/src/nodes/addTriggerNode.ts +2 -1
- package/src/nodes/nodeFactory.ts +5 -1
- package/src/platform/mindedConnection.ts +13 -13
- package/src/platform/models/mindedChatOpenAI.ts +49 -0
- package/src/platform/models/parallelWrapper.ts +141 -0
- package/src/playbooks/playbooks.ts +6 -6
- package/src/types/Flows.types.ts +17 -1
- package/src/types/LLM.types.ts +5 -5
- package/src/types/LangGraph.types.ts +5 -0
- package/src/utils/logger.ts +1 -1
- package/src/voice/voiceSession.ts +16 -17
- package/src/platform/mindedChatOpenAI.ts +0 -19
package/src/agent.ts
CHANGED
|
@@ -304,9 +304,17 @@ export class Agent {
|
|
|
304
304
|
|
|
305
305
|
// Add edge from start to first node if no triggers exist
|
|
306
306
|
const hasTrigger = nodes.some((node) => node.type === NodeType.TRIGGER && node.triggerType !== TriggerType.MANUAL);
|
|
307
|
-
if (!hasTrigger
|
|
308
|
-
|
|
309
|
-
|
|
307
|
+
if (!hasTrigger) {
|
|
308
|
+
// Find the Main flow
|
|
309
|
+
const mainFlow = this.flows.find((flow) => flow.name === 'Main flow');
|
|
310
|
+
if (mainFlow && mainFlow.nodes.length > 0) {
|
|
311
|
+
this.startingNodeId = mainFlow.nodes[0].name;
|
|
312
|
+
graph.addEdge('__start__', mainFlow.nodes[0].name as any);
|
|
313
|
+
} else if (nodes.length > 0) {
|
|
314
|
+
// Fallback to first node if Main flow not found
|
|
315
|
+
this.startingNodeId = nodes[0].name;
|
|
316
|
+
graph.addEdge('__start__', nodes[0].name as any);
|
|
317
|
+
}
|
|
310
318
|
} else {
|
|
311
319
|
nodes.forEach((node) => {
|
|
312
320
|
if (node.type === NodeType.TRIGGER && node.triggerType !== TriggerType.MANUAL) {
|
|
@@ -348,6 +356,7 @@ export class Agent {
|
|
|
348
356
|
sessionId: state.sessionId || uuidv4(), // Preserve existing sessionId or generate new one
|
|
349
357
|
sessionType: state.sessionType || SessionType.TEXT,
|
|
350
358
|
overrideStartFromNodeId: null,
|
|
359
|
+
goto: null,
|
|
351
360
|
};
|
|
352
361
|
|
|
353
362
|
// Emit INIT event with the initial state
|
|
@@ -421,7 +430,7 @@ export class Agent {
|
|
|
421
430
|
// Try to acquire lock atomically (unless bypassing session check)
|
|
422
431
|
if (!bypassSessionCheck && !(await this.interruptSessionManager.lock(sessionId))) {
|
|
423
432
|
// Could not acquire lock, session is being processed - enqueue the message
|
|
424
|
-
logger.info({
|
|
433
|
+
logger.info({ msg: 'Enqueuing message', sessionId, triggerBody, triggerName, appName });
|
|
425
434
|
await this.interruptSessionManager.enqueueMessage(sessionId, {
|
|
426
435
|
triggerBody,
|
|
427
436
|
triggerName,
|
|
@@ -458,7 +467,7 @@ export class Agent {
|
|
|
458
467
|
const handlerResult = results.find((r) => r !== undefined);
|
|
459
468
|
if (handlerResult) {
|
|
460
469
|
if (!handlerResult.isQualified) {
|
|
461
|
-
logger.info({ message:
|
|
470
|
+
logger.info({ message: '[Trigger] Disqualified', triggerName, triggerBody, sessionId });
|
|
462
471
|
await this.interruptSessionManager.release(sessionId);
|
|
463
472
|
return;
|
|
464
473
|
}
|
|
@@ -469,7 +478,7 @@ export class Agent {
|
|
|
469
478
|
}
|
|
470
479
|
}
|
|
471
480
|
|
|
472
|
-
logger.info({
|
|
481
|
+
logger.info({ msg: '[Trigger] Received', triggerName, triggerBody, sessionId });
|
|
473
482
|
const langraphConfig = this.getLangraphConfig(sessionId || uuidv4());
|
|
474
483
|
const state = await this.compiledGraph.getState(langraphConfig);
|
|
475
484
|
const suffixes = Object.values(internalNodesSuffix);
|
|
@@ -478,7 +487,10 @@ export class Agent {
|
|
|
478
487
|
? state.values.overrideStartFromNodeId
|
|
479
488
|
? state.values.overrideStartFromNodeId
|
|
480
489
|
: state.next[0]
|
|
481
|
-
: this.startingNodeId
|
|
490
|
+
: this.startingNodeId;
|
|
491
|
+
if (!nodeToBeInvoked) {
|
|
492
|
+
throw new Error('No node to be invoked');
|
|
493
|
+
}
|
|
482
494
|
nodeToBeInvoked = nodeToBeInvoked.replace(new RegExp(suffixes.join('|'), 'g'), '');
|
|
483
495
|
const historyStep = this.createTriggerHistoryStep(
|
|
484
496
|
state.values.history,
|
|
@@ -543,7 +555,6 @@ export class Agent {
|
|
|
543
555
|
langraphConfig,
|
|
544
556
|
);
|
|
545
557
|
}
|
|
546
|
-
|
|
547
558
|
const nextMessage = await this.interruptSessionManager.dequeue(sessionId);
|
|
548
559
|
if (nextMessage) {
|
|
549
560
|
// Dequeue the first message and process it recursively
|
|
@@ -557,7 +568,7 @@ export class Agent {
|
|
|
557
568
|
if (nextMessage.appName) {
|
|
558
569
|
invokeParams.appName = nextMessage.appName;
|
|
559
570
|
}
|
|
560
|
-
logger.info({
|
|
571
|
+
logger.info({ msg: 'Invoking next message in the queue', invokeParams });
|
|
561
572
|
return await this.invoke(invokeParams);
|
|
562
573
|
}
|
|
563
574
|
|
|
@@ -567,7 +578,7 @@ export class Agent {
|
|
|
567
578
|
return res;
|
|
568
579
|
} catch (error: any) {
|
|
569
580
|
logger.error({
|
|
570
|
-
|
|
581
|
+
msg: '[Trigger] Error',
|
|
571
582
|
errorMessage: error.message,
|
|
572
583
|
stack: error.stack,
|
|
573
584
|
sessionId,
|
|
@@ -805,7 +816,7 @@ export class Agent {
|
|
|
805
816
|
// Skip secret loading in local development
|
|
806
817
|
const { runLocally } = getConfig();
|
|
807
818
|
if (runLocally) {
|
|
808
|
-
logger.
|
|
819
|
+
logger.debug({ msg: '[Agent] Secrets loaded from local .env file' });
|
|
809
820
|
return {};
|
|
810
821
|
}
|
|
811
822
|
if (!mindedConnection.isConnected()) {
|
|
@@ -832,10 +843,10 @@ export class Agent {
|
|
|
832
843
|
const secrets = response.secrets || {};
|
|
833
844
|
|
|
834
845
|
// Load secrets into process.env
|
|
835
|
-
logger.debug(`Loading ${Object.keys(secrets).length} secrets into environment variables`);
|
|
836
846
|
Object.entries(secrets).forEach(([key, value]) => {
|
|
837
847
|
process.env[key] = value;
|
|
838
848
|
});
|
|
849
|
+
logger.debug(`[Agent] Loaded ${Object.keys(secrets).length} secrets from platform`);
|
|
839
850
|
|
|
840
851
|
// Cache the secrets for future requests
|
|
841
852
|
this.secretsCache = secrets;
|
|
@@ -851,7 +862,7 @@ export class Agent {
|
|
|
851
862
|
}
|
|
852
863
|
|
|
853
864
|
private setupVoice(): void {
|
|
854
|
-
logger.info(
|
|
865
|
+
logger.info('[Voice] Setting up voice');
|
|
855
866
|
if (!mindedConnection.isConnected()) {
|
|
856
867
|
throw new Error('Minded connection is required');
|
|
857
868
|
}
|
|
@@ -874,7 +885,7 @@ export class Agent {
|
|
|
874
885
|
voiceSession.sendAudio(audioMessage.audioData);
|
|
875
886
|
} else {
|
|
876
887
|
logger.trace({
|
|
877
|
-
message: 'Audio received; voice session not found for sessionId',
|
|
888
|
+
message: '[Voice] Audio received; voice session not found for sessionId',
|
|
878
889
|
sessionId: audioMessage.sessionId,
|
|
879
890
|
activeSessions: Array.from(this.voiceSessions.keys()),
|
|
880
891
|
});
|
|
@@ -884,13 +895,13 @@ export class Agent {
|
|
|
884
895
|
// Hangup / end session handler
|
|
885
896
|
connection.on(mindedConnectionSocketMessageType.DASHBOARD_VOICE_SESSION_END, (message) => {
|
|
886
897
|
const hangup = message as BaseVoiceMessage;
|
|
887
|
-
logger.debug({
|
|
898
|
+
logger.debug({ msg: '[Voice] Dashboard eneded voice session', sessionId: hangup.sessionId });
|
|
888
899
|
const voiceSession = this.voiceSessions.get(hangup.sessionId);
|
|
889
900
|
if (voiceSession) {
|
|
890
901
|
voiceSession.hangup();
|
|
891
902
|
} else {
|
|
892
903
|
logger.trace({
|
|
893
|
-
message: 'Session ended; voice session not found for sessionId',
|
|
904
|
+
message: '[Voice] Session ended; voice session not found for sessionId',
|
|
894
905
|
sessionId: hangup.sessionId,
|
|
895
906
|
activeSessions: this.voiceSessions.keys(),
|
|
896
907
|
});
|
|
@@ -917,7 +928,8 @@ export class Agent {
|
|
|
917
928
|
voiceId: voiceTrigger.voiceId,
|
|
918
929
|
});
|
|
919
930
|
await voiceSession.init();
|
|
920
|
-
logger.debug({
|
|
931
|
+
logger.debug({ msg: '[Voice] Voice session initialized', sessionId: params.sessionId });
|
|
932
|
+
|
|
921
933
|
this.voiceSessions.set(params.sessionId, voiceSession);
|
|
922
934
|
|
|
923
935
|
// Emit voice session start event
|
|
@@ -945,7 +957,7 @@ export class Agent {
|
|
|
945
957
|
To be used by the Lambda wrapper to restore checkpoints
|
|
946
958
|
*/
|
|
947
959
|
public async restoreCheckpoint(sessionId: string, checkpointId: string): Promise<void> {
|
|
948
|
-
logger.info({
|
|
960
|
+
logger.info({ msg: '[Agent]Restoring checkpoint', sessionId, checkpointId });
|
|
949
961
|
const langraphConfig = this.getLangraphConfig(sessionId, checkpointId);
|
|
950
962
|
await this.compiledGraph.invoke(
|
|
951
963
|
new Command({
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { logger } from '../utils/logger';
|
|
2
|
+
|
|
3
|
+
// Browser Use Cloud API configuration
|
|
4
|
+
const BROWSER_USE_API_BASE_URL = 'https://api.browser-use.com/api/v1';
|
|
5
|
+
|
|
6
|
+
// Types for Browser Use Cloud API
|
|
7
|
+
export interface CloudTaskResponse {
|
|
8
|
+
id: string;
|
|
9
|
+
status: string;
|
|
10
|
+
live_url?: string;
|
|
11
|
+
steps?: any[];
|
|
12
|
+
output?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Browser Use Cloud API methods
|
|
16
|
+
export const createCloudTask = async (prompt: string, model?: string): Promise<string> => {
|
|
17
|
+
const apiKey = process.env.BROWSER_USE_API_KEY;
|
|
18
|
+
if (!apiKey) {
|
|
19
|
+
throw new Error('BROWSER_USE_API_KEY environment variable is required');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
logger.debug({ msg: 'Creating cloud browser task', prompt: prompt.substring(0, 100) + '...' });
|
|
23
|
+
|
|
24
|
+
const response = await fetch(`${BROWSER_USE_API_BASE_URL}/run-task`, {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: {
|
|
27
|
+
Authorization: `Bearer ${apiKey}`,
|
|
28
|
+
'Content-Type': 'application/json',
|
|
29
|
+
},
|
|
30
|
+
body: JSON.stringify({
|
|
31
|
+
task: prompt,
|
|
32
|
+
use_proxy: false,
|
|
33
|
+
llm_model: model || 'gpt-4o',
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
logger.error({ msg: 'Failed to create cloud browser task', status: response.status, statusText: response.statusText });
|
|
39
|
+
throw new Error(`Failed to create browser task: ${response.statusText}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const data = await response.json();
|
|
43
|
+
logger.debug({ msg: 'Cloud browser task created', taskId: data.id });
|
|
44
|
+
return data.id;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const getTaskDetails = async (taskId: string): Promise<CloudTaskResponse> => {
|
|
48
|
+
const apiKey = process.env.BROWSER_USE_API_KEY;
|
|
49
|
+
if (!apiKey) {
|
|
50
|
+
throw new Error('BROWSER_USE_API_KEY environment variable is required');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const response = await fetch(`${BROWSER_USE_API_BASE_URL}/task/${taskId}`, {
|
|
54
|
+
headers: {
|
|
55
|
+
Authorization: `Bearer ${apiKey}`,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
logger.error({ msg: 'Failed to get task details', taskId, status: response.status, statusText: response.statusText });
|
|
61
|
+
throw new Error(`Failed to get task details: ${response.statusText}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return response.json();
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const waitForLiveUrl = async (taskId: string, maxWaitTime: number = 30000): Promise<CloudTaskResponse> => {
|
|
68
|
+
const startTime = Date.now();
|
|
69
|
+
const pollInterval = 2000; // 2 seconds
|
|
70
|
+
let pollCount = 0;
|
|
71
|
+
|
|
72
|
+
logger.debug({ msg: 'Starting to poll for live_url', taskId, maxWaitTime, pollInterval });
|
|
73
|
+
|
|
74
|
+
while (Date.now() - startTime < maxWaitTime) {
|
|
75
|
+
pollCount++;
|
|
76
|
+
const elapsedTime = Date.now() - startTime;
|
|
77
|
+
|
|
78
|
+
logger.trace({
|
|
79
|
+
msg: 'Polling for live_url',
|
|
80
|
+
taskId,
|
|
81
|
+
pollCount,
|
|
82
|
+
elapsedTime,
|
|
83
|
+
remainingTime: maxWaitTime - elapsedTime,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const taskDetails = await getTaskDetails(taskId);
|
|
87
|
+
|
|
88
|
+
logger.trace({
|
|
89
|
+
msg: 'Task details received',
|
|
90
|
+
taskId,
|
|
91
|
+
status: taskDetails.status,
|
|
92
|
+
hasLiveUrl: !!taskDetails.live_url,
|
|
93
|
+
pollCount,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (taskDetails.live_url) {
|
|
97
|
+
logger.debug({
|
|
98
|
+
msg: 'Live URL available',
|
|
99
|
+
taskId,
|
|
100
|
+
liveUrl: taskDetails.live_url,
|
|
101
|
+
pollCount,
|
|
102
|
+
totalTime: elapsedTime,
|
|
103
|
+
});
|
|
104
|
+
return taskDetails;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (taskDetails.status === 'failed' || taskDetails.status === 'stopped') {
|
|
108
|
+
logger.error({
|
|
109
|
+
msg: 'Task failed while waiting for live_url',
|
|
110
|
+
taskId,
|
|
111
|
+
status: taskDetails.status,
|
|
112
|
+
pollCount,
|
|
113
|
+
elapsedTime,
|
|
114
|
+
});
|
|
115
|
+
throw new Error(`Task failed with status: ${taskDetails.status}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
logger.trace({
|
|
119
|
+
msg: 'Live URL not yet available, continuing to poll',
|
|
120
|
+
taskId,
|
|
121
|
+
status: taskDetails.status,
|
|
122
|
+
pollCount,
|
|
123
|
+
nextPollIn: pollInterval,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
logger.error({
|
|
130
|
+
msg: 'Timeout waiting for live_url',
|
|
131
|
+
taskId,
|
|
132
|
+
pollCount,
|
|
133
|
+
totalTime: Date.now() - startTime,
|
|
134
|
+
maxWaitTime,
|
|
135
|
+
});
|
|
136
|
+
throw new Error('Timeout waiting for live_url to become available');
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export const waitForCompletion = async (taskId: string, maxWaitTime: number = 300000): Promise<CloudTaskResponse> => {
|
|
140
|
+
const startTime = Date.now();
|
|
141
|
+
const pollInterval = 3000; // 3 seconds for completion polling
|
|
142
|
+
let pollCount = 0;
|
|
143
|
+
|
|
144
|
+
logger.debug({ msg: 'Starting to poll for task completion', taskId, maxWaitTime, pollInterval });
|
|
145
|
+
|
|
146
|
+
while (Date.now() - startTime < maxWaitTime) {
|
|
147
|
+
pollCount++;
|
|
148
|
+
const elapsedTime = Date.now() - startTime;
|
|
149
|
+
|
|
150
|
+
logger.trace({
|
|
151
|
+
msg: 'Polling for task completion',
|
|
152
|
+
taskId,
|
|
153
|
+
pollCount,
|
|
154
|
+
elapsedTime,
|
|
155
|
+
remainingTime: maxWaitTime - elapsedTime,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const taskDetails = await getTaskDetails(taskId);
|
|
159
|
+
|
|
160
|
+
logger.trace({
|
|
161
|
+
msg: 'Task completion status received',
|
|
162
|
+
taskId,
|
|
163
|
+
status: taskDetails.status,
|
|
164
|
+
pollCount,
|
|
165
|
+
hasOutput: !!taskDetails.output,
|
|
166
|
+
stepCount: taskDetails.steps?.length || 0,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
if (taskDetails.status === 'finished') {
|
|
170
|
+
logger.debug({
|
|
171
|
+
msg: 'Task completed successfully',
|
|
172
|
+
taskId,
|
|
173
|
+
pollCount,
|
|
174
|
+
totalTime: elapsedTime,
|
|
175
|
+
stepCount: taskDetails.steps?.length || 0,
|
|
176
|
+
hasOutput: !!taskDetails.output,
|
|
177
|
+
});
|
|
178
|
+
return taskDetails;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (taskDetails.status === 'failed' || taskDetails.status === 'stopped') {
|
|
182
|
+
logger.error({
|
|
183
|
+
msg: 'Task failed during completion polling',
|
|
184
|
+
taskId,
|
|
185
|
+
status: taskDetails.status,
|
|
186
|
+
pollCount,
|
|
187
|
+
elapsedTime,
|
|
188
|
+
stepCount: taskDetails.steps?.length || 0,
|
|
189
|
+
});
|
|
190
|
+
throw new Error(`Task failed with status: ${taskDetails.status}`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
logger.trace({
|
|
194
|
+
msg: 'Task still in progress, continuing to poll',
|
|
195
|
+
taskId,
|
|
196
|
+
status: taskDetails.status,
|
|
197
|
+
pollCount,
|
|
198
|
+
stepCount: taskDetails.steps?.length || 0,
|
|
199
|
+
nextPollIn: pollInterval,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
logger.error({
|
|
206
|
+
msg: 'Timeout waiting for task completion',
|
|
207
|
+
taskId,
|
|
208
|
+
pollCount,
|
|
209
|
+
totalTime: Date.now() - startTime,
|
|
210
|
+
maxWaitTime,
|
|
211
|
+
});
|
|
212
|
+
throw new Error('Timeout waiting for task completion');
|
|
213
|
+
};
|
package/src/cli/index.ts
CHANGED
|
@@ -19,7 +19,7 @@ function setToken(token: string): void {
|
|
|
19
19
|
if (!fs.existsSync(envPath)) {
|
|
20
20
|
// Create .env file with the token
|
|
21
21
|
fs.writeFileSync(envPath, tokenLine + '\n');
|
|
22
|
-
logger.info({
|
|
22
|
+
logger.info({ msg: `Created ${envPath} and added token` });
|
|
23
23
|
} else {
|
|
24
24
|
// Read existing .env file
|
|
25
25
|
const envContent = fs.readFileSync(envPath, 'utf8');
|
|
@@ -29,12 +29,12 @@ function setToken(token: string): void {
|
|
|
29
29
|
// Replace existing token
|
|
30
30
|
const updatedContent = envContent.replace(/MINDED_CONNECTION_TOKEN=.*/, tokenLine);
|
|
31
31
|
fs.writeFileSync(envPath, updatedContent);
|
|
32
|
-
logger.info({
|
|
32
|
+
logger.info({ msg: `Updated MINDED_CONNECTION_TOKEN in ${envPath}` });
|
|
33
33
|
} else {
|
|
34
34
|
// Append token to existing file
|
|
35
35
|
const newContent = envContent.endsWith('\n') ? envContent + tokenLine + '\n' : envContent + '\n' + tokenLine + '\n';
|
|
36
36
|
fs.writeFileSync(envPath, newContent);
|
|
37
|
-
logger.info({
|
|
37
|
+
logger.info({ msg: `Added MINDED_CONNECTION_TOKEN to ${envPath}` });
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -44,7 +44,7 @@ function generateLambdaHandler(): void {
|
|
|
44
44
|
|
|
45
45
|
// Check if minded.json exists
|
|
46
46
|
if (!fs.existsSync(mindedConfigPath)) {
|
|
47
|
-
logger.error({
|
|
47
|
+
logger.error({ msg: 'minded.json not found in the current directory' });
|
|
48
48
|
process.exit(1);
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -54,14 +54,14 @@ function generateLambdaHandler(): void {
|
|
|
54
54
|
const configContent = fs.readFileSync(mindedConfigPath, 'utf8');
|
|
55
55
|
mindedConfig = JSON.parse(configContent);
|
|
56
56
|
} catch (error) {
|
|
57
|
-
logger.error({
|
|
57
|
+
logger.error({ msg: 'Failed to read or parse minded.json:', error });
|
|
58
58
|
process.exit(1);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
// Get the agent path
|
|
62
62
|
const agentPath = mindedConfig.agent;
|
|
63
63
|
if (!agentPath) {
|
|
64
|
-
logger.error({
|
|
64
|
+
logger.error({ msg: 'No agent path found in minded.json' });
|
|
65
65
|
process.exit(1);
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -91,7 +91,7 @@ function generateLambdaHandler(): void {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
if (!templateContent) {
|
|
94
|
-
logger.error({
|
|
94
|
+
logger.error({ msg: 'Could not find Lambda handler template file' });
|
|
95
95
|
process.exit(1);
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -102,10 +102,10 @@ function generateLambdaHandler(): void {
|
|
|
102
102
|
// Write the Lambda handler to index.ts at the root
|
|
103
103
|
const outputPath = path.join(process.cwd(), 'index.ts');
|
|
104
104
|
fs.writeFileSync(outputPath, templateContent);
|
|
105
|
-
logger.info({
|
|
105
|
+
logger.info({ msg: `Generated Lambda handler at ${outputPath}` });
|
|
106
106
|
|
|
107
107
|
// Compile the index.ts file using TypeScript
|
|
108
|
-
logger.info({
|
|
108
|
+
logger.info({ msg: 'Compiling the Lambda handler...' });
|
|
109
109
|
|
|
110
110
|
try {
|
|
111
111
|
// Read tsconfig.json to get the outDir
|
|
@@ -138,12 +138,12 @@ function generateLambdaHandler(): void {
|
|
|
138
138
|
cwd: process.cwd(),
|
|
139
139
|
});
|
|
140
140
|
|
|
141
|
-
logger.info({
|
|
141
|
+
logger.info({ msg: `Successfully compiled Lambda handler to ${outDir}/index.js` });
|
|
142
142
|
} catch (compileError) {
|
|
143
|
-
logger.error({
|
|
143
|
+
logger.error({ msg: 'Failed to compile Lambda handler', error: compileError });
|
|
144
144
|
}
|
|
145
145
|
} catch (error) {
|
|
146
|
-
logger.error({
|
|
146
|
+
logger.error({ msg: 'Failed to generate Lambda handler', error: error });
|
|
147
147
|
process.exit(1);
|
|
148
148
|
}
|
|
149
149
|
}
|
|
@@ -155,14 +155,14 @@ function main() {
|
|
|
155
155
|
if (command === 'token') {
|
|
156
156
|
const token = args[1];
|
|
157
157
|
if (!token) {
|
|
158
|
-
logger.error({
|
|
158
|
+
logger.error({ msg: 'Please provide a token' });
|
|
159
159
|
process.exit(1);
|
|
160
160
|
}
|
|
161
161
|
setToken(token);
|
|
162
162
|
} else if (command === 'generate-lambda-ts-handler') {
|
|
163
163
|
generateLambdaHandler();
|
|
164
164
|
} else {
|
|
165
|
-
logger.error({
|
|
165
|
+
logger.error({ msg: 'Unknown command. Available commands: token, generate-lambda-ts-handler' });
|
|
166
166
|
process.exit(1);
|
|
167
167
|
}
|
|
168
168
|
}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { StepForwardEdge } from '../types/Flows.types';
|
|
2
|
+
import { stateAnnotation } from '../types/LangGraph.types';
|
|
2
3
|
import { logger } from '../utils/logger';
|
|
3
4
|
|
|
4
5
|
export const createDirectEdge = (edge: StepForwardEdge) => {
|
|
5
|
-
return async () => {
|
|
6
|
+
return async (state: typeof stateAnnotation.State) => {
|
|
7
|
+
if (state.goto) {
|
|
8
|
+
console.log('Jumping to node', state.goto);
|
|
9
|
+
return state.goto;
|
|
10
|
+
}
|
|
6
11
|
// For direct edges, we just return the target of the first edge
|
|
7
12
|
// since there's no conditional logic needed
|
|
8
|
-
logger.info({
|
|
13
|
+
logger.info({ msg: `[Router] Direct edge`, target: edge.target });
|
|
9
14
|
return edge.target;
|
|
10
15
|
};
|
|
11
16
|
};
|
|
@@ -8,7 +8,12 @@ const CONDITION_TIMEOUT = 5000; // 5 seconds
|
|
|
8
8
|
|
|
9
9
|
export const createLogicalRouter = ({ edges }: { edges: LogicalConditionEdge[] }) => {
|
|
10
10
|
return async (state: typeof stateAnnotation.State) => {
|
|
11
|
-
logger.debug(`Evaluating logical conditions for ${edges.length} edges`);
|
|
11
|
+
logger.debug({ msg: `[Router] Evaluating logical conditions for ${edges.length} edges` });
|
|
12
|
+
|
|
13
|
+
if (state.goto) {
|
|
14
|
+
console.log('Jumping to node', state.goto);
|
|
15
|
+
return state.goto;
|
|
16
|
+
}
|
|
12
17
|
|
|
13
18
|
// Separate regular conditions from "else" conditions
|
|
14
19
|
const regularEdges = edges.filter((edge) => edge.condition.trim() !== 'else');
|
|
@@ -59,7 +64,16 @@ export const createLogicalRouter = ({ edges }: { edges: LogicalConditionEdge[] }
|
|
|
59
64
|
]);
|
|
60
65
|
|
|
61
66
|
if (result === true) {
|
|
62
|
-
|
|
67
|
+
if (edge.source == edge.target) {
|
|
68
|
+
logger.info({ msg: `[Router] Stay at node ${edge.source}`, node: edge.source, condition: edge.condition });
|
|
69
|
+
} else {
|
|
70
|
+
logger.info({
|
|
71
|
+
msg: `[Router] Logical condition matched`,
|
|
72
|
+
transitionFrom: edge.source,
|
|
73
|
+
transitionTo: edge.target,
|
|
74
|
+
condition: edge.condition,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
63
77
|
return edge.target;
|
|
64
78
|
}
|
|
65
79
|
} catch (error) {
|
|
@@ -68,7 +82,7 @@ export const createLogicalRouter = ({ edges }: { edges: LogicalConditionEdge[] }
|
|
|
68
82
|
const conditionPreview = edge.condition.length > 100 ? `${edge.condition.substring(0, 100)}...` : edge.condition;
|
|
69
83
|
|
|
70
84
|
logger.error({
|
|
71
|
-
|
|
85
|
+
msg: `[Router] Error evaluating condition for edge ${edge.source} → ${edge.target}`,
|
|
72
86
|
condition: conditionPreview,
|
|
73
87
|
error: errorMessage,
|
|
74
88
|
edgeIndex: edges.indexOf(edge),
|
|
@@ -83,15 +97,18 @@ export const createLogicalRouter = ({ edges }: { edges: LogicalConditionEdge[] }
|
|
|
83
97
|
|
|
84
98
|
// If no regular conditions matched, check for "else" conditions
|
|
85
99
|
if (elseEdges.length > 0) {
|
|
86
|
-
logger.info({ message: `No regular conditions matched, evaluating ${elseEdges.length} else condition(s)` });
|
|
87
100
|
// Return the first "else" condition's target
|
|
88
101
|
const elseEdge = elseEdges[0];
|
|
89
|
-
logger.info({
|
|
102
|
+
logger.info({
|
|
103
|
+
msg: `[Router] Else condition matched`,
|
|
104
|
+
transitionFrom: elseEdge.source,
|
|
105
|
+
transitionTo: elseEdge.target,
|
|
106
|
+
});
|
|
90
107
|
return elseEdge.target;
|
|
91
108
|
}
|
|
92
109
|
|
|
93
110
|
// If no conditions matched or all failed, return to the source node
|
|
94
|
-
logger.info({
|
|
111
|
+
logger.info({ msg: `[Router] Stay at node ${edges[0].source}, no conditions matched`, node: edges[0].source });
|
|
95
112
|
return null;
|
|
96
113
|
};
|
|
97
114
|
};
|
|
@@ -70,11 +70,19 @@ export const createPromptRouter = ({
|
|
|
70
70
|
currentNodeName?: string;
|
|
71
71
|
}) => {
|
|
72
72
|
return async (state: typeof stateAnnotation.State) => {
|
|
73
|
-
logger.
|
|
73
|
+
logger.debug({ msg: `[Router] Executing prompt router`, edges: JSON.stringify(edges) });
|
|
74
|
+
|
|
75
|
+
if (state.goto) {
|
|
76
|
+
console.log('Jumping to node', state.goto);
|
|
77
|
+
return state.goto;
|
|
78
|
+
}
|
|
74
79
|
|
|
75
80
|
// If canStayInCurrentNode is true and there are no edges, return current node immediately
|
|
76
81
|
if (canStayInCurrentNode && edges.length === 0 && currentNodeName) {
|
|
77
|
-
logger.info({
|
|
82
|
+
logger.info({
|
|
83
|
+
msg: `[Router] Stay at node, No edges available and canStayInCurrentNode==true`,
|
|
84
|
+
node: currentNodeName,
|
|
85
|
+
});
|
|
78
86
|
return currentNodeName;
|
|
79
87
|
}
|
|
80
88
|
|
|
@@ -174,14 +182,13 @@ export const createPromptRouter = ({
|
|
|
174
182
|
|
|
175
183
|
const decision = validatedResponse.nextNodeId === currentNodeName ? 'stay in current node' : validatedResponse.nextNodeId;
|
|
176
184
|
const reasoning = includeReasoning && 'reasoning' in validatedResponse ? ` - Reasoning: ${validatedResponse.reasoning}` : '';
|
|
177
|
-
logger.
|
|
185
|
+
logger.debug({ msg: `[Router] Decision: ${decision}`, reasoning });
|
|
178
186
|
|
|
179
187
|
return validatedResponse.nextNodeId;
|
|
180
188
|
} catch (error) {
|
|
181
189
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
182
190
|
logger.warn({
|
|
183
|
-
|
|
184
|
-
edge: edges,
|
|
191
|
+
msg: `[Router] Prompt router attempt ${attempts} failed`,
|
|
185
192
|
error: lastError.message,
|
|
186
193
|
attempt: attempts,
|
|
187
194
|
maxRetries,
|
|
@@ -191,7 +198,7 @@ export const createPromptRouter = ({
|
|
|
191
198
|
// If all retries failed, return the first available edge as fallback
|
|
192
199
|
const fallbackNode = edges[0]?.target;
|
|
193
200
|
logger.error({
|
|
194
|
-
|
|
201
|
+
msg: '[Router] Prompt router reached max retries, using fallback',
|
|
195
202
|
fallbackNode,
|
|
196
203
|
lastError: lastError.message,
|
|
197
204
|
});
|
package/src/edges/edgeFactory.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Edge,
|
|
3
|
+
EdgeType,
|
|
4
|
+
Node,
|
|
5
|
+
PromptConditionEdge,
|
|
6
|
+
LogicalConditionEdge,
|
|
7
|
+
StepForwardEdge,
|
|
8
|
+
NodeType,
|
|
9
|
+
ToolNode,
|
|
10
|
+
BrowserTaskNode,
|
|
11
|
+
} from '../types/Flows.types';
|
|
2
12
|
import { PreCompiledGraph } from '../types/LangGraph.types';
|
|
3
13
|
import { BaseLanguageModel } from '@langchain/core/language_models/base';
|
|
4
14
|
import { createPromptRouter } from './createPromptRouter';
|
|
@@ -6,6 +16,7 @@ import { createLogicalRouter } from './createLogicalRouter';
|
|
|
6
16
|
import { createDirectEdge } from './createDirectEdge';
|
|
7
17
|
import { addHumanInTheLoopNode, buildHumanInTheLoopNodeName } from '../nodes/addHumanInTheLoopNode';
|
|
8
18
|
import { addToolRunNode, buildToolRunNodeName } from '../nodes/addToolRunNode';
|
|
19
|
+
import { addBrowserTaskRunNode, buildBrowserTaskRunNodeName } from '../nodes/addBrowserTaskRunNode';
|
|
9
20
|
import { Tool } from '../types/Tools.types';
|
|
10
21
|
import { Agent } from '../agent';
|
|
11
22
|
import { logger } from '../utils/logger';
|
|
@@ -39,7 +50,7 @@ export const edgeFactory = ({
|
|
|
39
50
|
// Priority 1: Step forward edge (max 1)
|
|
40
51
|
if (edgesBySource.stepForward) {
|
|
41
52
|
const directRouter = createDirectEdge(edgesBySource.stepForward);
|
|
42
|
-
return await directRouter();
|
|
53
|
+
return await directRouter(state);
|
|
43
54
|
}
|
|
44
55
|
|
|
45
56
|
// Priority 2: Logical condition edges
|
|
@@ -49,7 +60,7 @@ export const edgeFactory = ({
|
|
|
49
60
|
if (result) {
|
|
50
61
|
return result;
|
|
51
62
|
} else {
|
|
52
|
-
logger.debug({
|
|
63
|
+
logger.debug({ msg: 'No logical conditions matched, continuing to prompt conditions' });
|
|
53
64
|
}
|
|
54
65
|
}
|
|
55
66
|
|
|
@@ -69,7 +80,7 @@ export const edgeFactory = ({
|
|
|
69
80
|
|
|
70
81
|
// Fallback: stay at current source node
|
|
71
82
|
const source = originalNode?.name || sourceNode;
|
|
72
|
-
logger.info({
|
|
83
|
+
logger.info({ msg: `No conditions matched, returning to source: ${source}` });
|
|
73
84
|
return source;
|
|
74
85
|
};
|
|
75
86
|
};
|
|
@@ -92,6 +103,11 @@ export const edgeFactory = ({
|
|
|
92
103
|
effectiveSource = buildToolRunNodeName(source);
|
|
93
104
|
}
|
|
94
105
|
|
|
106
|
+
if (originalNode?.type === NodeType.BROWSER_TASK) {
|
|
107
|
+
addBrowserTaskRunNode({ graph, browserTaskNode: originalNode as BrowserTaskNode, attachedToNodeName: source, agent });
|
|
108
|
+
effectiveSource = buildBrowserTaskRunNodeName(source);
|
|
109
|
+
}
|
|
110
|
+
|
|
95
111
|
// Create and add the combined conditional edge with original node reference
|
|
96
112
|
const combinedRouter = createCombinedRouter(effectiveSource, edgesBySource, originalNode);
|
|
97
113
|
graph.addConditionalEdges(effectiveSource as any, combinedRouter);
|