@minded-ai/mindedjs 1.0.96 → 1.0.97-beta-1235

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 (77) hide show
  1. package/dist/browserTask/README.md +419 -0
  2. package/dist/browserTask/browserAgent.py +632 -0
  3. package/dist/browserTask/captcha_isolated.png +0 -0
  4. package/dist/browserTask/executeBrowserTask.d.ts +2 -0
  5. package/dist/browserTask/executeBrowserTask.d.ts.map +1 -0
  6. package/dist/browserTask/executeBrowserTask.js +78 -0
  7. package/dist/browserTask/executeBrowserTask.js.map +1 -0
  8. package/dist/browserTask/executeBrowserTask.ts +78 -0
  9. package/dist/browserTask/requirements.txt +8 -0
  10. package/dist/browserTask/setup.sh +144 -0
  11. package/dist/cli/index.js +44 -1
  12. package/dist/cli/index.js.map +1 -1
  13. package/dist/nodes/addAppToolNode.js +2 -2
  14. package/dist/nodes/addAppToolNode.js.map +1 -1
  15. package/dist/nodes/addBrowserTaskNode.d.ts +11 -0
  16. package/dist/nodes/addBrowserTaskNode.d.ts.map +1 -0
  17. package/dist/nodes/addBrowserTaskNode.js +100 -0
  18. package/dist/nodes/addBrowserTaskNode.js.map +1 -0
  19. package/dist/nodes/nodeFactory.d.ts.map +1 -1
  20. package/dist/nodes/nodeFactory.js +4 -0
  21. package/dist/nodes/nodeFactory.js.map +1 -1
  22. package/dist/types/Flows.types.d.ts +9 -2
  23. package/dist/types/Flows.types.d.ts.map +1 -1
  24. package/dist/types/Flows.types.js +1 -0
  25. package/dist/types/Flows.types.js.map +1 -1
  26. package/dist/utils/logger.js +1 -1
  27. package/dist/utils/logger.js.map +1 -1
  28. package/docs/getting-started/installation.md +42 -0
  29. package/package.json +13 -5
  30. package/src/agent.ts +0 -897
  31. package/src/checkpointer/checkpointSaverFactory.ts +0 -18
  32. package/src/cli/index.ts +0 -170
  33. package/src/cli/lambdaHandlerTemplate.ts +0 -46
  34. package/src/edges/createDirectEdge.ts +0 -11
  35. package/src/edges/createLogicalRouter.ts +0 -98
  36. package/src/edges/createPromptRouter.ts +0 -210
  37. package/src/edges/edgeFactory.ts +0 -125
  38. package/src/events/AgentEvents.ts +0 -47
  39. package/src/events/index.ts +0 -3
  40. package/src/index.ts +0 -51
  41. package/src/interfaces/zendesk.ts +0 -157
  42. package/src/internalTools/appActionRunnerTool.ts +0 -75
  43. package/src/internalTools/sendPlaceholderMessage.ts +0 -27
  44. package/src/internalTools/timer.ts +0 -137
  45. package/src/llm/createLlmInstance.ts +0 -10
  46. package/src/nodes/addAppToolNode.ts +0 -95
  47. package/src/nodes/addHumanInTheLoopNode.ts +0 -25
  48. package/src/nodes/addJumpToNode.ts +0 -24
  49. package/src/nodes/addJunctionNode.ts +0 -19
  50. package/src/nodes/addPromptNode.ts +0 -117
  51. package/src/nodes/addToolNode.ts +0 -71
  52. package/src/nodes/addToolRunNode.ts +0 -74
  53. package/src/nodes/addTriggerNode.ts +0 -26
  54. package/src/nodes/nodeFactory.ts +0 -53
  55. package/src/platform/config.ts +0 -77
  56. package/src/platform/mindedChatOpenAI.ts +0 -19
  57. package/src/platform/mindedCheckpointSaver.ts +0 -146
  58. package/src/platform/mindedConnection.ts +0 -199
  59. package/src/platform/mindedConnectionTypes.ts +0 -191
  60. package/src/platform/piiGateway/gateway.ts +0 -103
  61. package/src/platform/piiGateway/index.ts +0 -5
  62. package/src/platform/piiGateway/types.ts +0 -29
  63. package/src/playbooks/playbooks.ts +0 -209
  64. package/src/triggers/triggerTypeToDefaultMessage.ts +0 -9
  65. package/src/types/Agent.types.ts +0 -67
  66. package/src/types/Flows.types.ts +0 -184
  67. package/src/types/LLM.types.ts +0 -15
  68. package/src/types/LangGraph.types.ts +0 -48
  69. package/src/types/Platform.types.ts +0 -1
  70. package/src/types/Tools.types.ts +0 -31
  71. package/src/types/Voice.types.ts +0 -4
  72. package/src/utils/extractStateMemoryResponse.ts +0 -16
  73. package/src/utils/history.ts +0 -9
  74. package/src/utils/logger.ts +0 -22
  75. package/src/utils/wait.ts +0 -1
  76. package/src/voice/elevenLabsUtils.ts +0 -81
  77. package/src/voice/voiceSession.ts +0 -295
