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