@minded-ai/mindedjs 2.0.9-beta.3 → 2.0.10

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 (85) hide show
  1. package/dist/agent.js +1 -1
  2. package/dist/agent.js.map +1 -1
  3. package/dist/browserTask/executeBrowserTask.d.ts.map +1 -1
  4. package/dist/browserTask/executeBrowserTask.js +47 -18
  5. package/dist/browserTask/executeBrowserTask.js.map +1 -1
  6. package/dist/browserTask/executeBrowserTask.py +76 -7
  7. package/dist/cli/index.js +0 -0
  8. package/dist/edges/createPromptRouter.d.ts.map +1 -1
  9. package/dist/edges/createPromptRouter.js +2 -0
  10. package/dist/edges/createPromptRouter.js.map +1 -1
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +2 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/nodes/addAppToolNode.js +1 -1
  16. package/dist/nodes/addAppToolNode.js.map +1 -1
  17. package/dist/nodes/addBrowserTaskNode.d.ts.map +1 -1
  18. package/dist/nodes/addBrowserTaskNode.js +6 -26
  19. package/dist/nodes/addBrowserTaskNode.js.map +1 -1
  20. package/dist/nodes/addPromptNode.js +1 -1
  21. package/dist/nodes/addPromptNode.js.map +1 -1
  22. package/dist/nodes/addRpaNode.d.ts +18 -0
  23. package/dist/nodes/addRpaNode.d.ts.map +1 -0
  24. package/dist/nodes/addRpaNode.js +162 -0
  25. package/dist/nodes/addRpaNode.js.map +1 -0
  26. package/dist/nodes/addToolNode.js +1 -1
  27. package/dist/nodes/addToolNode.js.map +1 -1
  28. package/dist/nodes/nodeFactory.d.ts.map +1 -1
  29. package/dist/nodes/nodeFactory.js +4 -0
  30. package/dist/nodes/nodeFactory.js.map +1 -1
  31. package/dist/nodes/rpaStepsExecutor.d.ts +5 -0
  32. package/dist/nodes/rpaStepsExecutor.d.ts.map +1 -0
  33. package/dist/nodes/rpaStepsExecutor.js +156 -0
  34. package/dist/nodes/rpaStepsExecutor.js.map +1 -0
  35. package/dist/types/Flows.types.d.ts +41 -2
  36. package/dist/types/Flows.types.d.ts.map +1 -1
  37. package/dist/types/Flows.types.js +13 -1
  38. package/dist/types/Flows.types.js.map +1 -1
  39. package/dist/utils/schemaUtils.d.ts +15 -0
  40. package/dist/utils/schemaUtils.d.ts.map +1 -0
  41. package/dist/utils/schemaUtils.js +56 -0
  42. package/dist/utils/schemaUtils.js.map +1 -0
  43. package/package.json +2 -2
  44. package/src/agent.ts +1 -1
  45. package/src/browserTask/executeBrowserTask.py +76 -7
  46. package/src/browserTask/executeBrowserTask.ts +56 -16
  47. package/src/edges/createPromptRouter.ts +7 -5
  48. package/src/index.ts +3 -0
  49. package/src/nodes/addAppToolNode.ts +1 -1
  50. package/src/nodes/addBrowserTaskNode.ts +7 -30
  51. package/src/nodes/addPromptNode.ts +1 -1
  52. package/src/nodes/addRpaNode.ts +199 -0
  53. package/src/nodes/addToolNode.ts +1 -1
  54. package/src/nodes/nodeFactory.ts +4 -0
  55. package/src/nodes/rpaStepsExecutor.ts +175 -0
  56. package/src/types/Flows.types.ts +43 -1
  57. package/src/utils/schemaUtils.ts +68 -0
  58. package/dist/browserTask/cdp.d.ts +0 -23
  59. package/dist/browserTask/cdp.d.ts.map +0 -1
  60. package/dist/browserTask/cdp.js +0 -162
  61. package/dist/browserTask/cdp.js.map +0 -1
  62. package/dist/guidelines/guidelinesManager.d.ts +0 -37
  63. package/dist/guidelines/guidelinesManager.d.ts.map +0 -1
  64. package/dist/guidelines/guidelinesManager.js +0 -172
  65. package/dist/guidelines/guidelinesManager.js.map +0 -1
  66. package/dist/internalTools/retell.d.ts +0 -12
  67. package/dist/internalTools/retell.d.ts.map +0 -1
  68. package/dist/internalTools/retell.js +0 -54
  69. package/dist/internalTools/retell.js.map +0 -1
  70. package/dist/internalTools/sendPlaceholderMessage.d.ts +0 -14
  71. package/dist/internalTools/sendPlaceholderMessage.d.ts.map +0 -1
  72. package/dist/internalTools/sendPlaceholderMessage.js +0 -61
  73. package/dist/internalTools/sendPlaceholderMessage.js.map +0 -1
  74. package/dist/platform/mindedChatOpenAI.d.ts +0 -5
  75. package/dist/platform/mindedChatOpenAI.d.ts.map +0 -1
  76. package/dist/platform/mindedChatOpenAI.js +0 -23
  77. package/dist/platform/mindedChatOpenAI.js.map +0 -1
  78. package/dist/utils/extractStateMemoryResponse.d.ts +0 -5
  79. package/dist/utils/extractStateMemoryResponse.d.ts.map +0 -1
  80. package/dist/utils/extractStateMemoryResponse.js +0 -91
  81. package/dist/utils/extractStateMemoryResponse.js.map +0 -1
  82. package/dist/utils/extractToolMemoryResponse.d.ts +0 -4
  83. package/dist/utils/extractToolMemoryResponse.d.ts.map +0 -1
  84. package/dist/utils/extractToolMemoryResponse.js +0 -16
  85. package/dist/utils/extractToolMemoryResponse.js.map +0 -1