package/src/agent.ts DELETED
@@ -1,897 +0,0 @@
1
- import { Flow, NodeType, Node, EdgeType, KnownTriggerNames, TriggerType, VoiceTriggerNode, internalNodesSuffix } from './types/Flows.types';
2
-
3
- import { Tool } from './types/Tools.types';
4
- import { v4 as uuidv4 } from 'uuid';
5
- import { BaseLanguageModel } from '@langchain/core/language_models/base';
6
- import { Command, StateGraph } from '@langchain/langgraph';
7
- import { BaseCheckpointSaver } from '@langchain/langgraph';
8
- import { nodeFactory } from './nodes/nodeFactory';
9
- import { CompiledGraph, PreCompiledGraph, stateAnnotation } from './types/LangGraph.types';
10
- import { edgeFactory } from './edges/edgeFactory';
11
- import { AgentEventRequestPayloads, AgentEventResponsePayloads, AgentEvents } from './events/AgentEvents';
12
- import { z } from 'zod';
13
- import * as mindedConnection from './platform/mindedConnection';
14
- import { InvokeMessage, mindedConnectionSocketMessageType } from './platform/mindedConnectionTypes';
15
- import * as fs from 'fs';
16
- import * as path from 'path';
17
- import * as yaml from 'js-yaml';
18
- import {
19
- AgentInvokeParams,
20
- AppTriggerHistoryStep,
21
- TriggerHistoryStep,
22
- MindedSDKConfig,
23
- SessionType,
24
- HistoryStep,
25
- } from './types/Agent.types';
26
- import { createLlmInstance } from './llm/createLlmInstance';
27
- import { createCheckpointSaver } from './checkpointer/checkpointSaverFactory';
28
- import { getConfig } from './platform/config';
29
- import { BaseMessage, HumanMessage } from '@langchain/core/messages';
30
- import triggerTypeToDefaultMessage from './triggers/triggerTypeToDefaultMessage';
31
- import appActionRunnerToolCreator from './internalTools/appActionRunnerTool';
32
- import { VoiceSession } from './voice/voiceSession';
33
- import { BaseVoiceMessage, OnVoiceAudioOut } from './platform/mindedConnectionTypes';
34
- import { PIIGateway, PIIGatewayInstance } from './platform/piiGateway';
35
- import { logger } from './utils/logger';
36
- import { loadPlaybooks, Playbook } from './playbooks/playbooks';
37
- import { createHistoryStep } from './utils/history';
38
- import { timerHandlers } from './internalTools/timer';
39
-
40
- type CreateAgentParams<Memory> = {
41
- memorySchema: z.ZodSchema;
42
- config: MindedSDKConfig;
43
- tools: Tool<any, Memory>[];
44
- platformToken?: string;
45
- memorySaver?: BaseCheckpointSaver;
46
- };
47
-
48
- /**
49
- * The main Agent class that orchestrates flows, tools, and integrations with the Minded platform.
50
- *
51
- * @example
52
- * ```typescript
53
- * const agent = new Agent({
54
- * memorySchema: z.object({
55
- * userName: z.string().optional(),
56
- * preferences: z.record(z.any()).optional()
57
- * }),
58
- * config: {
59
- * flows: ['./src/flows'],
60
- * llm: { provider: 'openai', model: 'gpt-4' }
61
- * },
62
- * tools: [myCustomTool]
63
- * });
64
- * ```
65
- */
66
- export class Agent {
67
- private memorySchema: z.ZodSchema;
68
- private flows!: Flow[];
69
- public tools!: Tool<any, any>[];
70
- /**
71
- * Internal tools
72
- */
73
- public llm!: BaseLanguageModel;
74
- // Remove the mindedConnection property since it's now a singleton module
75
-
76
- // Langgraph memory saver. In memory for local development, Custom for Platform
77
- private checkpointer!: BaseCheckpointSaver;
78
- // Langgraph compiled graph
79
- public compiledGraph!: CompiledGraph;
80
- // Cache for secrets to avoid repeated API calls
81
- private secretsCache: Record<string, string> | null = null;
82
- public config: MindedSDKConfig;
83
-
84
- // PII gateway instance
85
- private _piiGateway: PIIGatewayInstance | null = null;
86
- public playbooks: Playbook[] = [];
87
-
88
- // Getter for PII Gateway that ensures it's available
89
- public get piiGateway(): PIIGatewayInstance {
90
- if (!this._piiGateway) {
91
- throw new Error('PII Gateway is not initialized. Make sure the agent is connected to the Minded platform.');
92
- }
93
- return this._piiGateway;
94
- }
95
-
96
- // Event handlers storage keyed by event name. Handlers can optionally return a value.
97
- private eventHandlers: {
98
- [K in keyof AgentEventRequestPayloads<z.infer<typeof this.memorySchema>>]?: Array<
99
- (payload: AgentEventRequestPayloads<z.infer<typeof this.memorySchema>>[K]) => any | Promise<any>
100
- >;
101
- } = {};
102
-
103
- private initialized = false;
104
- private initPromise: Promise<void> | null = null;
105
- private startingNodeId: string | null = null;
106
- public voiceSessions: Map<string, VoiceSession> = new Map();
107
-
108
- /**
109
- * Create a new Agent instance.
110
- *
111
- * @param params - Configuration parameters for the agent
112
- * @param params.memorySchema - Zod schema defining the structure of the agent's memory
113
- * @param params.config - SDK configuration including flows directory and LLM settings
114
- * @param params.tools - Array of custom tools to be available to the agent
115
- * @param params.platformToken - Optional token for platform authentication
116
- * @param params.memorySaver - Optional custom checkpoint saver for conversation memory
117
- *
118
- * @example
119
- * ```typescript
120
- * const agent = new Agent({
121
- * memorySchema: z.object({
122
- * userName: z.string().optional(),
123
- * conversationHistory: z.array(z.string()).optional()
124
- * }),
125
- * config: {
126
- * flows: ['./src/flows'],
127
- * llm: {
128
- * provider: 'openai',
129
- * model: 'gpt-4-turbo'
130
- * }
131
- * },
132
- * tools: [
133
- * weatherTool,
134
- * databaseTool
135
- * ]
136
- * });
137
- * ```
138
- */
139
- constructor(params: CreateAgentParams<z.infer<typeof this.memorySchema>>) {
140
- const { memorySchema } = params;
141
- this.memorySchema = memorySchema;
142
- this.initPromise = this.init(params);
143
- this.config = params.config;
144
- }
145
-
146
- private async init(params: CreateAgentParams<z.infer<typeof this.memorySchema>>): Promise<void> {
147
- try {
148
- const { config, tools, memorySaver } = params;
149
- const { runLocally } = getConfig();
150
- if (!runLocally) {
151
- await mindedConnection.start();
152
-
153
- // Initialize PII gateway
154
- this._piiGateway = new PIIGateway();
155
-
156
- mindedConnection.on(mindedConnectionSocketMessageType.INVOKE, async (message, callback) => {
157
- const invokeMessage = message as InvokeMessage;
158
- const result = await this.invoke({
159
- triggerBody: invokeMessage.triggerBody,
160
- triggerName: invokeMessage.triggerName,
161
- appName: invokeMessage.appName,
162
- sessionId: invokeMessage.sessionId,
163
- });
164
- callback(result);
165
- });
166
-
167
- mindedConnection.on(mindedConnectionSocketMessageType.RESTORE_CHECKPOINT, async (restoreCheckpointMessage, callback) => {
168
- await this.restoreCheckpoint(restoreCheckpointMessage.sessionId, restoreCheckpointMessage.checkpointId);
169
- callback({ success: true });
170
- });
171
- }
172
-
173
- const [, flows, playbooks] = await Promise.all([this.loadSecrets(), this.loadFlows(config.flows), loadPlaybooks(config.playbooks)]);
174
-
175
- this.playbooks = playbooks;
176
- this.flows = flows;
177
- this.validate();
178
- const appActionsRunnerTools = this.initAppActionsRunnerTools();
179
- this.tools = [...tools, ...appActionsRunnerTools];
180
- this.checkpointer = memorySaver || createCheckpointSaver();
181
-
182
- // call here methods that needs environment variables to be loaded
183
- this.llm = createLlmInstance(config.llm);
184
- this.compiledGraph = this.initializeGraph();
185
- this.initialized = true;
186
-
187
- const flowHasVoiceTrigger = this.flows.some((flow) =>
188
- flow.nodes.some((node) => node.type === NodeType.TRIGGER && node.triggerType === TriggerType.VOICE),
189
- );
190
- if (flowHasVoiceTrigger) {
191
- this.setupVoice();
192
- }
193
- } catch (error) {
194
- this.initialized = false;
195
- throw error;
196
- }
197
- }
198
-
199
- private async loadFlows(flowsDirectories: string[]) {
200
- const { env, isDeployed } = getConfig();
201
- if (['sandbox-staging', 'sandbox'].includes(env) && isDeployed) {
202
- const response = await mindedConnection.awaitEmit<object, { flows: Record<string, Flow> }>(
203
- mindedConnectionSocketMessageType.GET_FLOWS,
204
- {},
205
- );
206
- if (!response?.flows) {
207
- throw new Error('Could not load flows from the platform connection');
208
- }
209
- return Object.values(response.flows);
210
- }
211
- return this.loadFlowsFromDirectory(flowsDirectories);
212
- }
213
-
214
- private loadFlowsFromDirectory(flowsDirectories: string[]): Flow[] {
215
- const flows: Flow[] = [];
216
- for (const flowsDirectory of flowsDirectories) {
217
- if (!fs.existsSync(flowsDirectory)) {
218
- throw new Error(`Flows directory does not exist: ${flowsDirectory}`);
219
- }
220
-
221
- const files = fs.readdirSync(flowsDirectory);
222
-
223
- for (const file of files) {
224
- if (file.endsWith('.yaml') || file.endsWith('.yml')) {
225
- const filePath = path.join(flowsDirectory, file);
226
- try {
227
- const fileContent = fs.readFileSync(filePath, 'utf8');
228
- const parsedFlow = yaml.load(fileContent) as Flow;
229
-
230
- // Validate that the parsed flow has the required structure
231
- if (!parsedFlow.name || !parsedFlow.nodes || !parsedFlow.edges) {
232
- throw new Error(`Invalid flow structure in ${file}. Flow must have name, nodes, and edges.`);
233
- }
234
-
235
- flows.push(parsedFlow);
236
- } catch (error) {
237
- throw new Error(`Failed to parse flow file ${file}: ${error}`);
238
- }
239
- }
240
- }
241
-
242
- if (flows.length === 0) {
243
- throw new Error(`No YAML flow files found in directory: ${flowsDirectory}`);
244
- }
245
- }
246
-
247
- return flows;
248
- }
249
-
250
- private validate() {
251
- if (this.flows.length === 0) {
252
- throw new Error('No flows provided');
253
- }
254
- }
255
-
256
- private async waitForInitialization(): Promise<void> {
257
- if (this.initialized) {
258
- return;
259
- }
260
- if (this.initPromise) {
261
- try {
262
- await this.initPromise;
263
- if (!this.initialized) {
264
- throw new Error('Agent initialization failed');
265
- }
266
- } catch (error) {
267
- throw new Error(`Failed to initialize agent: ${error instanceof Error ? error.message : 'Unknown error'}`);
268
- }
269
- } else {
270
- throw new Error('Agent initialization has not started');
271
- }
272
- }
273
-
274
- private initializeGraph(): CompiledGraph {
275
- const nodes = this.flows.flatMap((flow) => flow.nodes);
276
- const edges = this.flows.flatMap((flow) => flow.edges);
277
-
278
- // Initialize the graph
279
- const graph = new StateGraph(stateAnnotation)
280
- .addNode('init', this.initGraphState.bind(this))
281
- .addEdge('__start__', 'init') as PreCompiledGraph;
282
-
283
- // Add nodes and create nodes object
284
- const nodesObject: { [key: string]: Node } = {};
285
- nodes.forEach((node) => {
286
- // Add to nodes object
287
- nodesObject[node.name] = node;
288
- // Add to graph
289
- nodeFactory({
290
- graph,
291
- node,
292
- tools: this.tools,
293
- llm: this.llm,
294
- emit: this.emit.bind(this),
295
- agent: this,
296
- });
297
- });
298
-
299
- // Add edge from start to first node if no triggers exist
300
- const hasTrigger = nodes.some((node) => node.type === NodeType.TRIGGER && node.triggerType !== TriggerType.MANUAL);
301
- if (!hasTrigger) {
302
- // Find the Main flow
303
- const mainFlow = this.flows.find((flow) => flow.name === 'Main flow');
304
- if (mainFlow && mainFlow.nodes.length > 0) {
305
- this.startingNodeId = mainFlow.nodes[0].name;
306
- graph.addEdge('__start__', mainFlow.nodes[0].name as any);
307
- } else if (nodes.length > 0) {
308
- // Fallback to first node if Main flow not found
309
- this.startingNodeId = nodes[0].name;
310
- graph.addEdge('__start__', nodes[0].name as any);
311
- }
312
- } else {
313
- nodes.forEach((node) => {
314
- if (node.type === NodeType.TRIGGER && node.triggerType !== TriggerType.MANUAL) {
315
- this.startingNodeId = node.name;
316
- graph.addEdge('__start__', node.name as any);
317
- }
318
- });
319
- }
320
-
321
- // Add edges to __end__ for nodes with no outgoing edges
322
- const sourceNodes = new Set(edges.map((edge) => edge.source));
323
- const lastNodes = Object.keys(nodesObject).filter((nodeName) => !sourceNodes.has(nodeName) && !nodesObject[nodeName].canStayOnNode);
324
- lastNodes.forEach((nodeName) => {
325
- edges.push({
326
- source: nodeName,
327
- target: '__end__',
328
- type: EdgeType.STEP_FORWARD,
329
- });
330
- });
331
-
332
- // Add edges
333
- edgeFactory({ graph, edges, nodes: nodesObject, tools: this.tools, llm: this.llm, agent: this });
334
-
335
- // Compile the graph
336
- return graph.compile({ checkpointer: this.checkpointer }) as CompiledGraph;
337
- }
338
-
339
- private async initGraphState(state: typeof stateAnnotation.State): Promise<typeof stateAnnotation.State> {
340
- // Try to parse empty object through the schema to apply default values
341
- // If parsing fails due to required fields without defaults, use empty object as fallback
342
- const parseResult = this.memorySchema.safeParse({});
343
- const initialMemory = parseResult.success ? parseResult.data : {};
344
-
345
- const initialState = {
346
- messages: [],
347
- memory: initialMemory,
348
- triggerMetadata: null,
349
- history: [],
350
- sessionId: state.sessionId || uuidv4(), // Preserve existing sessionId or generate new one
351
- sessionType: state.sessionType || SessionType.TEXT,
352
- overrideStartFromNodeId: null,
353
- };
354
-
355
- // Emit INIT event with the initial state
356
- await this.emit(AgentEvents.INIT, {
357
- state: initialState,
358
- });
359
-
360
- return initialState;
361
- }
362
-
363
- private createTriggerHistoryStep(
364
- currentHistory: HistoryStep[],
365
- nodeId: string,
366
- messageIds: string[],
367
- triggerName: string,
368
- triggerBody: any,
369
- appName?: string,
370
- ): HistoryStep {
371
- const baseStep = {
372
- nodeId: nodeId,
373
- nodeDisplayName: triggerName,
374
- raw: triggerBody,
375
- messageIds,
376
- } as HistoryStep;
377
-
378
- return appName
379
- ? createHistoryStep<AppTriggerHistoryStep>(currentHistory, {
380
- ...baseStep,
381
- type: NodeType.TRIGGER,
382
- appName,
383
- })
384
- : createHistoryStep<TriggerHistoryStep>(currentHistory, {
385
- ...baseStep,
386
- type: NodeType.TRIGGER,
387
- });
388
- }
389
-
390
- /**
391
- * Invoke a trigger to start agent execution with the specified parameters.
392
- *
393
- * This method processes triggers from external systems or applications, allowing the agent
394
- * to respond to events and execute the appropriate flows based on the trigger type.
395
- *
396
- * @param params - The trigger invocation parameters
397
- * @param params.triggerBody - The payload/data associated with the trigger
398
- * @param params.triggerName - The name/type of the trigger being invoked
399
- * @param params.sessionId - Optional session identifier for conversation continuity
400
- * @param params.appName - Optional name of the application triggering the agent in case of an app trigger
401
- *
402
- * @returns Promise that resolves with the agent's execution result
403
- *
404
- * @throws {Error} When the agent is not properly initialized
405
- *
406
- * @example
407
- * ```typescript
408
- * // Manual invoke with a message trigger
409
- * const result = await agent.invoke({
410
- * triggerName: 'minded.message.in.conversation',
411
- * triggerBody: {
412
- * text: 'Hello, how can you help me?',
413
- * },
414
- * sessionId: 'user-123-session'
415
- * });
416
- * ```
417
- */
418
- public async invoke({ triggerBody, triggerName, sessionId, appName }: AgentInvokeParams) {
419
- sessionId = sessionId ?? uuidv4();
420
- try {
421
- await this.waitForInitialization();
422
- let messages: Array<BaseMessage> = [];
423
- let memoryUpdate = {};
424
- let sessionType: SessionType = SessionType.TEXT;
425
- if (triggerName === KnownTriggerNames.DASHBOARD_MESSAGE || triggerName === KnownTriggerNames.VOICE_MESSAGE) {
426
- if (!triggerBody.content) {
427
- throw new Error(`Trigger body content is required for ${JSON.stringify(triggerBody)}`);
428
- }
429
- messages = [new HumanMessage({ content: triggerBody.content, id: uuidv4() })];
430
- sessionType = triggerName === KnownTriggerNames.VOICE_MESSAGE ? SessionType.VOICE : SessionType.TEXT;
431
- } else {
432
- const results = await this.emit(AgentEvents.TRIGGER_EVENT, {
433
- triggerName,
434
- triggerBody,
435
- sessionId,
436
- });
437
- if (results.length === 0) {
438
- if (appName) {
439
- messages = triggerTypeToDefaultMessage[appName]?.[triggerName]?.(triggerBody) ?? [
440
- new HumanMessage({ content: JSON.stringify(triggerBody), id: uuidv4() }),
441
- ];
442
- } else {
443
- messages = [new HumanMessage({ content: JSON.stringify(triggerBody), id: uuidv4() })];
444
- }
445
- } else {
446
- const handlerResult = results.find((r) => r !== undefined);
447
- if (handlerResult) {
448
- if (!handlerResult.isQualified) {
449
- logger.info(`Trigger ${triggerName} was disqualified`);
450
- return;
451
- }
452
- memoryUpdate = handlerResult.memory || {};
453
- messages = handlerResult.messages ?? [];
454
- sessionId = handlerResult.sessionId ?? sessionId;
455
- }
456
- }
457
- }
458
-
459
- logger.info({ message: 'Invoking trigger', triggerName, sessionId, triggerBody });
460
- const langraphConfig = this.getLangraphConfig(sessionId || uuidv4());
461
- const state = await this.compiledGraph.getState(langraphConfig);
462
- const suffixes = Object.values(internalNodesSuffix);
463
- let nodeToBeInvoked =
464
- state.next.length > 0
465
- ? state.values.overrideStartFromNodeId
466
- ? state.values.overrideStartFromNodeId
467
- : state.next[0]
468
- : this.startingNodeId!;
469
- nodeToBeInvoked = nodeToBeInvoked.replace(new RegExp(suffixes.join('|'), 'g'), '');
470
- const historyStep = this.createTriggerHistoryStep(
471
- state.values.history,
472
- nodeToBeInvoked,
473
- messages.map((m) => m.id!),
474
- triggerName,
475
- triggerBody,
476
- appName,
477
- );
478
-
479
- let res;
480
- // Resume interruption
481
- if (state.tasks?.[0]?.interrupts?.length > 0) {
482
- res = await this.compiledGraph.invoke(
483
- new Command({
484
- resume: { memory: memoryUpdate, messages, history: historyStep, sessionId, sessionType },
485
- }),
486
- langraphConfig,
487
- );
488
- } else if (state.values.overrideStartFromNodeId) {
489
- res = await this.compiledGraph.invoke(
490
- new Command({
491
- update: {
492
- overrideStartFromNodeId: null, //reset the overrideStartFromNodeId
493
- messages,
494
- memory: memoryUpdate,
495
- history: historyStep,
496
- sessionId,
497
- sessionType,
498
- },
499
- goto: state.values.overrideStartFromNodeId,
500
- }),
501
- langraphConfig,
502
- );
503
- } else {
504
- res = await this.compiledGraph.invoke(
505
- { messages, memory: memoryUpdate, history: historyStep, sessionId, sessionType },
506
- langraphConfig,
507
- );
508
- }
509
- return res;
510
- } catch (error: any) {
511
- logger.error({
512
- message: 'Invoke error',
513
- errorMessage: error.message,
514
- stack: error.stack,
515
- sessionId,
516
- });
517
- const state = await this.compiledGraph.getState(this.getLangraphConfig(sessionId));
518
- this.emit(AgentEvents.ERROR, { error: error instanceof Error ? error : new Error(JSON.stringify(error)), state: state.values });
519
- throw error;
520
- }
521
- }
522
-
523
- /**
524
- * Register an event handler for specific agent events.
525
- *
526
- * This method allows you to listen for and respond to various events that occur during
527
- * agent execution, such as trigger events, memory updates, or custom application events.
528
- * Multiple handlers can be registered for the same event type and will be executed in the order they are registered.
529
- *
530
- * ## Available Event Types
531
- *
532
- * ### INIT
533
- * Emitted when the agent's graph state is initialized. This happens when a new session begins
534
- * or when the agent starts processing a new conversation context.
535
- *
536
- * **Input Structure:**
537
- * ```typescript
538
- * {
539
- * state: { // Full initial agent state
540
- * messages: BaseMessage[]; // Empty array - no messages yet
541
- * memory: Memory; // Initial memory state (from your schema defaults)
542
- * triggerInvocations: Array<...>; // Empty array - no triggers invoked yet
543
- * triggerMetadata: null; // No trigger metadata initially
544
- * history: HistoryStep[]; // Empty array - no flow history yet
545
- * sessionId: string; // Session identifier (generated or provided)
546
- * sessionType: SessionType; // Type of session (TEXT, VOICE, etc.)
547
- * }
548
- * }
549
- * ```
550
- *
551
- * **Expected Output:** `void` - Handlers are used for side effects like logging, setup, or initialization tasks
552
- *
553
- * **Common Use Cases:**
554
- * - Session logging and analytics tracking
555
- * - Resource initialization for new sessions
556
- * - State validation and verification
557
- * - External service setup and configuration
558
- * - Session routing and management
559
- * - Debugging and troubleshooting
560
- *
561
- * ### AI_MESSAGE
562
- * Emitted when an AI generates a message that should be sent to the user.
563
- *
564
- * **Input Structure:**
565
- * ```typescript
566
- * {
567
- * message: string; // The AI-generated message content
568
- * state: { // Full agent state
569
- * messages: BaseMessage[]; // Conversation messages
570
- * memory: Memory; // Current memory state (your defined memory schema)
571
- * triggerInvocations: Array<...>; // Trigger invocation history
572
- * triggerMetadata: {...} | null; // Current trigger metadata
573
- * history: HistoryStep[]; // Flow execution history with detailed step information
574
- * sessionId: string; // Session identifier
575
- * }
576
- * }
577
- * ```
578
- *
579
- * **Expected Output:** `void` - Handlers are used for side effects like sending messages to UI
580
- *
581
- * **Common Use Cases:**
582
- * - Real-time chat UI updates with session context
583
- * - Logging AI responses for analytics or debugging
584
- * - Message formatting and transformation
585
- * - Notifications and alerts based on AI responses
586
- * - Session-based message routing
587
- *
588
- * ### TRIGGER_EVENT
589
- * Emitted when a trigger node is executed. Allows you to qualify, transform, and provide
590
- * initial state for trigger inputs before they're processed by the agent.
591
- *
592
- * **Input Structure:**
593
- * ```typescript
594
- * {
595
- * triggerName: string; // Name of the trigger being executed
596
- * triggerBody: any; // The trigger input data (type varies by trigger)
597
- * }
598
- * ```
599
- *
600
- * **Expected Output:** One of three possible return types:
601
- *
602
- * 1. **Provide Initial State:**
603
- * ```typescript
604
- * {
605
- * messages?: BaseMessage[]; // Initial messages for the conversation
606
- * memory?: Memory; // Initial memory state
607
- * sessionId?: string; // Session ID for persistence (resumes existing sessions)
608
- * }
609
- * ```
610
- *
611
- * 2. **Disqualify the Trigger:**
612
- * ```typescript
613
- * false // Rejects/disqualifies the trigger from processing
614
- * ```
615
- *
616
- * 3. **Qualify without Initial State:**
617
- * ```typescript
618
- * void // Qualifies the trigger but provides no initial state
619
- * ```
620
- *
621
- * **Common Use Cases:**
622
- * - Input validation and trigger qualification
623
- * - Data transformation into standardized formats
624
- * - Context setting with initial memory state
625
- * - Access control and business rule enforcement
626
- * - Routing logic for different trigger types
627
- *
628
- * @template E - The event type, constrained to known agent event types
629
- * @param event - The name of the event to listen for
630
- * @param handler - The function to call when the event is emitted
631
- *
632
- * @example
633
- * ```typescript
634
- * // INIT Event Handler
635
- * agent.on('INIT', async ({ state }) => {
636
- * logger.info({ msg: 'Agent initialized for session:', sessionId: state.sessionId });
637
- * logger.info({ msg: 'Session type:', sessionType: state.sessionType });
638
- * logger.info({ msg: 'Initial memory:', memory: state.memory });
639
- *
640
- * // Setup session-specific resources
641
- * await initializeSessionResources(state.sessionId);
642
- *
643
- * // Log session start for analytics
644
- * await logSessionStart({
645
- * sessionId: state.sessionId,
646
- * sessionType: state.sessionType,
647
- * timestamp: new Date(),
648
- * });
649
- * });
650
- *
651
- * // AI_MESSAGE Event Handler
652
- * agent.on('AI_MESSAGE', async ({ message, state }) => {
653
- * logger.info({ msg: 'AI said:', output: message });
654
- * logger.info({ msg: 'Current memory:', output: state.memory });
655
- * logger.info({ msg: 'Session ID:', output: state.sessionId });
656
- *
657
- * // Send to user interface with session context
658
- * await sendMessageToUser(message, state.sessionId);
659
- *
660
- * // Send via WebSocket
661
- * await websocket.send(JSON.stringify({
662
- * type: 'ai_message',
663
- * content: message,
664
- * sessionId: state.sessionId,
665
- * memory: state.memory
666
- * }));
667
- * });
668
- *
669
- * // TRIGGER_EVENT Event Handler - Input Validation
670
- * agent.on('TRIGGER_EVENT', async ({ triggerName, triggerBody }) => {
671
- * // Validate trigger input
672
- * if (!isValidInput(triggerBody)) {
673
- * return false; // Disqualify the trigger
674
- * }
675
- *
676
- * // Business hours check
677
- * if (triggerName === 'supportRequest' && !isBusinessHours()) {
678
- * return false;
679
- * }
680
- *
681
- * return {
682
- * memory: { validatedInput: triggerBody },
683
- * messages: [new HumanMessage('Support request received')],
684
- * sessionId: triggerBody.userId // Resume existing session
685
- * };
686
- * });
687
- *
688
- * // TRIGGER_EVENT Event Handler - Data Transformation
689
- * agent.on('TRIGGER_EVENT', async ({ triggerName, triggerBody }) => {
690
- * if (triggerName === 'emailTrigger') {
691
- * // Transform email data into structured format
692
- * const parsedEmail = parseEmailContent(triggerBody);
693
- *
694
- * return {
695
- * memory: {
696
- * emailSubject: parsedEmail.subject,
697
- * senderEmail: parsedEmail.from
698
- * },
699
- * messages: [new HumanMessage(parsedEmail.content)],
700
- * };
701
- * }
702
- * });
703
- * ```
704
- */
705
- // Public API for registering event listeners
706
- public on<E extends keyof AgentEventRequestPayloads<z.infer<typeof this.memorySchema>>>(
707
- event: E,
708
- handler: (
709
- payload: AgentEventRequestPayloads<z.infer<typeof this.memorySchema>>[E],
710
- ) => Promise<AgentEventResponsePayloads<z.infer<typeof this.memorySchema>>[E] | void>,
711
- ): void {
712
- if (!this.eventHandlers[event]) {
713
- this.eventHandlers[event] = [];
714
- }
715
- // We can safely cast here since we ensured the array exists
716
- (this.eventHandlers[event] as Array<typeof handler>).push(handler);
717
- }
718
-
719
- // Internal method to emit events to the registered listeners
720
- private async emit<E extends keyof AgentEventRequestPayloads<z.infer<typeof this.memorySchema>>>(
721
- event: E,
722
- payload: AgentEventRequestPayloads<z.infer<typeof this.memorySchema>>[E],
723
- ): Promise<AgentEventResponsePayloads<z.infer<typeof this.memorySchema>>[E][]> {
724
- if (!this.eventHandlers[event]) {
725
- return [];
726
- }
727
- const results = await Promise.all(this.eventHandlers[event]!.map(async (handler) => handler(payload)));
728
- return results;
729
- }
730
-
731
- private initAppActionsRunnerTools() {
732
- const { runLocally } = getConfig();
733
- if (!runLocally && !mindedConnection.isConnected() && process.env.NODE_ENV !== 'test') {
734
- throw new Error('Minded connection is mandatory to use run app action tools');
735
- }
736
- return this.flows
737
- .flatMap((flow) => flow.nodes.filter((node) => node.type === NodeType.APP_TOOL))
738
- .map((node) => appActionRunnerToolCreator(node.metadata.schema, node.displayName!));
739
- }
740
-
741
- // Private method to get secrets from the backend service and load them into environment variables
742
- private async loadSecrets(): Promise<Record<string, string>> {
743
- // Skip secret loading in local development
744
- const { runLocally } = getConfig();
745
- if (runLocally) {
746
- logger.info('Running locally - skipping secret loading');
747
- return {};
748
- }
749
- if (!mindedConnection.isConnected()) {
750
- throw new Error('Minded connection is not established when trying to get secrets');
751
- }
752
-
753
- // Return cached secrets if available
754
- if (this.secretsCache !== null) {
755
- return this.secretsCache;
756
- }
757
-
758
- try {
759
- // Check if mindedConnection is available
760
- if (!mindedConnection.isConnected()) {
761
- throw new Error('Platform is not available');
762
- }
763
-
764
- const response = await mindedConnection.awaitEmit<object, { secrets: Record<string, string> }>(
765
- mindedConnectionSocketMessageType.GET_SECRETS,
766
- {},
767
- );
768
-
769
- // Extract secrets from response
770
- const secrets = response.secrets || {};
771
-
772
- // Load secrets into process.env
773
- logger.debug(`Loading ${Object.keys(secrets).length} secrets into environment variables`);
774
- Object.entries(secrets).forEach(([key, value]) => {
775
- process.env[key] = value;
776
- });
777
-
778
- // Cache the secrets for future requests
779
- this.secretsCache = secrets;
780
-
781
- return secrets;
782
- } catch (error) {
783
- throw new Error(`Failed to fetch secrets: ${error instanceof Error ? error.message : 'Unknown error'}`);
784
- }
785
- }
786
-
787
- public getLangraphConfig(sessionId: string, checkpointId?: string) {
788
- return { configurable: { thread_id: sessionId, recursionLimit: 3, checkpoint_id: checkpointId } };
789
- }
790
-
791
- private setupVoice(): void {
792
- logger.info('Setting up voice');
793
- if (!mindedConnection.isConnected()) {
794
- throw new Error('Minded connection is required');
795
- }
796
-
797
- const connection = mindedConnection;
798
- const { dashboardConnected } = getConfig();
799
-
800
- if (dashboardConnected) {
801
- // Listen for voice session start
802
- connection.on(mindedConnectionSocketMessageType.DASHBOARD_VOICE_SESSION_START, async (message) => {
803
- const sessionStart = message as BaseVoiceMessage;
804
- await this.startVoiceSession({ sessionId: sessionStart.sessionId });
805
- });
806
-
807
- // Listen for incoming audio from the platform
808
- connection.on(mindedConnectionSocketMessageType.DASHBOARD_VOICE_USER_AUDIO, (message) => {
809
- const audioMessage = message as OnVoiceAudioOut;
810
- const voiceSession = this.voiceSessions.get(audioMessage.sessionId);
811
- if (voiceSession) {
812
- voiceSession.sendAudio(audioMessage.audioData);
813
- } else {
814
- logger.trace({
815
- message: 'Audio received; voice session not found for sessionId',
816
- sessionId: audioMessage.sessionId,
817
- activeSessions: Array.from(this.voiceSessions.keys()),
818
- });
819
- }
820
- });
821
-
822
- // Hangup / end session handler
823
- connection.on(mindedConnectionSocketMessageType.DASHBOARD_VOICE_SESSION_END, (message) => {
824
- const hangup = message as BaseVoiceMessage;
825
- logger.debug({ message: 'Dashboard eneded voice session', sessionId: hangup.sessionId });
826
- const voiceSession = this.voiceSessions.get(hangup.sessionId);
827
- if (voiceSession) {
828
- voiceSession.hangup();
829
- } else {
830
- logger.trace({
831
- message: 'Session ended; voice session not found for sessionId',
832
- sessionId: hangup.sessionId,
833
- activeSessions: this.voiceSessions.keys(),
834
- });
835
- }
836
- });
837
- }
838
- }
839
-
840
- /*
841
- To be used by the Lambda wrapper to start voice sessions
842
- */
843
- public async startVoiceSession(params: { sessionId: string }): Promise<VoiceSession> {
844
- await this.waitForInitialization();
845
- const voiceTrigger = this.flows
846
- .flatMap((flow) => flow.nodes)
847
- .find((node) => node.type === NodeType.TRIGGER && node.triggerType === TriggerType.VOICE) as VoiceTriggerNode;
848
- if (!voiceTrigger) {
849
- throw new Error('Voice trigger not found in flows');
850
- }
851
- const voiceSession = new VoiceSession({
852
- agent: this,
853
- sessionId: params.sessionId,
854
- firstMessage: voiceTrigger.firstMessage,
855
- voiceId: voiceTrigger.voiceId,
856
- });
857
- await voiceSession.init();
858
- logger.debug({ message: 'Voice session initialized', sessionId: params.sessionId });
859
- this.voiceSessions.set(params.sessionId, voiceSession);
860
-
861
- // Emit voice session start event
862
- await this.emit(AgentEvents.VOICE_SESSION_START, {
863
- sessionId: params.sessionId,
864
- });
865
-
866
- return voiceSession;
867
- }
868
-
869
- /*
870
- To be used by the Lambda wrapper to trigger timers
871
- */
872
- public async timerTrigger(params: { sessionId: string; timerName: string; eventArgs: Record<string, any> }): Promise<void> {
873
- const handlers = timerHandlers.get(params.timerName) || [];
874
- for (const { handler } of handlers) {
875
- await handler({
876
- sessionId: params.sessionId,
877
- payload: params.eventArgs,
878
- });
879
- }
880
- }
881
-
882
- /*
883
- To be used by the Lambda wrapper to restore checkpoints
884
- */
885
- public async restoreCheckpoint(sessionId: string, checkpointId: string): Promise<void> {
886
- logger.info({ message: 'Restoring checkpoint', sessionId, checkpointId });
887
- const langraphConfig = this.getLangraphConfig(sessionId, checkpointId);
888
- await this.compiledGraph.invoke(
889
- new Command({
890
- resume: {
891
- sessionId,
892
- },
893
- }),
894
- langraphConfig,
895
- );
896
- }
897
- }