@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.
Files changed (136) hide show
  1. package/dist/agent.d.ts.map +1 -1
  2. package/dist/agent.js +29 -16
  3. package/dist/agent.js.map +1 -1
  4. package/dist/browserTask/executeBrowserTask.d.ts +12 -0
  5. package/dist/browserTask/executeBrowserTask.d.ts.map +1 -0
  6. package/dist/browserTask/executeBrowserTask.js +181 -0
  7. package/dist/browserTask/executeBrowserTask.js.map +1 -0
  8. package/dist/checkpointer/checkpointSaverFactory.js +1 -1
  9. package/dist/checkpointer/checkpointSaverFactory.js.map +1 -1
  10. package/dist/cli/index.js +14 -14
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/edges/createDirectEdge.d.ts +2 -1
  13. package/dist/edges/createDirectEdge.d.ts.map +1 -1
  14. package/dist/edges/createDirectEdge.js +6 -2
  15. package/dist/edges/createDirectEdge.js.map +1 -1
  16. package/dist/edges/createLogicalRouter.d.ts.map +1 -1
  17. package/dist/edges/createLogicalRouter.js +23 -6
  18. package/dist/edges/createLogicalRouter.js.map +1 -1
  19. package/dist/edges/createPromptRouter.d.ts.map +1 -1
  20. package/dist/edges/createPromptRouter.js +12 -6
  21. package/dist/edges/createPromptRouter.js.map +1 -1
  22. package/dist/edges/edgeFactory.d.ts.map +1 -1
  23. package/dist/edges/edgeFactory.js +8 -3
  24. package/dist/edges/edgeFactory.js.map +1 -1
  25. package/dist/index.d.ts +4 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +6 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/interrupts/BaseInterruptSessionManager.js +1 -1
  30. package/dist/interrupts/BaseInterruptSessionManager.js.map +1 -1
  31. package/dist/interrupts/interruptSessionManagerFactory.js +2 -2
  32. package/dist/interrupts/interruptSessionManagerFactory.js.map +1 -1
  33. package/dist/llm/createLlmInstance.d.ts +1 -1
  34. package/dist/llm/createLlmInstance.d.ts.map +1 -1
  35. package/dist/llm/createLlmInstance.js +18 -1
  36. package/dist/llm/createLlmInstance.js.map +1 -1
  37. package/dist/nodes/addAppToolNode.d.ts.map +1 -1
  38. package/dist/nodes/addAppToolNode.js +5 -4
  39. package/dist/nodes/addAppToolNode.js.map +1 -1
  40. package/dist/nodes/addBrowserTaskNode.d.ts +13 -0
  41. package/dist/nodes/addBrowserTaskNode.d.ts.map +1 -0
  42. package/dist/nodes/addBrowserTaskNode.js +232 -0
  43. package/dist/nodes/addBrowserTaskNode.js.map +1 -0
  44. package/dist/nodes/addBrowserTaskRunNode.d.ts +13 -0
  45. package/dist/nodes/addBrowserTaskRunNode.d.ts.map +1 -0
  46. package/dist/nodes/addBrowserTaskRunNode.js +130 -0
  47. package/dist/nodes/addBrowserTaskRunNode.js.map +1 -0
  48. package/dist/nodes/addHumanInTheLoopNode.d.ts.map +1 -1
  49. package/dist/nodes/addHumanInTheLoopNode.js +1 -1
  50. package/dist/nodes/addHumanInTheLoopNode.js.map +1 -1
  51. package/dist/nodes/addJumpToNode.d.ts.map +1 -1
  52. package/dist/nodes/addJumpToNode.js +2 -1
  53. package/dist/nodes/addJumpToNode.js.map +1 -1
  54. package/dist/nodes/addJunctionNode.d.ts.map +1 -1
  55. package/dist/nodes/addJunctionNode.js +1 -0
  56. package/dist/nodes/addJunctionNode.js.map +1 -1
  57. package/dist/nodes/addPromptNode.d.ts.map +1 -1
  58. package/dist/nodes/addPromptNode.js +8 -18
  59. package/dist/nodes/addPromptNode.js.map +1 -1
  60. package/dist/nodes/addToolNode.js +2 -4
  61. package/dist/nodes/addToolNode.js.map +1 -1
  62. package/dist/nodes/addToolRunNode.d.ts.map +1 -1
  63. package/dist/nodes/addToolRunNode.js +2 -1
  64. package/dist/nodes/addToolRunNode.js.map +1 -1
  65. package/dist/nodes/addTriggerNode.d.ts.map +1 -1
  66. package/dist/nodes/addTriggerNode.js +2 -1
  67. package/dist/nodes/addTriggerNode.js.map +1 -1
  68. package/dist/nodes/nodeFactory.d.ts.map +1 -1
  69. package/dist/nodes/nodeFactory.js +4 -0
  70. package/dist/nodes/nodeFactory.js.map +1 -1
  71. package/dist/platform/mindedConnection.js +13 -13
  72. package/dist/platform/mindedConnection.js.map +1 -1
  73. package/dist/platform/models/mindedChatOpenAI.d.ts +20 -0
  74. package/dist/platform/models/mindedChatOpenAI.d.ts.map +1 -0
  75. package/dist/platform/models/mindedChatOpenAI.js +32 -0
  76. package/dist/platform/models/mindedChatOpenAI.js.map +1 -0
  77. package/dist/platform/models/parallelWrapper.d.ts +17 -0
  78. package/dist/platform/models/parallelWrapper.d.ts.map +1 -0
  79. package/dist/platform/models/parallelWrapper.js +105 -0
  80. package/dist/platform/models/parallelWrapper.js.map +1 -0
  81. package/dist/playbooks/playbooks.js +6 -6
  82. package/dist/playbooks/playbooks.js.map +1 -1
  83. package/dist/types/Flows.types.d.ts +18 -3
  84. package/dist/types/Flows.types.d.ts.map +1 -1
  85. package/dist/types/Flows.types.js +2 -0
  86. package/dist/types/Flows.types.js.map +1 -1
  87. package/dist/types/LLM.types.d.ts.map +1 -1
  88. package/dist/types/LLM.types.js +1 -1
  89. package/dist/types/LLM.types.js.map +1 -1
  90. package/dist/types/LangGraph.types.d.ts +2 -0
  91. package/dist/types/LangGraph.types.d.ts.map +1 -1
  92. package/dist/types/LangGraph.types.js +5 -0
  93. package/dist/types/LangGraph.types.js.map +1 -1
  94. package/dist/utils/logger.js +1 -1
  95. package/dist/utils/logger.js.map +1 -1
  96. package/dist/voice/voiceSession.d.ts.map +1 -1
  97. package/dist/voice/voiceSession.js +16 -17
  98. package/dist/voice/voiceSession.js.map +1 -1
  99. package/docs/SUMMARY.md +1 -0
  100. package/docs/low-code-editor/nodes.md +27 -0
  101. package/docs/low-code-editor/tools.md +32 -0
  102. package/docs/platform/parallel-llm.md +242 -0
  103. package/package.json +2 -1
  104. package/src/agent.ts +30 -18
  105. package/src/browserTask/executeBrowserTask.ts +213 -0
  106. package/src/checkpointer/checkpointSaverFactory.ts +1 -1
  107. package/src/cli/index.ts +14 -14
  108. package/src/edges/createDirectEdge.ts +7 -2
  109. package/src/edges/createLogicalRouter.ts +23 -6
  110. package/src/edges/createPromptRouter.ts +13 -6
  111. package/src/edges/edgeFactory.ts +20 -4
  112. package/src/index.ts +6 -0
  113. package/src/interrupts/BaseInterruptSessionManager.ts +1 -1
  114. package/src/interrupts/interruptSessionManagerFactory.ts +2 -2
  115. package/src/llm/createLlmInstance.ts +25 -2
  116. package/src/nodes/addAppToolNode.ts +5 -4
  117. package/src/nodes/addBrowserTaskNode.ts +231 -0
  118. package/src/nodes/addBrowserTaskRunNode.ts +144 -0
  119. package/src/nodes/addHumanInTheLoopNode.ts +2 -1
  120. package/src/nodes/addJumpToNode.ts +2 -1
  121. package/src/nodes/addJunctionNode.ts +1 -0
  122. package/src/nodes/addPromptNode.ts +8 -19
  123. package/src/nodes/addToolNode.ts +4 -4
  124. package/src/nodes/addToolRunNode.ts +3 -1
  125. package/src/nodes/addTriggerNode.ts +2 -1
  126. package/src/nodes/nodeFactory.ts +5 -1
  127. package/src/platform/mindedConnection.ts +13 -13
  128. package/src/platform/models/mindedChatOpenAI.ts +49 -0
  129. package/src/platform/models/parallelWrapper.ts +141 -0
  130. package/src/playbooks/playbooks.ts +6 -6
  131. package/src/types/Flows.types.ts +17 -1
  132. package/src/types/LLM.types.ts +5 -5
  133. package/src/types/LangGraph.types.ts +5 -0
  134. package/src/utils/logger.ts +1 -1
  135. package/src/voice/voiceSession.ts +16 -17
  136. 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 && nodes.length > 0) {
308
- this.startingNodeId = nodes[0].name;
309
- graph.addEdge('__start__', nodes[0].name as any);
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({ message: 'Enqueuing message', sessionId, triggerBody, triggerName, appName });
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: `Trigger ${triggerName} was disqualified` });
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({ message: 'Invoking trigger', triggerName, sessionId, triggerBody });
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({ message: 'Invoking next message in the queue', invokeParams });
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
- message: 'Invoke error',
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.info({ message: 'Running locally - skipping secret loading' });
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({ message: 'Setting up voice' });
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({ message: 'Dashboard eneded voice session', sessionId: hangup.sessionId });
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({ message: 'Voice session initialized', sessionId: params.sessionId });
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({ message: 'Restoring checkpoint', sessionId, checkpointId });
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
+ };
@@ -13,6 +13,6 @@ export function createCheckpointSaver(): BaseCheckpointSaver {
13
13
  return new MemorySaver();
14
14
  }