@@ -0,0 +1,15 @@
1
+ import { z } from 'zod';
2
+ export interface SchemaField {
3
+ name: string;
4
+ type: 'string' | 'number' | 'boolean' | 'array' | 'object';
5
+ description?: string;
6
+ required?: boolean;
7
+ }
8
+ /**
9
+ * Converts an array of schema field definitions to a Zod object schema
10
+ * @param fields Array of field definitions
11
+ * @param defaultSchema Optional default schema to use if no fields are provided
12
+ * @returns Zod object schema
13
+ */
14
+ export declare function createZodSchemaFromFields(fields?: SchemaField[], defaultSchema?: Record<string, z.ZodTypeAny>): z.ZodObject<Record<string, z.ZodTypeAny>>;
15
+ //# sourceMappingURL=schemaUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemaUtils.d.ts","sourceRoot":"","sources":["../../src/utils/schemaUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,CAAC,EAAE,WAAW,EAAE,EACtB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,CAAC,GAC3C,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAiD3C"}
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createZodSchemaFromFields = createZodSchemaFromFields;
4
+ const zod_1 = require("zod");
5
+ /**
6
+ * Converts an array of schema field definitions to a Zod object schema
7
+ * @param fields Array of field definitions
8
+ * @param defaultSchema Optional default schema to use if no fields are provided
9
+ * @returns Zod object schema
10
+ */
11
+ function createZodSchemaFromFields(fields, defaultSchema) {
12
+ const schemaFields = {};
13
+ if (fields && fields.length > 0) {
14
+ for (const field of fields) {
15
+ let fieldSchema;
16
+ // Create appropriate Zod type based on field type
17
+ switch (field.type) {
18
+ case 'string':
19
+ fieldSchema = zod_1.z.string();
20
+ break;
21
+ case 'number':
22
+ fieldSchema = zod_1.z.number();
23
+ break;
24
+ case 'boolean':
25
+ fieldSchema = zod_1.z.boolean();
26
+ break;
27
+ case 'array':
28
+ // For arrays, we'll default to array of any unless more specific
29
+ fieldSchema = zod_1.z.array(zod_1.z.any());
30
+ break;
31
+ case 'object':
32
+ // For objects, we'll default to record of any unless more specific
33
+ fieldSchema = zod_1.z.record(zod_1.z.any());
34
+ break;
35
+ default:
36
+ // Default to string for unknown types
37
+ fieldSchema = zod_1.z.string();
38
+ }
39
+ // Add description if available
40
+ if (field.description) {
41
+ fieldSchema = fieldSchema.describe(field.description);
42
+ }
43
+ // Handle optional fields
44
+ if (field.required === false) {
45
+ fieldSchema = fieldSchema.optional();
46
+ }
47
+ schemaFields[field.name] = fieldSchema;
48
+ }
49
+ }
50
+ else if (defaultSchema) {
51
+ // Use default schema if no fields provided
52
+ return zod_1.z.object(defaultSchema);
53
+ }
54
+ return zod_1.z.object(schemaFields);
55
+ }
56
+ //# sourceMappingURL=schemaUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemaUtils.js","sourceRoot":"","sources":["../../src/utils/schemaUtils.ts"],"names":[],"mappings":";;AAeA,8DAoDC;AAnED,6BAAwB;AASxB;;;;;GAKG;AACH,SAAgB,yBAAyB,CACvC,MAAsB,EACtB,aAA4C;IAE5C,MAAM,YAAY,GAAiC,EAAE,CAAC;IAEtD,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,WAAyB,CAAC;YAE9B,kDAAkD;YAClD,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;gBACnB,KAAK,QAAQ;oBACX,WAAW,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC;oBACzB,MAAM;gBACR,KAAK,QAAQ;oBACX,WAAW,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC;oBACzB,MAAM;gBACR,KAAK,SAAS;oBACZ,WAAW,GAAG,OAAC,CAAC,OAAO,EAAE,CAAC;oBAC1B,MAAM;gBACR,KAAK,OAAO;oBACV,iEAAiE;oBACjE,WAAW,GAAG,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,GAAG,EAAE,CAAC,CAAC;oBAC/B,MAAM;gBACR,KAAK,QAAQ;oBACX,mEAAmE;oBACnE,WAAW,GAAG,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,GAAG,EAAE,CAAC,CAAC;oBAChC,MAAM;gBACR;oBACE,sCAAsC;oBACtC,WAAW,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC;YAC7B,CAAC;YAED,+BAA+B;YAC/B,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;gBACtB,WAAW,GAAG,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YACxD,CAAC;YAED,yBAAyB;YACzB,IAAI,KAAK,CAAC,QAAQ,KAAK,KAAK,EAAE,CAAC;gBAC7B,WAAW,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC;YACvC,CAAC;YAED,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC;QACzC,CAAC;IACH,CAAC;SAAM,IAAI,aAAa,EAAE,CAAC;QACzB,2CAA2C;QAC3C,OAAO,OAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,OAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;AAChC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minded-ai/mindedjs",
3
- "version": "2.0.9-beta.3",
3
+ "version": "2.0.10",
4
4
  "description": "MindedJS is a TypeScript library for building agents.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -70,4 +70,4 @@
