@minded-ai/mindedjs 1.0.108 → 1.0.109-beta-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.
Files changed (112) hide show
  1. package/dist/agent.d.ts +12 -12
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +37 -13
  4. package/dist/agent.js.map +1 -1
  5. package/dist/browserTask/README.md +419 -0
  6. package/dist/browserTask/browserAgent.py +632 -0
  7. package/dist/browserTask/captcha_isolated.png +0 -0
  8. package/dist/browserTask/executeBrowserTask.d.ts +1 -11
  9. package/dist/browserTask/executeBrowserTask.d.ts.map +1 -1
  10. package/dist/browserTask/executeBrowserTask.js +67 -170
  11. package/dist/browserTask/executeBrowserTask.js.map +1 -1
  12. package/dist/browserTask/executeBrowserTask.ts +79 -0
  13. package/dist/browserTask/requirements.txt +8 -0
  14. package/dist/browserTask/setup.sh +144 -0
  15. package/dist/cli/index.js +103 -1
  16. package/dist/cli/index.js.map +1 -1
  17. package/dist/edges/createLogicalRouter.d.ts +3 -1
  18. package/dist/edges/createLogicalRouter.d.ts.map +1 -1
  19. package/dist/edges/createLogicalRouter.js +41 -2
  20. package/dist/edges/createLogicalRouter.js.map +1 -1
  21. package/dist/edges/edgeFactory.d.ts.map +1 -1
  22. package/dist/edges/edgeFactory.js +7 -7
  23. package/dist/edges/edgeFactory.js.map +1 -1
  24. package/dist/events/AgentEvents.d.ts +19 -1
  25. package/dist/events/AgentEvents.d.ts.map +1 -1
  26. package/dist/events/AgentEvents.js +2 -0
  27. package/dist/events/AgentEvents.js.map +1 -1
  28. package/dist/index.d.ts +2 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +4 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/internalTools/timer.d.ts +3 -3
  33. package/dist/internalTools/timer.d.ts.map +1 -1
  34. package/dist/internalTools/timer.js +3 -3
  35. package/dist/internalTools/timer.js.map +1 -1
  36. package/dist/nodes/addBrowserTaskNode.d.ts +1 -3
  37. package/dist/nodes/addBrowserTaskNode.d.ts.map +1 -1
  38. package/dist/nodes/addBrowserTaskNode.js +54 -186
  39. package/dist/nodes/addBrowserTaskNode.js.map +1 -1
  40. package/dist/nodes/nodeFactory.js +1 -1
  41. package/dist/nodes/nodeFactory.js.map +1 -1
  42. package/docs/SUMMARY.md +8 -4
  43. package/docs/low-code-editor/edges.md +4 -0
  44. package/docs/sdk/debugging.md +342 -0
  45. package/docs/{platform → sdk}/events.md +168 -1
  46. package/package.json +12 -5
  47. package/dist/nodes/addBrowserTaskRunNode.d.ts +0 -13
  48. package/dist/nodes/addBrowserTaskRunNode.d.ts.map +0 -1
  49. package/dist/nodes/addBrowserTaskRunNode.js +0 -130
  50. package/dist/nodes/addBrowserTaskRunNode.js.map +0 -1
  51. package/src/agent.ts +0 -928
  52. package/src/browserTask/executeBrowserTask.ts +0 -213
  53. package/src/checkpointer/checkpointSaverFactory.ts +0 -18
  54. package/src/cli/index.ts +0 -170
  55. package/src/cli/lambdaHandlerTemplate.ts +0 -45
  56. package/src/edges/createDirectEdge.ts +0 -16
  57. package/src/edges/createLogicalRouter.ts +0 -114
  58. package/src/edges/createPromptRouter.ts +0 -218
  59. package/src/edges/edgeFactory.ts +0 -141
  60. package/src/events/AgentEvents.ts +0 -47
  61. package/src/events/index.ts +0 -3
  62. package/src/index.ts +0 -70
  63. package/src/interfaces/zendesk.ts +0 -157
  64. package/src/internalTools/appActionRunnerTool.ts +0 -68
  65. package/src/internalTools/documentExtraction/documentExtraction.ts +0 -809
  66. package/src/internalTools/documentExtraction/types.ts +0 -59
  67. package/src/internalTools/libraryActionRunnerTool.ts +0 -63
  68. package/src/internalTools/retell.ts +0 -28
  69. package/src/internalTools/sendPlaceholderMessage.ts +0 -27
  70. package/src/internalTools/timer.ts +0 -137
  71. package/src/llm/createLlmInstance.ts +0 -33
  72. package/src/nodes/addAppToolNode.ts +0 -106
  73. package/src/nodes/addBrowserTaskNode.ts +0 -231
  74. package/src/nodes/addBrowserTaskRunNode.ts +0 -144
  75. package/src/nodes/addHumanInTheLoopNode.ts +0 -25
  76. package/src/nodes/addJumpToNode.ts +0 -25
  77. package/src/nodes/addJunctionNode.ts +0 -20
  78. package/src/nodes/addPromptNode.ts +0 -119
  79. package/src/nodes/addToolNode.ts +0 -72
  80. package/src/nodes/addToolRunNode.ts +0 -76
  81. package/src/nodes/addTriggerNode.ts +0 -27
  82. package/src/nodes/nodeFactory.ts +0 -57
  83. package/src/platform/config.ts +0 -77
  84. package/src/platform/mindedCheckpointSaver.ts +0 -146
  85. package/src/platform/mindedConnection.ts +0 -199
  86. package/src/platform/mindedConnectionTypes.ts +0 -220
  87. package/src/platform/models/mindedChatOpenAI.ts +0 -49
  88. package/src/platform/models/parallelWrapper.ts +0 -141
  89. package/src/platform/piiGateway/gateway.ts +0 -103
  90. package/src/platform/piiGateway/index.ts +0 -5
  91. package/src/platform/piiGateway/types.ts +0 -29
  92. package/src/platform/utils/parseAttachments.ts +0 -56
  93. package/src/playbooks/playbooks.ts +0 -209
  94. package/src/toolsLibrary/index.ts +0 -6
  95. package/src/toolsLibrary/parseDocument.ts +0 -136
  96. package/src/triggers/triggerTypeToDefaultMessage.ts +0 -9
  97. package/src/types/Agent.types.ts +0 -67
  98. package/src/types/Flows.types.ts +0 -200
  99. package/src/types/LLM.types.ts +0 -15
  100. package/src/types/LangGraph.types.ts +0 -53
  101. package/src/types/Platform.types.ts +0 -1
  102. package/src/types/Tools.types.ts +0 -31
  103. package/src/types/Voice.types.ts +0 -4
  104. package/src/utils/extractStateMemoryResponse.ts +0 -16
  105. package/src/utils/history.ts +0 -9
  106. package/src/utils/logger.ts +0 -22
  107. package/src/utils/wait.ts +0 -1
  108. package/src/voice/elevenLabsUtils.ts +0 -81
  109. package/src/voice/voiceSession.ts +0 -294
  110. /package/docs/{platform → sdk}/logging.md +0 -0
  111. /package/docs/{platform → sdk}/memory.md +0 -0
  112. /package/docs/{platform → sdk}/parallel-llm.md +0 -0
