@minded-ai/mindedjs 2.0.4 → 2.0.5-beta.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 (72) hide show
  1. package/dist/agent.d.ts.map +1 -1
  2. package/dist/agent.js +2 -0
  3. package/dist/agent.js.map +1 -1
  4. package/dist/browserTask/cdp.d.ts +23 -0
  5. package/dist/browserTask/cdp.d.ts.map +1 -0
  6. package/dist/browserTask/cdp.js +162 -0
  7. package/dist/browserTask/cdp.js.map +1 -0
  8. package/dist/browserTask/executeBrowserTask.d.ts +9 -3
  9. package/dist/browserTask/executeBrowserTask.d.ts.map +1 -1
  10. package/dist/browserTask/executeBrowserTask.js +50 -3
  11. package/dist/browserTask/executeBrowserTask.js.map +1 -1
  12. package/dist/browserTask/executeBrowserTask.py +42 -0
  13. package/dist/browserTask/localBrowserTask.d.ts +21 -0
  14. package/dist/browserTask/localBrowserTask.d.ts.map +1 -0
  15. package/dist/browserTask/localBrowserTask.js +172 -0
  16. package/dist/browserTask/localBrowserTask.js.map +1 -0
  17. package/dist/cli/index.js +0 -0
  18. package/dist/edges/createPromptRouter.d.ts.map +1 -1
  19. package/dist/edges/createPromptRouter.js +3 -10
  20. package/dist/edges/createPromptRouter.js.map +1 -1
  21. package/dist/guidelines/guidelinesManager.d.ts +37 -0
  22. package/dist/guidelines/guidelinesManager.d.ts.map +1 -0
  23. package/dist/guidelines/guidelinesManager.js +172 -0
  24. package/dist/guidelines/guidelinesManager.js.map +1 -0
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +4 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/internalTools/retell.d.ts +12 -0
  30. package/dist/internalTools/retell.d.ts.map +1 -0
  31. package/dist/internalTools/retell.js +54 -0
  32. package/dist/internalTools/retell.js.map +1 -0
  33. package/dist/internalTools/sendPlaceholderMessage.d.ts +14 -0
  34. package/dist/internalTools/sendPlaceholderMessage.d.ts.map +1 -0
  35. package/dist/internalTools/sendPlaceholderMessage.js +61 -0
  36. package/dist/internalTools/sendPlaceholderMessage.js.map +1 -0
  37. package/dist/nodes/addBrowserTaskNode.d.ts.map +1 -1
  38. package/dist/nodes/addBrowserTaskNode.js +6 -1
  39. package/dist/nodes/addBrowserTaskNode.js.map +1 -1
  40. package/dist/nodes/addBrowserTaskRunNode.d.ts.map +1 -1
  41. package/dist/nodes/addBrowserTaskRunNode.js +1 -1
  42. package/dist/nodes/addBrowserTaskRunNode.js.map +1 -1
  43. package/dist/platform/mindedChatOpenAI.d.ts +5 -0
  44. package/dist/platform/mindedChatOpenAI.d.ts.map +1 -0
  45. package/dist/platform/mindedChatOpenAI.js +23 -0
  46. package/dist/platform/mindedChatOpenAI.js.map +1 -0
  47. package/dist/platform/mindedConnectionTypes.d.ts +10 -0
  48. package/dist/platform/mindedConnectionTypes.d.ts.map +1 -1
  49. package/dist/platform/mindedConnectionTypes.js +1 -0
  50. package/dist/platform/mindedConnectionTypes.js.map +1 -1
  51. package/dist/types/Flows.types.d.ts +1 -0
  52. package/dist/types/Flows.types.d.ts.map +1 -1
  53. package/dist/types/Flows.types.js.map +1 -1
  54. package/dist/utils/extractStateMemoryResponse.d.ts +5 -0
  55. package/dist/utils/extractStateMemoryResponse.d.ts.map +1 -0
  56. package/dist/utils/extractStateMemoryResponse.js +91 -0
  57. package/dist/utils/extractStateMemoryResponse.js.map +1 -0
  58. package/dist/utils/extractToolMemoryResponse.d.ts +4 -0
  59. package/dist/utils/extractToolMemoryResponse.d.ts.map +1 -0
  60. package/dist/utils/extractToolMemoryResponse.js +16 -0
  61. package/dist/utils/extractToolMemoryResponse.js.map +1 -0
  62. package/package.json +4 -3
  63. package/src/agent.ts +2 -0
  64. package/src/browserTask/executeBrowserTask.py +42 -0
  65. package/src/browserTask/executeBrowserTask.ts +59 -1
  66. package/src/browserTask/localBrowserTask.ts +195 -0
  67. package/src/edges/createPromptRouter.ts +7 -15
  68. package/src/index.ts +2 -0
  69. package/src/nodes/addBrowserTaskNode.ts +7 -2
  70. package/src/nodes/addBrowserTaskRunNode.ts +1 -0
  71. package/src/platform/mindedConnectionTypes.ts +12 -0
  72. package/src/types/Flows.types.ts +1 -0
