@minded-ai/mindedjs 1.0.0-ec2-beta-14 → 1.0.0-ec2-beta-15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minded-ai/mindedjs",
3
- "version": "1.0.0-ec2-beta-14",
3
+ "version": "1.0.0-ec2-beta-15",
4
4
  "description": "MindedJS is a TypeScript library for building agents.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/agent.ts CHANGED
@@ -510,7 +510,7 @@ export class Agent {
510
510
  // Add playbooks to messages
511
511
  const combinedPlaybooks = combinePlaybooks(this.playbooks);
512
512
  if (combinedPlaybooks) {
513
- const compiledPrompt = compilePrompt(combinedPlaybooks, state);
513
+ const compiledPrompt = compilePrompt(combinedPlaybooks, { state: state, memory: state.memory, env: process.env });
514
514
  const systemMessage = new SystemMessage(compiledPrompt);
515
515
  if (state.messages.length === 0 || state.messages[0].getType() === 'system') {
516
516
  state.messages[0] = systemMessage;
@@ -172,12 +172,11 @@ def create_pydantic_model_from_schema(output_schema: Optional[List[Dict[str, Any
172
172
 
173
173
 
174
174
  async def main(session_id: str, cdp_url: str, task: str, output_schema_json: Optional[str] = None,
175
- otp_secret: Optional[str] = None, screenshot_config: Optional[Dict[str, Any]] = None):
175
+ otp_secret: Optional[str] = None, screenshot_config: Optional[Dict[str, Any]] = None, folder_path: str = None):
176
176
  llm = ChatOpenAI(
177
177
  model="gpt-4.1",
178
178
  api_key=os.getenv("OPENAI_API_KEY"),
179
179
  )
180
- folder_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), f'downloads_{session_id}')
181
180
 
182
181
  # Create folder if it doesn't exist
183
182
  os.makedirs(folder_path, exist_ok=True)
@@ -299,11 +298,12 @@ if __name__ == '__main__':
299
298
  output_schema = payload.get('outputSchema')
300
299
  otp_secret = payload.get('otpSecret')
301
300
  screenshot_config = payload.get('screenshotConfig')
301
+ folder_path = payload.get('folderPath')
302
302
 
303
- if not session_id or not cdp_url or not task:
304
- raise SystemExit("Missing required fields in JSON payload: sessionId, cdpUrl, task")
303
+ if not session_id or not cdp_url or not task or not folder_path:
304
+ raise SystemExit("Missing required fields in JSON payload: sessionId, cdpUrl, task, folderPath")
305
305
 
306
306
  output_schema_json = json.dumps(output_schema) if output_schema is not None else None
307
- asyncio.run(main(session_id, cdp_url, task, output_schema_json, otp_secret, screenshot_config))
307
+ asyncio.run(main(session_id, cdp_url, task, output_schema_json, otp_secret, screenshot_config, folder_path))
308
308
  else:
309
309
  raise SystemExit("Usage: uv run executeBrowserTask.py; send a JSON payload via stdin")
@@ -2,6 +2,7 @@ import { spawn } from 'node:child_process';
2
2
  import { logger } from '../utils/logger';
3
3
  import { mindedConnection } from '../platform/mindedConnection';
4
4
  import path from 'path';
5
+ import { existsSync, readdirSync } from 'fs';
5
6
  import {
6
7
  mindedConnectionSocketMessageType,
7
8
  CreateBrowserSessionResponse,
@@ -120,6 +121,9 @@ export const invokeBrowserTask = async (options: InvokeBrowserTaskOptions): Prom
120
121
  validateLocalOperatorSetup();
121
122
  const pythonScriptPath = path.resolve(__dirname, 'executeBrowserTask.py');
122
123
 
124
+ // Calculate folder path for downloads
125
+ const folderPath = path.join(__dirname, `downloads_${sessionId}`);
126
+
123
127
  const args = ['run', pythonScriptPath];
124
128
  logger.info({
125
129
  message: 'Spawning Python process',
@@ -146,6 +150,7 @@ export const invokeBrowserTask = async (options: InvokeBrowserTaskOptions): Prom
146
150
  outputSchema,
147
151
  otpSecret: process.env.TOTP_SECRET,
148
152
  screenshotConfig,
153
+ folderPath,
149
154
  };
150
155
 
151
156
  // Write JSON payload to stdin
@@ -167,14 +172,11 @@ export const invokeBrowserTask = async (options: InvokeBrowserTaskOptions): Prom
167
172
  process.stderr.write(text);
168
173
  });
169
174
 
170
- let wasKilledByUs = false;
171
-
172
175
  const interval = setInterval(() => {
173
176
  // Check if chromium process stopped running
174
177
  if (!isLocalBrowserRunning()) {
175
178
  logger.error({ message: 'Local browser process stopped running, killing browser task' });
176
- wasKilledByUs = true;
177
- child.kill('SIGTERM');
179
+ child.kill();
178
180
  clearInterval(interval);
179
181
  }
180
182
  }, 1000);
@@ -191,11 +193,6 @@ export const invokeBrowserTask = async (options: InvokeBrowserTaskOptions): Prom
191
193
 
192
194
  clearInterval(interval);
193
195
 
194
- if (wasKilledByUs) {
195
- logger.error({ message: 'Browser task was killed by user', stderr: stderrBuffer });
196
- throw new Error('Task was stopped by user');
197
- }
198
-
199
196
  if (exitCode !== 0) {
200
197
  logger.error({ message: 'Operator failed', exitCode, stderr: stderrBuffer });
201
198
  throw new Error(`Local browser task failed with exit code ${exitCode}`);
@@ -212,10 +209,23 @@ export const invokeBrowserTask = async (options: InvokeBrowserTaskOptions): Prom
212
209
  }
213
210
  }
214
211
 
212
+ // Read downloaded files from the folder
213
+ let downloadedFiles: string[] = [];
214
+ if (existsSync(folderPath)) {
215
+ try {
216
+ const files = readdirSync(folderPath);
217
+ downloadedFiles = files.map((file) => path.join(folderPath, file));
218
+ logger.debug({ message: 'Found downloaded files', count: downloadedFiles.length, files: downloadedFiles });
219
+ } catch (error) {
220
+ logger.error({ message: 'Failed to read downloads folder', error });
221
+ }
222
+ }
223
+
215
224
  return {
216
225
  result,
217
226
  steps: [],
218
227
  recordings: [],
228
+ downloadedFiles,
219
229
  };
220
230
  }
221
231
 
@@ -107,9 +107,12 @@ export const createPromptRouter = ({
107
107
  const promptTemplate = includeReasoning ? ROUTER_PROMPT : ROUTER_PROMPT_WITHOUT_REASONING;
108
108
 
109
109
  // Compile prompt with EJS and parameters
110
- const routerPrompt = compilePrompt(promptTemplate, state, {
110
+ const routerPrompt = compilePrompt(promptTemplate, {
111
111
  steps: stepsStr,
112
112
  messages: messagesStr,
113
+ memory: state.memory,
114
+ state: state,
115
+ env: process.env,
113
116
  });
114
117
 
115
118
  // Define response schema
@@ -47,7 +47,7 @@ const appActionRunnerToolCreator = (schema: ActionInputParam[], nodeTitle: strin
47
47
  name: nodeTitle,
48
48
  description,
49
49
  input: zodSchema,
50
- execute: async ({ input }: ToolExecuteInput<typeof zodSchema>) => {
50
+ execute: async ({ input, state }: ToolExecuteInput<typeof zodSchema>) => {
51
51
  const response = await mindedConnection.awaitEmit(
52
52
  mindedConnectionSocketMessageType.OnAppAction,
53
53
  {
@@ -57,6 +57,7 @@ const appActionRunnerToolCreator = (schema: ActionInputParam[], nodeTitle: strin
57
57
  20000,
58
58
  );
59
59
  return {
60
+ state,
60
61
  result: response as { result?: any },
61
62
  };
62
63
  },
@@ -39,12 +39,13 @@ export const addAppToolNode = async ({
39
39
 
40
40
  // Compile parameters with variable injection support
41
41
  const compiledParameters: Record<string, any> = {};
42
+ const compileContext = { state, memory: state.memory, env: process.env };
42
43
 
43
44
  for (const [key, value] of Object.entries(node.parameters || {})) {
44
45
  if (value !== '') {
45
46
  // If the value is a string, compile it to allow variable injection
46
47
  if (typeof value === 'string') {
47
- compiledParameters[key] = compilePrompt(value, state);
48
+ compiledParameters[key] = compilePrompt(value, compileContext);
48
49
  } else {
49
50
  compiledParameters[key] = value;
50
51
  }
@@ -69,7 +70,7 @@ export const addAppToolNode = async ({
69
70
  const combinedPlaybooks = combinePlaybooks(agent.playbooks) || '';
70
71
 
71
72
  // Compile the prompt if it exists to allow variable injection
72
- const compiledNodePrompt = node.prompt ? compilePrompt(node.prompt, state) : null;
73
+ const compiledNodePrompt = node.prompt ? compilePrompt(node.prompt, compileContext) : null;
73
74
 
74
75
  // Check if any compiled parameter is too long (>1000 characters)
75
76
  const hasLongParameters = Object.values(compiledParameters).some((value) => typeof value === 'string' && value.length > 1000);
@@ -106,7 +107,7 @@ export const addAppToolNode = async ({
106
107
  User instructions for choosing tool parameters are:
107
108
  ${compiledNodePrompt ? compiledNodePrompt : 'no instructions set by the user'}`;
108
109
 
109
- const compiledPrompt = compilePrompt(message, state);
110
+ const compiledPrompt = compilePrompt(message, { state: state, memory: state.memory, env: process.env });
110
111
  const systemMessage = new SystemMessage(compiledPrompt);
111
112
  if (state.messages.length === 0 || state.messages[0].getType() === 'system') {
112
113
  state.messages[0] = systemMessage;
@@ -39,7 +39,7 @@ export const addBrowserTaskNode = async ({ graph, node, agent, llm }: AddBrowser
39
39
 
40
40
  const combinedPlaybooks = combinePlaybooks(agent.playbooks);
41
41
  if (combinedPlaybooks) {
42
- const compiledPrompt = compilePrompt(combinedPlaybooks, state);
42
+ const compiledPrompt = compilePrompt(combinedPlaybooks, { state: state, memory: state.memory });
43
43
  const systemMessage = new SystemMessage(compiledPrompt);
44
44
  if (state.messages.length === 0 || state.messages[0].getType() === 'system') {
45
45
  state.messages[0] = systemMessage;
@@ -59,8 +59,16 @@ export const addBrowserTaskNode = async ({ graph, node, agent, llm }: AddBrowser
59
59
  const toolCall = AIToolCallMessage.tool_calls[0];
60
60
  const inputParams = toolCall.args || {};
61
61
 
62
+ // Prepare parameters for prompt compilation
63
+ const promptParams = {
64
+ input: inputParams,
65
+ memory: state.memory,
66
+ state: state,
67
+ system: { currentTime: new Date().toISOString() },
68
+ };
69
+
62
70
  // Compile the prompt with parameters
63
- const compiledPrompt = compilePrompt(node.prompt, state, { input: inputParams });
71
+ const compiledPrompt = compilePrompt(node.prompt, { ...promptParams });
64
72
 
65
73
  // Build the full prompt with compiled content
66
74
  const fullPrompt = `
@@ -53,7 +53,7 @@ export const addBrowserTaskRunNode = async ({ graph, browserTaskNode, attachedTo
53
53
  }));
54
54
 
55
55
  // We compile the env variables to avoid having to pass them to the platform in the tool input
56
- const promptCompiledWithEnv = compilePrompt(prompt, state);
56
+ const promptCompiledWithEnv = compilePrompt(prompt, { env: process.env });
57
57
 
58
58
  const { browserTaskMode } = getConfig();
59
59
 
@@ -111,6 +111,7 @@ export const addBrowserTaskRunNode = async ({ graph, browserTaskNode, attachedTo
111
111
  hasResult: !!result.result,
112
112
  stepCount: result.steps?.length || 0,
113
113
  recordingCount: result.recordings?.length || 0,
114
+ downloadedFilesCount: result.downloadedFiles?.length || 0,
114
115
  });
115
116
 
116
117
  // Create tool message with the result
@@ -121,6 +122,7 @@ export const addBrowserTaskRunNode = async ({ graph, browserTaskNode, attachedTo
121
122
  steps: result.steps || [],
122
123
  recordings: result.recordings || [],
123
124
  inputParams: inputParams,
125
+ downloadedFiles: result.downloadedFiles || [],
124
126
  }),
125
127
  name: 'browser-task',
126
128
  tool_call_id: toolCall.id,
@@ -133,6 +135,7 @@ export const addBrowserTaskRunNode = async ({ graph, browserTaskNode, attachedTo
133
135
  steps: result.steps,
134
136
  recordings: result.recordings,
135
137
  inputParams: inputParams,
138
+ downloadedFiles: result.downloadedFiles || [],
136
139
  },
137
140
  },
138
141
  });
@@ -149,6 +152,7 @@ export const addBrowserTaskRunNode = async ({ graph, browserTaskNode, attachedTo
149
152
  nodeDisplayName: browserTaskNode.displayName,
150
153
  steps: result.steps,
151
154
  recordings: result.recordings,
155
+ downloadedFiles: result.downloadedFiles || [],
152
156
  status: 'completed',
153
157
  },
154
158
  update: true, // This triggers the message reducer to update the existing message
@@ -51,7 +51,7 @@ export const addPromptNode = async ({ graph, node, llm, tools, emit, agent }: Ad
51
51
  if (combinedPlaybooks) {
52
52
  finalMessage = combinedPlaybooks + '\n\n' + currentPromptNode;
53
53
  }
54
- const compiledPrompt = compilePrompt(finalMessage, state);
54
+ const compiledPrompt = compilePrompt(finalMessage, { state: state, memory: state.memory, env: process.env });
55
55
  const systemMessage = new SystemMessage(compiledPrompt);
56
56
  if (state.messages.length === 0 || state.messages[0].getType() === 'system') {
57
57
  state.messages[0] = systemMessage;
@@ -52,7 +52,7 @@ export const addToolNode = async ({
52
52
  }
53
53
  }
54
54
  if (finalMessage) {
55
- const compiledPrompt = compilePrompt(finalMessage, state);
55
+ const compiledPrompt = compilePrompt(finalMessage, { state: state, memory: state.memory, env: process.env });
56
56
  const systemMessage = new SystemMessage(compiledPrompt);
57
57
  if (state.messages.length === 0 || state.messages[0].getType() === 'system') {
58
58
  state.messages[0] = systemMessage;
@@ -1,16 +1,13 @@
1
1
  import * as ejs from 'ejs';
2
2
  import { logger } from '../utils/logger';
3
- import { stateAnnotation } from '../types/LangGraph.types';
3
+
4
4
  /**
5
5
  * Compile prompt with parameters using EJS and placeholder replacement
6
6
  */
7
- export function compilePrompt(prompt: string, state: typeof stateAnnotation.State, params: Record<string, any> = {}): string {
7
+ export function compilePrompt(prompt: string, params: Record<string, any> = {}): string {
8
8
  try {
9
- // Always-included params
9
+ // Define system parameters
10
10
  params.system = { currentTime: new Date().toISOString() };
11
- params.env = process.env;
12
- params.state = state;
13
- params.memory = state.memory;
14
11
 
15
12
  // First, render with EJS
16
13
  let compiledPrompt = ejs.render(prompt, params);
@@ -15,11 +15,17 @@ export async function executeRpaStep(
15
15
  state: typeof stateAnnotation.State,
16
16
  llm: (typeof LLMProviders)[keyof typeof LLMProviders],
17
17
  ): Promise<any> {
18
+ // Create params object with memory and secrets
19
+ const params = {
20
+ env: process.env,
21
+ state: state,
22
+ };
23
+
18
24
  switch (step.type) {
19
25
  case RpaActionType.CLICK: {
20
26
  // Compile selectors in case they contain placeholders
21
- const compiledXpath = step.xpath ? compilePrompt(step.xpath, state) : undefined;
22
- const compiledSelector = step.selector ? compilePrompt(step.selector, state) : undefined;
27
+ const compiledXpath = step.xpath ? compilePrompt(step.xpath, params) : undefined;
28
+ const compiledSelector = step.selector ? compilePrompt(step.selector, params) : undefined;
23
29
 
24
30
  if (compiledXpath) {
25
31
  await page.locator(`xpath=${compiledXpath}`).click({ timeout: 15000 });
@@ -31,9 +37,9 @@ export async function executeRpaStep(
31
37
 
32
38
  case RpaActionType.TYPE: {
33
39
  // Compile text and selectors
34
- const compiledText = step.text ? compilePrompt(step.text, state) : '';
35
- const compiledTypeXpath = step.xpath ? compilePrompt(step.xpath, state) : undefined;
36
- const compiledTypeSelector = step.selector ? compilePrompt(step.selector, state) : undefined;
40
+ const compiledText = step.text ? compilePrompt(step.text, params) : '';
41
+ const compiledTypeXpath = step.xpath ? compilePrompt(step.xpath, params) : undefined;
42
+ const compiledTypeSelector = step.selector ? compilePrompt(step.selector, params) : undefined;
37
43
 
38
44
  if (step.shouldReplaceExistingText) {
39
45
  if (compiledTypeXpath) {
@@ -57,23 +63,23 @@ export async function executeRpaStep(
57
63
 
58
64
  case RpaActionType.GOTO: {
59
65
  // Compile URL
60
- const compiledUrl = step.url ? compilePrompt(step.url, state) : '';
66
+ const compiledUrl = step.url ? compilePrompt(step.url, params) : '';
61
67
  await page.goto(compiledUrl, { waitUntil: 'load', timeout: 15000 });
62
68
  return { action: 'navigated', url: compiledUrl };
63
69
  }
64
70
 
65
71
  case RpaActionType.PRESS: {
66
72
  // Compile key
67
- const compiledKey = step.key ? compilePrompt(step.key, state) : 'Enter';
73
+ const compiledKey = step.key ? compilePrompt(step.key, params) : 'Enter';
68
74
  await page.keyboard.press(compiledKey);
69
75
  return { action: 'pressed', key: compiledKey };
70
76
  }
71
77
 
72
78
  case RpaActionType.SELECT: {
73
79
  // Compile value and selectors
74
- const compiledValue = step.value ? compilePrompt(step.value, state) : '';
75
- const compiledSelectXpath = step.xpath ? compilePrompt(step.xpath, state) : undefined;
76
- const compiledSelectSelector = step.selector ? compilePrompt(step.selector, state) : undefined;
80
+ const compiledValue = step.value ? compilePrompt(step.value, params) : '';
81
+ const compiledSelectXpath = step.xpath ? compilePrompt(step.xpath, params) : undefined;
82
+ const compiledSelectSelector = step.selector ? compilePrompt(step.selector, params) : undefined;
77
83
 
78
84
  if (compiledSelectXpath) {
79
85
  await page.locator(`xpath=${compiledSelectXpath}`).selectOption(compiledValue, { timeout: 15000 });
@@ -86,7 +92,7 @@ export async function executeRpaStep(
86
92
  case RpaActionType.SCREENSHOT: {
87
93
  const screenshot = await page.screenshot({ type: 'png' });
88
94
  // Compile description if provided
89
- const compiledDescription = step.description ? compilePrompt(step.description, state) : undefined;
95
+ const compiledDescription = step.description ? compilePrompt(step.description, params) : undefined;
90
96
  return {
91
97
  action: 'screenshot',
92
98
  description: compiledDescription,
@@ -471,6 +471,7 @@ export interface InvokeBrowserTaskResponse extends BaseSdkConnectionSocketMessag
471
471
  result?: any;
472
472
  steps?: any[];
473
473
  recordings?: any[];
474
+ downloadedFiles?: string[];
474
475
  }
475
476
 
476
477
  export interface ExecuteToolRequest extends BasemindedConnectionSocketMessage {