15
15
 
16
- logger.debug('Using remote checkpoint saver');
16
+ logger.debug('[Agent] Using remote checkpoint saver');
17
17
  return new MindedCheckpointSaver();
18
18
  }
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({ message: `Created ${envPath} and added token` });
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({ message: `Updated MINDED_CONNECTION_TOKEN in ${envPath}` });
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({ message: `Added MINDED_CONNECTION_TOKEN to ${envPath}` });
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({ message: 'minded.json not found in the current directory' });
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({ message: 'Failed to read or parse minded.json:', 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({ message: 'No agent path found in minded.json' });
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({ message: 'Could not find Lambda handler template file' });
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({ message: `Generated Lambda handler at ${outputPath}` });
105
+ logger.info({ msg: `Generated Lambda handler at ${outputPath}` });
106
106
 
107
107
  // Compile the index.ts file using TypeScript
108
- logger.info({ message: 'Compiling the Lambda handler...' });
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({ message: `Successfully compiled Lambda handler to ${outDir}/index.js` });
141
+ logger.info({ msg: `Successfully compiled Lambda handler to ${outDir}/index.js` });
142
142
  } catch (compileError) {
143
- logger.error({ message: 'Failed to compile Lambda handler', error: compileError });
143
+ logger.error({ msg: 'Failed to compile Lambda handler', error: compileError });
144
144
  }
145
145
  } catch (error) {
146
- logger.error({ message: 'Failed to generate Lambda handler', error: 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({ message: 'Please provide a token' });
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({ message: 'Unknown command. Available commands: token, generate-lambda-ts-handler' });
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({ message: `Executing edge ${JSON.stringify(edge)}` });
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
- logger.info({ message: `Condition matched for edge ${edge.source} ${edge.target}` });
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
- message: `Error evaluating condition for edge ${edge.source} → ${edge.target}`,
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({ message: `Else condition matched for edge ${elseEdge.source} → ${elseEdge.target}`, edge: elseEdge });
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({ message: 'No conditions matched' });
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.info({ message: `Executing prompt router. Edges: ${JSON.stringify(edges)}` });
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({ message: `No edges available and canStayInCurrentNode is true, staying in current node: ${currentNodeName}` });
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.info({ message: `Router decision: ${decision}`, reasoning });
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
- message: `Prompt router attempt ${attempts} failed`,
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
- message: 'Prompt router reached max retries, using fallback',
201
+ msg: '[Router] Prompt router reached max retries, using fallback',
195
202
  fallbackNode,
196
203
  lastError: lastError.message,
197
204
  });
@@ -1,4 +1,14 @@
1
- import { Edge, EdgeType, Node, PromptConditionEdge, LogicalConditionEdge, StepForwardEdge, NodeType, ToolNode } from '../types/Flows.types';
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({ message: 'No logical conditions matched, continuing to prompt conditions' });
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({ message: `No conditions matched, returning to source: ${source}` });
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);