@@ -1,17 +1,32 @@
1
+ import { exec as execCb } from 'node:child_process';
1
2
  import { logger } from '../utils/logger';
2
3
  import { mindedConnection } from '../platform/mindedConnection';
4
+ import path from 'path';
3
5
  import {
4
6
  mindedConnectionSocketMessageType,
5
7
  CreateBrowserSessionResponse,
6
8
  InvokeBrowserTaskResponse,
7
9
  CreateBrowserSessionRequest,
8
10
  InvokeBrowserTaskRequest,
11
+ DestroyBrowserSessionRequest,
12
+ DestroyBrowserSessionResponse,
9
13
  } from '../platform/mindedConnectionTypes';
14
+ import { promisify } from 'node:util';
15
+ import { kill, getOrStartLocalCDP } from './localBrowserTask';
16
+ const exec = promisify(execCb);
10
17
 
11
18
  // Socket-based browser task functions
12
- export const createBrowserSession = async (proxy?: string, onPrem?: boolean): Promise<CreateBrowserSessionResponse> => {
19
+ export const createBrowserSession = async ({ sessionId, proxy, onPrem, localRun }: { sessionId: string, proxy?: string, onPrem?: boolean, localRun?: boolean }): Promise<CreateBrowserSessionResponse> => {
13
20
  logger.debug({ msg: 'Creating browser session via socket', proxy });
14
21
 
22
+ if (localRun) {
23
+ const { cdpUrl } = await getOrStartLocalCDP({ headless: false });
24
+ return {
25
+ sessionId,
26
+ cdpUrl
27
+ }
28
+ }
29
+
15
30
  const response = await mindedConnection.awaitEmit<CreateBrowserSessionRequest, CreateBrowserSessionResponse>(
16
31
  mindedConnectionSocketMessageType.CREATE_BROWSER_SESSION,
17
32
  {
@@ -36,6 +51,35 @@ export const createBrowserSession = async (proxy?: string, onPrem?: boolean): Pr
36
51
  return response;
37
52
  };
38
53
 
54
+ export const destroyBrowserSession = async (sessionId: string, onPrem?: boolean, localRun?: boolean): Promise<DestroyBrowserSessionResponse> => {
55
+ logger.debug({ msg: 'Destroying browser session via socket', sessionId, onPrem });
56
+
57
+ if (localRun) {
58
+ await kill();
59
+ return {
60
+ success: true,
61
+ };
62
+ }
63
+
64
+ const response = await mindedConnection.awaitEmit<DestroyBrowserSessionRequest, DestroyBrowserSessionResponse>(
65
+ mindedConnectionSocketMessageType.DESTROY_BROWSER_SESSION,
66
+ {
67
+ type: mindedConnectionSocketMessageType.DESTROY_BROWSER_SESSION,
68
+ sessionId,
69
+ onPrem,
70
+ },
71
+ 30000,
72
+ );
73
+
74
+ if (response.error) {
75
+ logger.error({ msg: 'Failed to destroy browser session', error: response.error, sessionId });
76
+ throw new Error(response.error);
77
+ }
78
+
79
+ logger.debug({ msg: 'Browser session destroyed successfully', sessionId });
80
+ return response;
81
+ };
82
+
39
83
  export const invokeBrowserTask = async (
40
84
  sessionId: string,
41
85
  cdpUrl: string,
@@ -43,6 +87,7 @@ export const invokeBrowserTask = async (
43
87
  keepAlive?: boolean,
44
88
  hooks?: { name: string }[],
45
89
  onPrem?: boolean,
90
+ localRun?: boolean,
46
91
  toolSchemas?: any[],
47
92
  outputSchema?: {
48
93
  name: string;
@@ -62,6 +107,19 @@ export const invokeBrowserTask = async (
62
107
  });
63
108
 
64
109
  try {
110
+
111
+ if (localRun) {
112
+ 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 });
116
+ return {
117
+ result: stdout,
118
+ steps: [],
119
+ recordings: [],
120
+ };
121
+ }
122
+
65
123
  const response = await mindedConnection.awaitEmit<InvokeBrowserTaskRequest, InvokeBrowserTaskResponse>(
66
124
  mindedConnectionSocketMessageType.INVOKE_BROWSER_TASK,
67
125
  {
@@ -0,0 +1,195 @@
1
+ // getOrStartLocalCDP.ts
2
+ import { spawn, ChildProcess } from "child_process";
3
+ import * as fs from "fs/promises";
4
+ import * as fscb from "fs";
5
+ import * as path from "path";
6
+ import * as http from "http";
7
+ import { chromium } from "playwright";
8
+
9
+ let localBrowserTask: {
10
+ cdpUrl: string;
11
+ proc: ChildProcess;
12
+ } | null = null;
13
+
14
+ export type StartChromiumOptions = {
15
+ /** Headless by default. Set to false to see a window. */
16
+ headless?: boolean;
17
+ /** Extra CLI flags to pass to Chromium. */
18
+ extraArgs?: string[];
19
+ /** Provide your own user-data-dir. If omitted, a temp dir is created and later removable via `kill()`. */
20
+ userDataDir?: string;
21
+ /** Extra env vars. */
22
+ env?: NodeJS.ProcessEnv;
23
+ /** How long to wait for the DevTools port to come up (ms). Default: 10000 */
24
+ timeoutMs?: number;
25
+ };
26
+
27
+ /**
28
+ * Launch Chromium with --remote-debugging-port=0 and return the CDP ws URL.
29
+ * Works with Chrome/Chromium/Chrome for Testing.
30
+ */
31
+ export async function getOrStartLocalCDP(
32
+ opts: StartChromiumOptions
33
+ ): Promise<{ cdpUrl: string }> {
34
+
35
+ if (localBrowserTask) {
36
+ return {
37
+ cdpUrl: localBrowserTask.cdpUrl,
38
+ };
39
+ }
40
+
41
+ const {
42
+ headless = false,
43
+ extraArgs = [],
44
+ env,
45
+ timeoutMs = 10_000,
46
+ } = opts;
47
+
48
+ const executablePath = chromium.executablePath();
49
+
50
+ const profileDir = path.join(__dirname, "profile");
51
+ await fs.mkdir(profileDir, { recursive: true });
52
+
53
+ const userDataDir = opts.userDataDir ?? profileDir;
54
+
55
+ const args = [
56
+ "--remote-debugging-port=0", // let Chrome choose a free port
57
+ `--user-data-dir=${userDataDir}`,
58
+ "--no-first-run",
59
+ "--no-default-browser-check",
60
+ ...(headless ? ["--headless=new", "--disable-gpu"] : []),
61
+ // Add your own flags if needed (e.g., '--no-sandbox' in some containers)
62
+ ...extraArgs,
63
+ "about:blank",
64
+ ];
65
+
66
+ const proc = spawn(executablePath, args, {
67
+ stdio: ["ignore", "ignore", "pipe"], // stderr is useful for debugging
68
+ env: { ...process.env, ...env },
69
+ });
70
+
71
+ // If Chromium dies early, surface the error.
72
+ const earlyExit = new Promise<never>((_, reject) => {
73
+ proc.once("exit", (code, signal) => {
74
+ reject(
75
+ new Error(`Chromium exited prematurely (code=${code}, signal=${signal}).`)
76
+ );
77
+ });
78
+ });
79
+
80
+ const devtoolsPortFile = path.join(userDataDir, "DevToolsActivePort");
81
+
82
+ // Wait for DevToolsActivePort file to appear, then read it for port + browserId.
83
+ const whenReady = (async () => {
84
+ const startedAt = Date.now();
85
+ while (Date.now() - startedAt < timeoutMs) {
86
+ try {
87
+ // Access succeeds once the file is written.
88
+ await fs.access(devtoolsPortFile, fscb.constants.F_OK);
89
+ break;
90
+ } catch {
91
+ await delay(50);
92
+ }
93
+ }
94
+ // If still not there, bail.
95
+ await fs.access(devtoolsPortFile, fscb.constants.F_OK).catch(() => {
96
+ throw new Error(
97
+ `Timed out after ${timeoutMs}ms waiting for DevToolsActivePort at ${devtoolsPortFile}`
98
+ );
99
+ });
100
+
101
+ const raw = await fs.readFile(devtoolsPortFile, "utf8");
102
+ const [portLine, browserIdLine] = raw.trim().split(/\r?\n/);
103
+ const port = Number(portLine);
104
+ if (!Number.isFinite(port)) {
105
+ throw new Error(`Invalid DevTools port read from file: "${portLine}"`);
106
+ }
107
+
108
+ // Optional: ping /json/version to ensure the endpoint is responsive.
109
+ await waitForHttpOk(`http://127.0.0.1:${port}/json/version`, timeoutMs);
110
+
111
+ const browserId = (browserIdLine || "").trim();
112
+ const cdpUrl =
113
+ browserId.length > 0
114
+ ? `ws://127.0.0.1:${port}/devtools/browser/${browserId}`
115
+ : // Fallback: some builds don't include the second line; /json/version has the ws url too.
116
+ await fetchWsFromVersionEndpoint(port, timeoutMs);
117
+
118
+ localBrowserTask = {
119
+ cdpUrl,
120
+ proc,
121
+ };
122
+
123
+ return { cdpUrl };
124
+ })();
125
+
126
+ const { cdpUrl } = await Promise.race([whenReady, earlyExit]);
127
+
128
+ return { cdpUrl };
129
+ }
130
+
131
+ // ---- helpers ----
132
+ function delay(ms: number) {
133
+ return new Promise<void>((r) => setTimeout(r, ms));
134
+ }
135
+
136
+ async function waitForHttpOk(url: string, timeoutMs: number) {
137
+ const startedAt = Date.now();
138
+ while (Date.now() - startedAt < timeoutMs) {
139
+ const ok = await httpGetOk(url).catch(() => false);
140
+ if (ok) return;
141
+ await delay(50);
142
+ }
143
+ throw new Error(`Timed out after ${timeoutMs}ms waiting for ${url}`);
144
+ }
145
+
146
+ function httpGetOk(urlStr: string): Promise<boolean> {
147
+ return new Promise((resolve, reject) => {
148
+ const req = http.get(urlStr, (res) => {
149
+ // Drain data to allow socket reuse.
150
+ res.resume();
151
+ resolve(res.statusCode === 200);
152
+ });
153
+ req.on("error", reject);
154
+ req.setTimeout(3000, () => {
155
+ req.destroy(new Error("HTTP timeout"));
156
+ });
157
+ });
158
+ }
159
+
160
+ async function fetchWsFromVersionEndpoint(
161
+ port: number,
162
+ timeoutMs: number
163
+ ): Promise<string> {
164
+ const urlStr = `http://127.0.0.1:${port}/json/version`;
165
+ const body = await httpGetBody(urlStr, timeoutMs);
166
+ const parsed = JSON.parse(body);
167
+ if (!parsed.webSocketDebuggerUrl) {
168
+ throw new Error(`No webSocketDebuggerUrl in ${urlStr} response`);
169
+ }
170
+ return parsed.webSocketDebuggerUrl as string;
171
+ }
172
+
173
+ function httpGetBody(urlStr: string, timeoutMs: number): Promise<string> {
174
+ return new Promise((resolve, reject) => {
175
+ const req = http.get(urlStr, (res) => {
176
+ let data = "";
177
+ res.setEncoding("utf8");
178
+ res.on("data", (chunk) => (data += chunk));
179
+ res.on("end", () => {
180
+ if (res.statusCode === 200) resolve(data);
181
+ else reject(new Error(`HTTP ${res.statusCode}: ${data}`));
182
+ });
183
+ });
184
+ req.on("error", reject);
185
+ req.setTimeout(timeoutMs, () => req.destroy(new Error("HTTP timeout")));
186
+ });
187
+ }
188
+
189
+ export async function kill() {
190
+ if (localBrowserTask) {
191
+ localBrowserTask.proc.kill("SIGTERM");
192
+ await delay(300);
193
+ localBrowserTask.proc.kill("SIGKILL");
194
+ }
195
+ }
@@ -116,12 +116,12 @@ export const createPromptRouter = ({
116
116
  // Define response schema
117
117
  const responseSchema = includeReasoning
118
118
  ? z.object({
119
- nextNodeId: z.string(),
120
- reasoning: z.string(),
121
- })
119
+ nextNodeId: z.string(),
120
+ reasoning: z.string(),
121
+ })
122
122
  : z.object({
123
- nextNodeId: z.string(),
124
- });
123
+ nextNodeId: z.string(),
124
+ });
125
125
 
126
126
  let attempts = 0;
127
127
  let lastError: Error | null = null;
@@ -194,21 +194,13 @@ export const createPromptRouter = ({
194
194
  });
195
195
 
196
196
  if (attempts >= maxRetries) {
197
- // If all retries failed, return the first available edge as fallback
198
- const fallbackNode = edges[0]?.target;
199
197
  logger.error({
200
- msg: '[Router] Prompt router reached max retries, using fallback',
201
- fallbackNode,
198
+ msg: '[Router] Prompt router reached max retries',
202
199
  lastError: lastError.message,
203
200
  sessionId: state.sessionId,
204
201
  edges,
205
202
  });
206
-
207
- if (!fallbackNode) {
208
- throw new Error('No edges available for routing');
209
- }
210
-
211
- return fallbackNode;
203
+ throw new Error('Prompt router reached max retries, no edges available for routing');
212
204
  }
213
205
  }
214
206
  }
package/src/index.ts CHANGED
@@ -97,4 +97,6 @@ export type { ExtractionSchema, ExtractionConfig, ExtractionResult } from './too
97
97
  export { AgentEvents } from './events/AgentEvents';
98
98
  export type { AgentEventRequestPayloads, AgentEventResponsePayloads } from './events/AgentEvents';
99
99
 
100
+ export { createBrowserSession, destroyBrowserSession } from './browserTask/executeBrowserTask';
101
+
100
102
  export { LLMDebugCallbackHandler } from './debugging';
@@ -54,7 +54,7 @@ export const addBrowserTaskNode = async ({ graph, node, agent, llm }: AddBrowser
54
54
  const zodSchema = z.object(schemaFields);
55
55
 
56
56
  // Create langchain tool
57
- const tool = langchainTool(() => {}, {
57
+ const tool = langchainTool(() => { }, {
58
58
  name: 'browser-task',
59
59
  description: node.prompt,
60
60
  schema: zodSchema,
@@ -100,7 +100,12 @@ ${compiledPrompt}
100
100
  ${Object.keys(inputParams).length > 0 ? `# Input parameters:\n${JSON.stringify(inputParams, null, 2)}\n\n` : ''}`;
101
101
 
102
102
  // Create browser session using socket
103
- const session = await createBrowserSession(node.proxy, node.onPrem);
103
+ const session = await createBrowserSession({
104
+ sessionId: state.sessionId,
105
+ proxy: node.proxy,
106
+ onPrem: node.onPrem,
107
+ localRun: node.localRun,
108
+ });
104
109
 
105
110
  if (!session.sessionId || !session.cdpUrl) {
106
111
  throw new Error('Failed to create browser session: missing session details');
@@ -57,6 +57,7 @@ export const addBrowserTaskRunNode = async ({ graph, browserTaskNode, attachedTo
57
57
  keepAlive,
58
58
  hooks,
59
59
  browserTaskNode.onPrem,
60
+ browserTaskNode.localRun,
60
61
  toolSchemas,
61
62
  outputSchema,
62
63
  );
@@ -42,6 +42,7 @@ export enum mindedConnectionSocketMessageType {
42
42
  SDK_VERSION_MISMATCH = 'sdk-version-mismatch', // Browser Task
43
43
  CREATE_BROWSER_SESSION = 'create-browser-session',
44
44
  INVOKE_BROWSER_TASK = 'invoke-browser-task',
45
+ DESTROY_BROWSER_SESSION = 'destroy-browser-session',
45
46
  // Interrupt Session Management
46
47
  INTERRUPT_SESSION_IS_PROCESSED = 'interrupt-session-is-processed',
47
48
  INTERRUPT_SESSION_LOCK = 'interrupt-session-lock',
@@ -85,6 +86,7 @@ export type mindedConnectionSocketMessageTypeMap = {
85
86
  [mindedConnectionSocketMessageType.UPDATE_STATE]: UpdateStateRequest;
86
87
  [mindedConnectionSocketMessageType.CREATE_BROWSER_SESSION]: CreateBrowserSessionRequest;
87
88
  [mindedConnectionSocketMessageType.INVOKE_BROWSER_TASK]: InvokeBrowserTaskRequest;
89
+ [mindedConnectionSocketMessageType.DESTROY_BROWSER_SESSION]: DestroyBrowserSessionRequest;
88
90
  // Interrupt Session Management
89
91
  [mindedConnectionSocketMessageType.INTERRUPT_SESSION_IS_PROCESSED]: InterruptSessionIsProcessedRequest;
90
92
  [mindedConnectionSocketMessageType.INTERRUPT_SESSION_LOCK]: InterruptSessionLockRequest;
@@ -423,6 +425,16 @@ export interface CreateBrowserSessionResponse extends BaseSdkConnectionSocketMes
423
425
  liveViewUrl?: string;
424
426
  }
425
427
 
428
+ export interface DestroyBrowserSessionRequest extends BasemindedConnectionSocketMessage {
429
+ type: mindedConnectionSocketMessageType.DESTROY_BROWSER_SESSION;
430
+ sessionId: string;
431
+ onPrem?: boolean;
432
+ }
433
+
434
+ export interface DestroyBrowserSessionResponse extends BaseSdkConnectionSocketMessageResponseCallbackAck {
435
+ success?: boolean;
436
+ }
437
+
426
438
  export interface InvokeBrowserTaskRequest extends BasemindedConnectionSocketMessage {
427
439
  type: mindedConnectionSocketMessageType.INVOKE_BROWSER_TASK;
428
440
  cdpUrl: string;
@@ -121,6 +121,7 @@ export interface BrowserTaskNode extends BaseNode {
121
121
  proxy?: string; // 2-digit country code like 'IL'
122
122
  hooks?: { name: string }[]; // Array of hooks to be passed to the browser-use lambda
123
123
  onPrem?: boolean;
124
+ localRun?: boolean;
124
125
  }
125
126
 
126
127
  export type TriggerNode = AppTriggerNode | WebhookTriggerNode | ManualTriggerNode | VoiceTriggerNode | InterfaceTriggerNode;