@@ -1,213 +0,0 @@
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
- };
@@ -1,18 +0,0 @@
1
- import { BaseCheckpointSaver, MemorySaver } from '@langchain/langgraph';
2
- import { MindedCheckpointSaver } from '../platform/mindedCheckpointSaver';
3
- import * as mindedConnection from '../platform/mindedConnection';
4
- import { logger } from '../utils/logger';
5
-
6
- /**
7
- * Factory function to create the appropriate checkpoint saver based on environment variables.
8
- * @returns BaseCheckpointSaver instance (either MemorySaver or MindedCheckpointSaver)
9
- */
10
- export function createCheckpointSaver(): BaseCheckpointSaver {
11
- if (!mindedConnection.isConnected()) {
12
- logger.warn('mindedConnection not connected, falling back to in-memory checkpoint saver');
13
- return new MemorySaver();
14
- }
15
-
16
- logger.debug('[Agent] Using remote checkpoint saver');
17
- return new MindedCheckpointSaver();
18
- }
package/src/cli/index.ts DELETED
@@ -1,170 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import * as fs from 'fs';
4
- import * as path from 'path';
5
- import { logger } from '../utils/logger';
6
- import { execSync } from 'child_process';
7
-
8
- const ENV_FILE = '.env';
9
-
10
- function getEnvFilePath(): string {
11
- return path.join(process.cwd(), ENV_FILE);
12
- }
13
-
14
- function setToken(token: string): void {
15
- const envPath = getEnvFilePath();
16
- const tokenLine = `MINDED_CONNECTION_TOKEN=${token}`;
17
-
18
- // Check if .env file exists
19
- if (!fs.existsSync(envPath)) {
20
- // Create .env file with the token
21
- fs.writeFileSync(envPath, tokenLine + '\n');
22
- logger.info(`Created ${envPath} and added token`);
23
- } else {
24
- // Read existing .env file
25
- const envContent = fs.readFileSync(envPath, 'utf8');
26
-
27
- // Check if MINDED_CONNECTION_TOKEN already exists
28
- if (envContent.includes('MINDED_CONNECTION_TOKEN=')) {
29
- // Replace existing token
30
- const updatedContent = envContent.replace(/MINDED_CONNECTION_TOKEN=.*/, tokenLine);
31
- fs.writeFileSync(envPath, updatedContent);
32
- logger.info(`Updated MINDED_CONNECTION_TOKEN in ${envPath}`);
33
- } else {
34
- // Append token to existing file
35
- const newContent = envContent.endsWith('\n') ? envContent + tokenLine + '\n' : envContent + '\n' + tokenLine + '\n';
36
- fs.writeFileSync(envPath, newContent);
37
- logger.info(`Added MINDED_CONNECTION_TOKEN to ${envPath}`);
38
- }
39
- }
40
- }
41
-
42
- function generateLambdaHandler(): void {
43
- const mindedConfigPath = path.join(process.cwd(), 'minded.json');
44
-
45
- // Check if minded.json exists
46
- if (!fs.existsSync(mindedConfigPath)) {
47
- logger.error('minded.json not found in the current directory');
48
- process.exit(1);
49
- }
50
-
51
- // Read and parse minded.json
52
- let mindedConfig;
53
- try {
54
- const configContent = fs.readFileSync(mindedConfigPath, 'utf8');
55
- mindedConfig = JSON.parse(configContent);
56
- } catch (error) {
57
- logger.error('Failed to read or parse minded.json:', error);
58
- process.exit(1);
59
- }
60
-
61
- // Get the agent path
62
- const agentPath = mindedConfig.agent;
63
- if (!agentPath) {
64
- logger.error('No agent path found in minded.json');
65
- process.exit(1);
66
- }
67
-
68
- // Remove .ts or .js extension if present and convert to relative path
69
- const modulePath = agentPath.replace(/\.(ts|js)$/, '');
70
-
71
- // Get the path to the template file
72
- // Try multiple locations to support both development and npm package scenarios
73
- const templatePaths = [
74
- path.join(__dirname, 'lambdaHandlerTemplate.ts'), // For npm package
75
- path.join(__dirname, '..', '..', 'src', 'cli', 'lambdaHandlerTemplate.ts'), // For development
76
- ];
77
-
78
- let templateContent: string | null = null;
79
-
80
- // Try each path until we find the template
81
- for (const templatePath of templatePaths) {
82
- try {
83
- if (fs.existsSync(templatePath)) {
84
- templateContent = fs.readFileSync(templatePath, 'utf8');
85
- logger.debug(`Found template at ${templatePath}`);
86
- break;
87
- }
88
- } catch {
89
- // Continue to next path
90
- }
91
- }
92
-
93
- if (!templateContent) {
94
- logger.error('Could not find Lambda handler template file');
95
- process.exit(1);
96
- }
97
-
98
- try {
99
- // Replace the placeholder with the actual module path
100
- templateContent = templateContent.replace(/{MODULE_PATH}/g, modulePath);
101
-
102
- // Write the Lambda handler to index.ts at the root
103
- const outputPath = path.join(process.cwd(), 'index.ts');
104
- fs.writeFileSync(outputPath, templateContent);
105
- logger.info(`Generated Lambda handler at ${outputPath}`);
106
-
107
- // Compile the index.ts file using TypeScript
108
- logger.info('Compiling the Lambda handler...');
109
-
110
- try {
111
- // Read tsconfig.json to get the outDir
112
- let outDir = 'dist'; // Default value
113
- const tsconfigPath = path.join(process.cwd(), 'tsconfig.json');
114
-
115
- if (fs.existsSync(tsconfigPath)) {
116
- try {
117
- const tsconfigContent = fs.readFileSync(tsconfigPath, 'utf8');
118
- const tsconfig = JSON.parse(tsconfigContent);
119
-
120
- if (tsconfig.compilerOptions && tsconfig.compilerOptions.outDir) {
121
- outDir = tsconfig.compilerOptions.outDir;
122
- logger.debug(`Using outDir from tsconfig.json: ${outDir}`);
123
- }
124
- } catch {
125
- logger.warn('Failed to parse tsconfig.json, using default outDir: dist');
126
- }
127
- }
128
-
129
- // Check if output directory exists, create if it doesn't
130
- const distDir = path.join(process.cwd(), outDir);
131
- if (!fs.existsSync(distDir)) {
132
- fs.mkdirSync(distDir, { recursive: true });
133
- }
134
-
135
- // Compile index.ts to the configured output directory
136
- execSync(`npx tsc --target ES2018 --module CommonJS --esModuleInterop ${outputPath} --outDir ${outDir}`, {
137
- stdio: 'pipe',
138
- cwd: process.cwd(),
139
- });
140
-
141
- logger.info(`Successfully compiled Lambda handler to ${outDir}/index.js`);
142
- } catch (compileError) {
143
- logger.error({ message: 'Failed to compile Lambda handler', error: compileError });
144
- }
145
- } catch (error) {
146
- logger.error({ message: 'Failed to generate Lambda handler', error: error });
147
- process.exit(1);
148
- }
149
- }
150
-
151
- function main() {
152
- const args = process.argv.slice(2);
153
- const command = args[0];
154
-
155
- if (command === 'token') {
156
- const token = args[1];
157
- if (!token) {
158
- logger.error('Please provide a token');
159
- process.exit(1);
160
- }
161
- setToken(token);
162
- } else if (command === 'generate-lambda-ts-handler') {
163
- generateLambdaHandler();
164
- } else {
165
- logger.error('Unknown command. Available commands: token, generate-lambda-ts-handler');
166
- process.exit(1);
167
- }
168
- }
169
-
170
- main();
@@ -1,45 +0,0 @@
1
- // This is the template for the Lambda handler
2
- // The {MODULE_PATH} placeholder will be replaced with the actual path
3
-
4
- export const handler = async (event: any, context: any) => {
5
- const modulePath = '{MODULE_PATH}'; // Module path placeholder - DO NOT CHANGE THE {MODULE_PATH} TOKEN
6
- const resolvedPath = require.resolve(modulePath);
7
- delete require.cache[resolvedPath];
8
- // eslint-disable-next-line @typescript-eslint/no-require-imports
9
- const agent = require(modulePath).default;
10
- const { agentFunctionName, agentFunctionBody } = event.body ? JSON.parse(event.body) : event;
11
-
12
- if (!agent[agentFunctionName]) {
13
- throw new Error('Unknown agentFunctionName: ' + agentFunctionName);
14
- }
15
-
16
- const response = await agent[agentFunctionName](agentFunctionBody);
17
-
18
- // Safely stringify the response. If it fails (e.g., due to circular refs
19
- // or non-serialisable class instances), fall back to null.
20
- let responseBody = null;
21
- try {
22
- JSON.stringify(response);
23
- responseBody = response;
24
- } catch {
25
- // ignored - responseBody stays null
26
- }
27
-
28
- if (agentFunctionName === 'startVoiceSession') {
29
- const wait = (ms: number) => new Promise((r) => setTimeout(r, ms));
30
- while (context.getRemainingTimeInMillis() > 1000) {
31
- console.log('Lambda remaining time in seconds: ', context.getRemainingTimeInMillis() / 1000);
32
- await wait(1000); // sleep 1 s
33
- }
34
- }
35
-
36
- return {
37
- statusCode: 200,
38
- headers: {
39
- 'Content-Type': 'application/json',
40
- 'Access-Control-Allow-Origin': '*',
41
- 'Access-Control-Allow-Credentials': true,
42
- },
43
- body: JSON.stringify(responseBody),
44
- };
45
- };
@@ -1,16 +0,0 @@
1
- import { StepForwardEdge } from '../types/Flows.types';
2
- import { stateAnnotation } from '../types/LangGraph.types';
3
- import { logger } from '../utils/logger';
4
-
5
- export const createDirectEdge = (edge: StepForwardEdge) => {
6
- return async (state: typeof stateAnnotation.State) => {
7
- if (state.goto) {
8
- console.log('Jumping to node', state.goto);
9
- return state.goto;
10
- }
11
- // For direct edges, we just return the target of the first edge
12
- // since there's no conditional logic needed
13
- logger.info({ msg: `[Router] Direct edge`, target: edge.target });
14
- return edge.target;
15
- };
16
- };
@@ -1,114 +0,0 @@
1
- import { LogicalConditionEdge } from '../types/Flows.types';
2
- import { stateAnnotation } from '../types/LangGraph.types';
3
- import { logger } from '../utils/logger';
4
- import * as vm from 'vm';
5
-
6
- // Default timeout for condition evaluation (in milliseconds)
7
- const CONDITION_TIMEOUT = 5000; // 5 seconds
8
-
9
- export const createLogicalRouter = ({ edges }: { edges: LogicalConditionEdge[] }) => {
10
- return async (state: typeof stateAnnotation.State) => {
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
- }
17
-
18
- // Separate regular conditions from "else" conditions
19
- const regularEdges = edges.filter((edge) => edge.condition.trim() !== 'else');
20
- const elseEdges = edges.filter((edge) => edge.condition.trim() === 'else');
21
-
22
- // First, evaluate all regular conditions
23
- for (const edge of regularEdges) {
24
- try {
25
- // Customer is responsible for providing valid JavaScript syntax
26
- // We execute their condition in a sandboxed VM with timeout protection
27
- const conditionCode = `
28
- (function({ state }) {
29
- return ${edge.condition}
30
- })
31
- `;
32
-
33
- // Create a sandboxed context with limited access
34
- const context = vm.createContext({
35
- // Provide safe utilities that customers might need
36
- console: {
37
- log: (...args: any[]) => logger.debug('Customer condition log:', ...args),
38
- error: (...args: any[]) => logger.debug('Customer condition error:', ...args),
39
- },
40
- // Add other safe globals if needed
41
- JSON,
42
- Math,
43
- Date,
44
- // Prevent access to dangerous globals
45
- process: undefined,
46
- require: undefined,
47
- global: undefined,
48
- Buffer: undefined,
49
- });
50
-
51
- // Compile and run the condition with timeout
52
- const script = new vm.Script(conditionCode, {
53
- filename: `edge-condition-${edge.source}-to-${edge.target}`,
54
- });
55
-
56
- const conditionFn = script.runInContext(context, {
57
- timeout: CONDITION_TIMEOUT,
58
- });
59
-
60
- // Execute the customer's condition with the current state
61
- const result = await Promise.race([
62
- Promise.resolve(conditionFn({ state })),
63
- new Promise((_, reject) => setTimeout(() => reject(new Error('Condition execution timeout')), CONDITION_TIMEOUT)),
64
- ]);
65
-
66
- if (result === true) {
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
- }
77
- return edge.target;
78
- }
79
- } catch (error) {
80
- // Provide detailed error information back to the customer
81
- const errorMessage = error instanceof Error ? error.message : String(error);
82
- const conditionPreview = edge.condition.length > 100 ? `${edge.condition.substring(0, 100)}...` : edge.condition;
83
-
84
- logger.error({
85
- msg: `[Router] Error evaluating condition for edge ${edge.source} → ${edge.target}`,
86
- condition: conditionPreview,
87
- error: errorMessage,
88
- edgeIndex: edges.indexOf(edge),
89
- timeout: CONDITION_TIMEOUT,
90
- });
91
-
92
- // Continue to next edge instead of failing completely
93
- // This allows other conditions to be evaluated even if one fails
94
- continue;
95
- }
96
- }
97
-
98
- // If no regular conditions matched, check for "else" conditions
99
- if (elseEdges.length > 0) {
100
- // Return the first "else" condition's target
101
- const elseEdge = elseEdges[0];
102
- logger.info({
103
- msg: `[Router] Else condition matched`,
104
- transitionFrom: elseEdge.source,
105
- transitionTo: elseEdge.target,
106
- });
107
- return elseEdge.target;
108
- }
109
-
110
- // If no conditions matched or all failed, return to the source node
111
- logger.info({ msg: `[Router] Stay at node ${edges[0].source}, no conditions matched`, node: edges[0].source });
112
- return null;
113
- };
114
- };