70
70
  "peerDependencies": {
71
71
  "playwright": "^1.55.0"
72
72
  }
73
- }
73
+ }
package/src/agent.ts CHANGED
@@ -465,7 +465,7 @@ export class Agent {
465
465
  // Add playbooks to messages
466
466
  const combinedPlaybooks = combinePlaybooks(this.playbooks);
467
467
  if (combinedPlaybooks) {
468
- const compiledPrompt = compilePrompt(combinedPlaybooks, { memory: state.memory });
468
+ const compiledPrompt = compilePrompt(combinedPlaybooks, { state: state, memory: state.memory, env: process.env });
469
469
  const systemMessage = new SystemMessage(compiledPrompt);
470
470
  if (state.messages.length === 0 || state.messages[0].getType() === 'system') {
471
471
  state.messages[0] = systemMessage;
@@ -1,5 +1,9 @@
1
1
  import asyncio
2
- from browser_use import Agent, BrowserSession, BrowserProfile
2
+ import json
3
+ from typing import List, Optional, Dict, Any, TypedDict
4
+ from pydantic import BaseModel, create_model, Field
5
+ from browser_use import Agent, Controller
6
+ from browser_use.browser import BrowserProfile, BrowserSession
3
7
  from browser_use.llm import ChatOpenAI
4
8
  import os
5
9
  import sys
@@ -7,7 +11,39 @@ from dotenv import load_dotenv
7
11
 
8
12
  load_dotenv()
9
13
 
10
- async def main(session_id: str, cdp_url: str, task: str):
14
+ class OutputSchemaItemRequired(TypedDict):
15
+ name: str
16
+ type: str # 'string' | 'number'
17
+
18
+ class OutputSchemaItem(OutputSchemaItemRequired, total=False):
19
+ description: str
20
+ required: bool
21
+
22
+
23
+ def create_pydantic_model_from_schema(output_schema: Optional[List[Dict[str, Any]]]) -> Optional[type[BaseModel]]:
24
+ if not output_schema:
25
+ return None
26
+
27
+ field_definitions: Dict[str, Any] = {}
28
+ for item in output_schema:
29
+ type_mapping = {
30
+ 'string': str,
31
+ 'number': float,
32
+ }
33
+ field_type = type_mapping.get(item.get('type'), str)
34
+ description = item.get('description', '')
35
+ is_required = item.get('required', True)
36
+ if is_required:
37
+ field_definitions[item['name']] = (field_type, Field(description=description))
38
+ else:
39
+ field_definitions[item['name']] = (Optional[field_type], Field(default=None, description=description))
40
+
41
+ if field_definitions:
42
+ return create_model('DynamicOutputModel', **field_definitions)
43
+ return None
44
+
45
+
46
+ async def main(session_id: str, cdp_url: str, task: str, output_schema_json: Optional[str] = None):
11
47
  llm = ChatOpenAI(
12
48
  model="gpt-4.1",
13
49
  api_key=os.getenv("OPENAI_API_KEY"),
@@ -24,19 +60,52 @@ async def main(session_id: str, cdp_url: str, task: str):
24
60
  browser_profile=BrowserProfile(downloads_path=folder_path),
25
61
  cdp_url=cdp_url
26
62
  )
63
+
64
+ output_schema = None
65
+ if output_schema_json:
66
+ try:
67
+ output_schema = json.loads(output_schema_json)
68
+ except Exception:
69
+ output_schema = None
70
+
71
+ output_model = create_pydantic_model_from_schema(output_schema)
72
+
27
73
  agent = Agent(
28
74
  task=task,
29
75
  llm=llm,
76
+ controller=Controller(output_model=output_model) if output_model is not None else Controller(),
30
77
  available_file_paths=available_files,
31
- browser=browser_session,
78
+ browser_session=browser_session,
32
79
  )
33
80
  history = await agent.run()
34
- print(history.final_result())
81
+ final_result = history.final_result()
82
+
83
+ # Parse result with output model if provided
84
+ parsed_result = None
85
+ if output_model and final_result:
86
+ try:
87
+ # Validate and parse the result using the Pydantic model
88
+ parsed_result = output_model.model_validate_json(final_result)
89
+ # Convert to dict for JSON serialization
90
+ parsed_result = parsed_result.model_dump()
91
+ except Exception as e:
92
+ print(f"Failed to parse result with output model: {e}")
93
+ # Fall back to raw result
94
+ parsed_result = final_result
95
+ else:
96
+ parsed_result = final_result
97
+
98
+ print("___RESULT___")
99
+ if parsed_result:
100
+ print(json.dumps(parsed_result, indent=2))
101
+ else:
102
+ print(final_result)
35
103
 
36
104
  if __name__ == '__main__':
37
- if len(sys.argv) < 3:
38
- raise SystemExit("Usage: uv run uploadG2n.py <session_id> <cdp_url> <task>")
105
+ if len(sys.argv) < 4:
106
+ raise SystemExit("Usage: uv run executeBrowserTask.py <session_id> <cdp_url> <task> [output_schema_json]")
39
107
  session_id = sys.argv[1]
40
108
  cdp_url = sys.argv[2]
41
109
  task = sys.argv[3]
42
- asyncio.run(main(session_id, cdp_url, task))
110
+ output_schema_json = sys.argv[4] if len(sys.argv) > 4 else None
111
+ asyncio.run(main(session_id, cdp_url, task, output_schema_json))
@@ -1,4 +1,4 @@
1
- import { exec as execCb } from 'node:child_process';
1
+ 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';
@@ -11,9 +11,7 @@ import {
11
11
  DestroyBrowserSessionRequest,
12
12
  DestroyBrowserSessionResponse,
13
13
  } from '../platform/mindedConnectionTypes';
14
- import { promisify } from 'node:util';
15
14
  import { kill, getOrStartLocalCDP } from './localBrowserTask';
16
- const exec = promisify(execCb);
17
15
 
18
16
  // Socket-based browser task functions
19
17
  export const createBrowserSession = async ({ sessionId, proxy, onPrem, localRun }: { sessionId: string, proxy?: string, onPrem?: boolean, localRun?: boolean }): Promise<CreateBrowserSessionResponse> => {
@@ -96,30 +94,72 @@ export const invokeBrowserTask = async (
96
94
  required?: boolean;
97
95
  }[],
98
96
  ): Promise<InvokeBrowserTaskResponse> => {
99
- logger.debug({
100
- msg: 'Invoking browser task via socket',
101
- sessionId,
102
- taskLength: task.length,
103
- keepAlive,
104
- hooksCount: hooks?.length || 0,
105
- onPrem,
106
- outputSchemaFields: outputSchema?.length || 0,
107
- });
108
97
 
109
98
  try {
110
99
 
111
100
  if (localRun) {
112
101
  const pythonScriptPath = path.resolve(__dirname, 'executeBrowserTask.py');
113
- const command = `uv run ${pythonScriptPath} "${sessionId}" "${cdpUrl}" "${task.replace(/"/g, '\\"')}"`;
114
- const { stdout, stderr } = await exec(command);
115
- logger.info({ message: 'Operator finished', stdout, stderr });
102
+ const outputSchemaArg = JSON.stringify(outputSchema || []);
103
+
104
+ const args = ['run', pythonScriptPath, sessionId, cdpUrl, task, outputSchemaArg];
105
+
106
+ logger.info({ message: 'Spawning Python process', args });
107
+
108
+ const child = spawn('uv', args, { stdio: ['ignore', 'pipe', 'pipe'] });
109
+
110
+ let stdoutBuffer = '';
111
+ let stderrBuffer = '';
112
+
113
+ child.stdout.on('data', (data) => {
114
+ const text = data.toString();
115
+ stdoutBuffer += text;
116
+ process.stdout.write(text);
117
+ });
118
+
119
+ child.stderr.on('data', (data) => {
120
+ const text = data.toString();
121
+ stderrBuffer += text;
122
+ process.stderr.write(text);
123
+ });
124
+
125
+ const exitCode: number = await new Promise((resolve, reject) => {
126
+ child.on('error', (err) => reject(err));
127
+ child.on('close', (code) => resolve(code ?? 1));
128
+ });
129
+
130
+ if (exitCode !== 0) {
131
+ logger.error({ message: 'Operator failed', exitCode, stderr: stderrBuffer });
132
+ throw new Error(`Local browser task failed with exit code ${exitCode}`);
133
+ }
134
+
135
+ logger.info({ message: 'Operator finished' });
136
+
137
+ let result = stdoutBuffer.split('___RESULT___')[1]?.trim() || stdoutBuffer;
138
+ if (outputSchema?.length) {
139
+ try {
140
+ result = JSON.parse(result);
141
+ } catch (error) {
142
+ logger.debug({ message: 'Failed to parse result', error });
143
+ }
144
+ }
145
+
116
146
  return {
117
- result: stdout,
147
+ result,
118
148
  steps: [],
119
149
  recordings: [],
120
150
  };
121
151
  }
122
152
 
153
+ logger.debug({
154
+ msg: 'Invoking browser task via socket',
155
+ sessionId,
156
+ taskLength: task.length,
157
+ keepAlive,
158
+ hooksCount: hooks?.length || 0,
159
+ onPrem,
160
+ outputSchemaFields: outputSchema?.length || 0,
161
+ });
162
+
123
163
  const response = await mindedConnection.awaitEmit<InvokeBrowserTaskRequest, InvokeBrowserTaskResponse>(
124
164
  mindedConnectionSocketMessageType.INVOKE_BROWSER_TASK,
125
165
  {
@@ -111,17 +111,19 @@ export const createPromptRouter = ({
111
111
  steps: stepsStr,
112
112
  messages: messagesStr,
113
113
  memory: state.memory,
114
+ state: state,
115
+ env: process.env,
114
116
  });
115
117
 
116
118
  // Define response schema
117
119
  const responseSchema = includeReasoning
118
120
  ? z.object({
119
- nextNodeId: z.string(),
120
- reasoning: z.string(),
121
- })
121
+ nextNodeId: z.string(),
122
+ reasoning: z.string(),
123
+ })
122
124
  : z.object({
123
- nextNodeId: z.string(),
124
- });
125
+ nextNodeId: z.string(),
126
+ });
125
127
 
126
128
  let attempts = 0;
127
129
  let lastError: Error | null = null;
package/src/index.ts CHANGED
@@ -48,6 +48,8 @@ export type {
48
48
  VoiceTriggerNode,
49
49
  WebhookTriggerNode,
50
50
  InterfaceTriggerNode,
51
+ RpaNode,
52
+ RpaStep,
51
53
  } from './types/Flows.types';
52
54
  export {
53
55
  NodeType,
@@ -57,6 +59,7 @@ export {
57
59
  AppNodeMetadataType,
58
60
  NodeMetadata,
59
61
  KnownTriggerNames,
62
+ RpaActionType,
60
63
  } from './types/Flows.types';
61
64
  export type { Tool, ToolExecuteInput } from './types/Tools.types';
62
65
 
@@ -70,7 +70,7 @@ export const addAppToolNode = async ({
70
70
  User instructions for choosing tool parameters are:
71
71
  ${node.prompt ? `${node.prompt}` : 'no instructions set by the user'}`;
72
72
 
73
- const compiledPrompt = compilePrompt(message, { memory: state.memory });
73
+ const compiledPrompt = compilePrompt(message, { state: state, memory: state.memory, env: process.env });
74
74
  const systemMessage = new SystemMessage(compiledPrompt);
75
75
  if (state.messages.length === 0 || state.messages[0].getType() === 'system') {
76
76
  state.messages[0] = systemMessage;
@@ -9,10 +9,10 @@ import { v4 as uuidv4 } from 'uuid';
9
9
  import { Agent } from '../agent';
10
10
  import { createBrowserSession } from '../browserTask/executeBrowserTask';
11
11
  import { tool as langchainTool } from '@langchain/core/tools';
12
- import { z } from 'zod';
13
12
  import { LLMProviders } from '../types/LLM.types';
14
13
  import { compilePrompt } from './compilePrompt';
15
14
  import { combinePlaybooks } from '../playbooks/playbooks';
15
+ import { createZodSchemaFromFields } from '../utils/schemaUtils';
16
16
 
17
17
  type AddBrowserTaskNodeParams = {
18
18
  graph: PreCompiledGraph;
@@ -26,35 +26,10 @@ export const addBrowserTaskNode = async ({ graph, node, agent, llm }: AddBrowser
26
26
  logger.info({ msg: `Executing browser task node ${node.displayName}`, prompt: node.prompt });
27
27
  await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId);
28
28
  // Create Zod schema from inputSchema
29
- const schemaFields: Record<string, z.ZodTypeAny> = {};
30
- if (node.inputSchema) {
31
- for (const field of node.inputSchema) {
32
- let fieldSchema: z.ZodTypeAny;
33
-
34
- if (field.type === 'string') {
35
- fieldSchema = z.string();
36
- } else if (field.type === 'number') {
37
- fieldSchema = z.number();
38
- } else {
39
- fieldSchema = z.string(); // Default to string
40
- }
41
-
42
- if (field.description) {
43
- fieldSchema = fieldSchema.describe(field.description);
44
- }
45
-
46
- if (field.required === false) {
47
- fieldSchema = fieldSchema.optional();
48
- }
49
-
50
- schemaFields[field.name] = fieldSchema;
51
- }
52
- }
53
-
54
- const zodSchema = z.object(schemaFields);
29
+ const zodSchema = createZodSchemaFromFields(node.inputSchema);
55
30
 
56
31
  // Create langchain tool
57
- const tool = langchainTool(() => { }, {
32
+ const tool = langchainTool(() => {}, {
58
33
  name: 'browser-task',
59
34
  description: node.prompt,
60
35
  schema: zodSchema,
@@ -62,7 +37,7 @@ export const addBrowserTaskNode = async ({ graph, node, agent, llm }: AddBrowser
62
37
 
63
38
  const combinedPlaybooks = combinePlaybooks(agent.playbooks);
64
39
  if (combinedPlaybooks) {
65
- const compiledPrompt = compilePrompt(combinedPlaybooks, { memory: state.memory });
40
+ const compiledPrompt = compilePrompt(combinedPlaybooks, { state: state, memory: state.memory, env: process.env });
66
41
  const systemMessage = new SystemMessage(compiledPrompt);
67
42
  if (state.messages.length === 0 || state.messages[0].getType() === 'system') {
68
43
  state.messages[0] = systemMessage;
@@ -86,11 +61,12 @@ export const addBrowserTaskNode = async ({ graph, node, agent, llm }: AddBrowser
86
61
  const promptParams = {
87
62
  input: inputParams,
88
63
  memory: state.memory,
64
+ state: state,
89
65
  system: { currentTime: new Date().toISOString() },
90
66
  };
91
67
 
92
68
  // Compile the prompt with parameters
93
- const compiledPrompt = compilePrompt(node.prompt, promptParams);
69
+ const compiledPrompt = compilePrompt(node.prompt, { ...promptParams, env: process.env });
94
70
 
95
71
  // Build the full prompt with compiled content
96
72
  const fullPrompt = `
@@ -145,6 +121,7 @@ ${Object.keys(inputParams).length > 0 ? `# Input parameters:\n${JSON.stringify(i
145
121
  keepAlive,
146
122
  hooks: node.hooks || [],
147
123
  onPrem: node.onPrem,
124
+ localRun: node.localRun,
148
125
  outputSchema: node.outputSchema,
149
126
  },
150
127
  },
@@ -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, { memory: state.memory });
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;
@@ -0,0 +1,199 @@
1
+ import { RunnableLike } from '@langchain/core/runnables';
2
+ import { NodeType, RpaNode, RpaActionType } from '../types/Flows.types';
3
+ import { PreCompiledGraph, stateAnnotation } from '../types/LangGraph.types';
4
+ import { Tool } from '../types/Tools.types';
5
+ import { AgentEventRequestPayloads } from '../events/AgentEvents';
6
+ import { EmitSignature, HistoryStep } from '../types/Agent.types';
7
+ import { Agent } from '../agent';
8
+ import { logger } from '../utils/logger';
9
+ import { createHistoryStep } from '../utils/history';
10
+ import { chromium, Browser, Page } from 'playwright';
11
+ import { LLMProviders } from '../types/LLM.types';
12
+ import { AIMessage, ToolMessage } from '@langchain/core/messages';
13
+ import { v4 as uuidv4 } from 'uuid';
14
+ import { executeRpaStep } from './rpaStepsExecutor';
15
+
16
+ type AddRpaNodeParams = {
17
+ graph: PreCompiledGraph;
18
+ node: RpaNode;
19
+ tools: Tool<any, any>[];
20
+ emit: EmitSignature<any, keyof AgentEventRequestPayloads<any>>;
21
+ agent: Agent;
22
+ llm: (typeof LLMProviders)[keyof typeof LLMProviders];
23
+ };
24
+
25
+ export const addRpaNode = async ({ graph, node, agent, llm }: AddRpaNodeParams) => {
26
+ const callback: RunnableLike = async (state: typeof stateAnnotation.State) => {
27
+ await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId);
28
+ logger.info({ msg: `[Node] Executing RPA node`, node: node.displayName, sessionId: state.sessionId });
29
+
30
+ let browser: Browser | null = null;
31
+ let page: Page | null = null;
32
+
33
+ // Create tool call for RPA execution
34
+ const toolCallId = uuidv4();
35
+ const aiMessageId = uuidv4();
36
+
37
+ // Get CDP URL from state
38
+ const cdpUrl = state.cdpUrl;
39
+
40
+ const toolCall = {
41
+ id: toolCallId,
42
+ name: 'rpa-task',
43
+ args: {
44
+ steps: node.steps,
45
+ },
46
+ };
47
+
48
+ // Create AI message with tool call
49
+ const aiMessage = new AIMessage({
50
+ id: aiMessageId,
51
+ content: '',
52
+ tool_calls: [toolCall],
53
+ additional_kwargs: {
54
+ mindedMetadata: {
55
+ nodeType: NodeType.RPA,
56
+ nodeDisplayName: node.displayName,
57
+ sessionId: state.sessionId,
58
+ cdpUrl: cdpUrl || undefined,
59
+ },
60
+ },
61
+ });
62
+
63
+ state.messages.push(aiMessage);
64
+
65
+ try {
66
+ if (!cdpUrl) {
67
+ throw new Error('CDP URL not found in state. Make sure a browser session is available.');
68
+ }
69
+
70
+ logger.debug({
71
+ msg: '[RPA] Connecting to browser via CDP',
72
+ cdpUrl,
73
+ sessionId: state.sessionId,
74
+ node: node.displayName,
75
+ });
76
+
77
+ // Connect to existing browser via CDP
78
+ browser = await chromium.connectOverCDP(cdpUrl);
79
+ const contexts = browser.contexts();
80
+ if (contexts.length === 0) {
81
+ throw new Error('No browser contexts found');
82
+ }
83
+
84
+ // Get the first page or create a new one
85
+ const pages = contexts[0].pages();
86
+ page = pages.length > 0 ? pages[0] : await contexts[0].newPage();
87
+
88
+ // Execute each step
89
+ const results = []; // Collect all extracted data
90
+
91
+ for (const [index, step] of node.steps.entries()) {
92
+ logger.debug({
93
+ msg: '[RPA] Executing step',
94
+ stepIndex: index + 1,
95
+ stepType: step.type,
96
+ sessionId: state.sessionId,
97
+ node: node.displayName,
98
+ });
99
+
100
+ try {
101
+ const result = await executeRpaStep(page, step, state, llm);
102
+
103
+ // Collect extracted data
104
+ if (step.type === RpaActionType.EXTRACT_DATA && result.data) {
105
+ results.push({
106
+ stepIndex: index + 1,
107
+ url: result.url,
108
+ data: result.data,
109
+ });
110
+ }
111
+ } catch (stepError) {
112
+ logger.error({
113
+ msg: '[RPA] Step execution failed',
114
+ stepIndex: index + 1,
115
+ stepType: step.type,
116
+ error: stepError instanceof Error ? stepError.message : 'Unknown error',
117
+ sessionId: state.sessionId,
118
+ node: node.displayName,
119
+ });
120
+
121
+ // Stop execution on error unless configured otherwise
122
+ throw stepError;
123
+ }
124
+ }
125
+
126
+ // Create tool message with results
127
+ const toolMessage = new ToolMessage({
128
+ id: toolCallId,
129
+ content: JSON.stringify({
130
+ result: results,
131
+ }),
132
+ name: 'rpa-task',
133
+ tool_call_id: toolCallId,
134
+ status: 'success',
135
+ });
136
+
137
+ // Update messages - remove old AI message and add updated one with tool message
138
+ state.messages.push(toolMessage);
139
+
140
+ // Update history with RPA execution results
141
+ state.history.push(
142
+ createHistoryStep<HistoryStep>(state.history, {
143
+ type: NodeType.RPA,
144
+ nodeId: node.name,
145
+ nodeDisplayName: node.displayName,
146
+ raw: {
147
+ steps: node.steps,
148
+ results,
149
+ },
150
+ messageIds: [aiMessageId, toolMessage.id!],
151
+ }),
152
+ );
153
+
154
+ return state;
155
+ } catch (error) {
156
+ logger.error({
157
+ msg: '[RPA] Error executing RPA node',
158
+ error: error instanceof Error ? error.message : 'Unknown error',
159
+ sessionId: state.sessionId,
160
+ node: node.displayName,
161
+ });
162
+
163
+ // Create error tool message
164
+ const errorToolMessage = new ToolMessage({
165
+ id: uuidv4(),
166
+ content: JSON.stringify({
167
+ error: error instanceof Error ? error.message : 'Unknown error',
168
+ }),
169
+ name: 'rpa-task',
170
+ tool_call_id: toolCallId,
171
+ status: 'error',
172
+ });
173
+
174
+ // Update messages - remove old AI message and add updated one with error tool message
175
+ state.messages.push(errorToolMessage);
176
+
177
+ // Update history with error
178
+ state.history.push(
179
+ createHistoryStep<HistoryStep>(state.history, {
180
+ type: NodeType.RPA,
181
+ nodeId: node.name,
182
+ nodeDisplayName: node.displayName,
183
+ raw: {
184
+ error: error instanceof Error ? error.message : 'Unknown error',
185
+ steps: node.steps,
186
+ },
187
+ messageIds: [aiMessageId, errorToolMessage.id!],
188
+ }),
189
+ );
190
+
191
+ return state;
192
+ } finally {
193
+ // Note: We don't close the browser as it's connected via CDP
194
+ // The browser session should remain active for other operations
195
+ }
196
+ };
197
+
198
+ graph.addNode(node.name, callback);
199
+ };
@@ -52,7 +52,7 @@ export const addToolNode = async ({
52
52
  }
53
53
  }
54
54
  if (finalMessage) {
55
- const compiledPrompt = compilePrompt(finalMessage, { memory: state.memory });
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;