@minded-ai/mindedjs 1.0.139 → 1.0.141
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.map +1 -1
- package/dist/agent.js +88 -57
- package/dist/agent.js.map +1 -1
- package/dist/edges/createLogicalRouter.d.ts.map +1 -1
- package/dist/edges/createLogicalRouter.js +30 -4
- package/dist/edges/createLogicalRouter.js.map +1 -1
- package/dist/events/AgentEvents.d.ts +23 -17
- package/dist/events/AgentEvents.d.ts.map +1 -1
- package/dist/events/AgentEvents.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interrupts/BaseInterruptSessionManager.d.ts +3 -6
- package/dist/interrupts/BaseInterruptSessionManager.d.ts.map +1 -1
- package/dist/interrupts/BaseInterruptSessionManager.js.map +1 -1
- package/dist/nodes/addPromptNode.d.ts.map +1 -1
- package/dist/nodes/addPromptNode.js +8 -2
- package/dist/nodes/addPromptNode.js.map +1 -1
- package/dist/platform/config.js +6 -6
- package/dist/platform/config.js.map +1 -1
- package/dist/platform/mindedConnectionTypes.d.ts +9 -41
- package/dist/platform/mindedConnectionTypes.d.ts.map +1 -1
- package/dist/types/Agent.types.d.ts +9 -7
- package/dist/types/Agent.types.d.ts.map +1 -1
- package/dist/types/Agent.types.js.map +1 -1
- package/dist/types/LangGraph.types.d.ts +0 -2
- package/dist/types/LangGraph.types.d.ts.map +1 -1
- package/dist/types/LangGraph.types.js +0 -4
- package/dist/types/LangGraph.types.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/voice/voiceSession.js +1 -1
- package/dist/voice/voiceSession.js.map +1 -1
- package/docs/SUMMARY.md +2 -0
- package/docs/getting-started/environment-configuration.md +45 -0
- package/docs/low-code-editor/tools.md +29 -1
- package/docs/low-code-editor/triggers.md +26 -0
- package/docs/platform/sso.md +27 -0
- package/docs/sdk/agent-api.md +3 -3
- package/docs/sdk/events.md +67 -15
- package/package.json +2 -2
- package/src/agent.ts +96 -70
- package/src/edges/createLogicalRouter.ts +36 -4
- package/src/events/AgentEvents.ts +23 -17
- package/src/index.ts +1 -0
- package/src/interrupts/BaseInterruptSessionManager.ts +4 -6
- package/src/nodes/addPromptNode.ts +8 -2
- package/src/platform/config.ts +6 -6
- package/src/platform/mindedConnectionTypes.ts +9 -41
- package/src/types/Agent.types.ts +11 -7
- package/src/types/LangGraph.types.ts +0 -4
- package/src/utils/logger.ts +14 -11
- package/src/voice/voiceSession.ts +6 -6
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# SSO (Single Sign-On)
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Minded platform supports SAML-based Single Sign-On (SSO) as an **enterprise feature** for authentication. This allows organizations to integrate their existing identity provider (IdP) with the Minded platform for seamless and secure user authentication.
|
|
6
|
+
|
|
7
|
+
## Configuration
|
|
8
|
+
|
|
9
|
+
Setting up SSO involves exchanging configuration details between Minded (as the Service Provider) and your organization's Identity Provider.
|
|
10
|
+
|
|
11
|
+
### From Minded (Service Provider → IdP)
|
|
12
|
+
|
|
13
|
+
When initiating SSO setup, Minded will provide the following configuration details:
|
|
14
|
+
|
|
15
|
+
- **Identifier (Entity ID)**: A unique identifier for the Minded platform as a service provider
|
|
16
|
+
- **Reply URL**: The endpoint where your IdP will send SAML responses after authentication
|
|
17
|
+
|
|
18
|
+
### From Customer (IdP → Service Provider)
|
|
19
|
+
|
|
20
|
+
Once SSO is configured on your organization's side, you will need to provide Minded with:
|
|
21
|
+
|
|
22
|
+
- **Identity Provider (IdP) metadata**: XML metadata file containing your IdP's configuration
|
|
23
|
+
- **SAML signing certificates**: Certificates used to verify the authenticity of SAML assertions
|
|
24
|
+
|
|
25
|
+
## Support
|
|
26
|
+
|
|
27
|
+
To setup SSO configuration please contact the Minded support team.
|
package/docs/sdk/agent-api.md
CHANGED
|
@@ -49,7 +49,7 @@ async updateState({
|
|
|
49
49
|
- `state` (Partial<State>): An object containing the state properties to update. Can include:
|
|
50
50
|
- `messages`: Array of messages to add or update
|
|
51
51
|
- `memory`: Partial memory object to merge with existing memory
|
|
52
|
-
- `
|
|
52
|
+
- `goto`: Node ID to override the next execution point
|
|
53
53
|
- Any other state properties defined in your agent
|
|
54
54
|
|
|
55
55
|
#### Usage Examples
|
|
@@ -156,7 +156,7 @@ await agent.updateState({
|
|
|
156
156
|
sessionId: voiceSessionId,
|
|
157
157
|
state: {
|
|
158
158
|
messages: [correctedMessage],
|
|
159
|
-
|
|
159
|
+
goto: currentNodeId,
|
|
160
160
|
},
|
|
161
161
|
});
|
|
162
162
|
```
|
|
@@ -214,7 +214,7 @@ const graphState = await agent.compiledGraph.getState(agent.getLangraphConfig(se
|
|
|
214
214
|
// Update state with LangGraph API
|
|
215
215
|
await agent.compiledGraph.updateState(agent.getLangraphConfig(sessionId), {
|
|
216
216
|
messages: [newMessage],
|
|
217
|
-
|
|
217
|
+
goto: 'specific-node',
|
|
218
218
|
});
|
|
219
219
|
```
|
|
220
220
|
|
package/docs/sdk/events.md
CHANGED
|
@@ -207,6 +207,9 @@ The three common patterns are:
|
|
|
207
207
|
memory?: Memory, // Optional initial memory state
|
|
208
208
|
history?: HistoryStep[], // Optional history steps to include
|
|
209
209
|
sessionId?: string, // Optional session continuity identifier
|
|
210
|
+
state?: {
|
|
211
|
+
goto?: string, // Optional: jump to a specific node ID
|
|
212
|
+
}
|
|
210
213
|
}
|
|
211
214
|
```
|
|
212
215
|
|
|
@@ -292,6 +295,27 @@ agent.on(events.TRIGGER_EVENT, async ({ triggerName, triggerBody }) => {
|
|
|
292
295
|
});
|
|
293
296
|
```
|
|
294
297
|
|
|
298
|
+
#### Dynamic Flow Control with goto
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
agent.on(events.TRIGGER_EVENT, async ({ triggerName, triggerBody, state }) => {
|
|
302
|
+
// Check condition and jump to specific node
|
|
303
|
+
if (triggerBody.priority === 'high') {
|
|
304
|
+
return {
|
|
305
|
+
isQualified: true,
|
|
306
|
+
state: {
|
|
307
|
+
...state,
|
|
308
|
+
goto: 'high-priority-handler', // Jump to specific node
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return { isQualified: true };
|
|
314
|
+
});
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
> **Note:** The jump occurs only when the current invocation completes and the next one begins. The agent will finish executing the current node before jumping to the specified node.
|
|
318
|
+
|
|
295
319
|
### Common Use Cases
|
|
296
320
|
|
|
297
321
|
- **Input Validation**: Ensure trigger data meets requirements before processing
|
|
@@ -302,7 +326,7 @@ agent.on(events.TRIGGER_EVENT, async ({ triggerName, triggerBody }) => {
|
|
|
302
326
|
|
|
303
327
|
## ERROR
|
|
304
328
|
|
|
305
|
-
The `ERROR` event is emitted when an error occurs during agent execution. This event allows you to handle errors gracefully, log them for debugging,
|
|
329
|
+
The `ERROR` event is emitted when an error occurs during agent execution. This event allows you to handle errors gracefully, log them for debugging, implement custom error recovery logic, or control whether the error should be thrown onwards.
|
|
306
330
|
|
|
307
331
|
### Input Structure
|
|
308
332
|
|
|
@@ -320,9 +344,15 @@ The `ERROR` event is emitted when an error occurs during agent execution. This e
|
|
|
320
344
|
|
|
321
345
|
### Handler Return Value
|
|
322
346
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
347
|
+
```typescript
|
|
348
|
+
{
|
|
349
|
+
throwError: boolean; // Whether to throw the error onwards and stop the agent run
|
|
350
|
+
state?: Partial<State<Memory>>; // Optional state updates to apply
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
- **throwError**: When set to `true`, the error will be thrown onwards and the agent run will stop. If `false` or not returned, the error is caught and the agent continues.
|
|
355
|
+
- **state**: Optional partial state updates to apply before continuing or throwing
|
|
326
356
|
|
|
327
357
|
### Usage Example
|
|
328
358
|
|
|
@@ -335,13 +365,12 @@ const agent = new Agent({
|
|
|
335
365
|
tools,
|
|
336
366
|
});
|
|
337
367
|
|
|
338
|
-
// Listen to errors
|
|
368
|
+
// Listen to errors and control error flow
|
|
339
369
|
agent.on(events.ERROR, async ({ error, state }) => {
|
|
340
370
|
console.error('Agent error occurred:', error.message);
|
|
341
371
|
console.error('Error stack:', error.stack);
|
|
342
372
|
console.log('Session ID:', state.sessionId);
|
|
343
373
|
console.log('Current memory:', state.memory);
|
|
344
|
-
console.log('Messages at error:', state.messages.length);
|
|
345
374
|
|
|
346
375
|
// Log to external service
|
|
347
376
|
await logger.error({
|
|
@@ -352,26 +381,49 @@ agent.on(events.ERROR, async ({ error, state }) => {
|
|
|
352
381
|
memory: state.memory,
|
|
353
382
|
});
|
|
354
383
|
|
|
355
|
-
//
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
384
|
+
// Handle recoverable vs non-recoverable errors
|
|
385
|
+
if (error.message.includes('rate limit')) {
|
|
386
|
+
// Recoverable error - update state and continue
|
|
387
|
+
return {
|
|
388
|
+
throwError: false, // Don't stop the agent
|
|
389
|
+
state: {
|
|
390
|
+
memory: {
|
|
391
|
+
...state.memory,
|
|
392
|
+
errorRecovered: true,
|
|
393
|
+
lastError: error.message,
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Critical error - stop the agent
|
|
400
|
+
if (error.message.includes('authentication failed')) {
|
|
401
|
+
await notificationService.alert({
|
|
402
|
+
type: 'critical_error',
|
|
403
|
+
error: error.message,
|
|
404
|
+
sessionId: state.sessionId,
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
throwError: true, // Stop the agent run
|
|
409
|
+
};
|
|
410
|
+
}
|
|
361
411
|
|
|
362
|
-
//
|
|
363
|
-
|
|
412
|
+
// Default: let the error be handled normally
|
|
413
|
+
return { throwError: false };
|
|
364
414
|
});
|
|
365
415
|
```
|
|
366
416
|
|
|
367
417
|
### Common Use Cases
|
|
368
418
|
|
|
369
419
|
- **Error Logging**: Record errors for debugging and monitoring with full context
|
|
420
|
+
- **Error Recovery**: Implement custom recovery logic by returning `throwError: false` and updating state
|
|
421
|
+
- **Critical Error Handling**: Stop agent execution for unrecoverable errors with `throwError: true`
|
|
370
422
|
- **User Notifications**: Provide graceful error messages to users
|
|
371
423
|
- **Monitoring & Alerting**: Integrate with monitoring systems for error tracking
|
|
372
|
-
- **Error Recovery**: Implement custom recovery logic or fallback behaviors
|
|
373
424
|
- **Session Management**: Clean up or reset sessions that encountered errors
|
|
374
425
|
- **Analytics**: Track error patterns and frequencies for system improvement
|
|
426
|
+
- **Graceful Degradation**: Continue execution with fallback behavior for non-critical errors
|
|
375
427
|
|
|
376
428
|
## ON_LOGICAL_CONDITION
|
|
377
429
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@minded-ai/mindedjs",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.141",
|
|
4
4
|
"description": "MindedJS is a TypeScript library for building agents.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -67,4 +67,4 @@
|
|
|
67
67
|
"zod": "^3.25.74",
|
|
68
68
|
"zod-to-json-schema": "^3.24.6"
|
|
69
69
|
}
|
|
70
|
-
}
|
|
70
|
+
}
|
package/src/agent.ts
CHANGED
|
@@ -26,9 +26,9 @@ import { AgentInvokeParams, MindedSDKConfig, SessionType } from './types/Agent.t
|
|
|
26
26
|
import { createLlmInstance } from './llm/createLlmInstance';
|
|
27
27
|
import { createCheckpointSaver } from './checkpointer/checkpointSaverFactory';
|
|
28
28
|
import { getConfig } from './platform/config';
|
|
29
|
-
import { InterruptSessionManager, InterruptType } from './interrupts/BaseInterruptSessionManager';
|
|
29
|
+
import { InterruptPayload, InterruptSessionManager, InterruptType } from './interrupts/BaseInterruptSessionManager';
|
|
30
30
|
import { createInterruptSessionManager } from './interrupts/interruptSessionManagerFactory';
|
|
31
|
-
import {
|
|
31
|
+
import { HumanMessage } from '@langchain/core/messages';
|
|
32
32
|
import triggerTypeToDefaultMessage from './triggers/triggerTypeToDefaultMessage';
|
|
33
33
|
import appActionRunnerToolCreator from './internalTools/appActionRunnerTool';
|
|
34
34
|
import { VoiceSession } from './voice/voiceSession';
|
|
@@ -417,96 +417,105 @@ export class Agent {
|
|
|
417
417
|
return;
|
|
418
418
|
}
|
|
419
419
|
// Session lock acquired, proceed with processing
|
|
420
|
-
|
|
421
|
-
let messages: Array<BaseMessage> = [];
|
|
422
|
-
let memoryUpdate = {};
|
|
423
|
-
let sessionType: SessionType = SessionType.TEXT;
|
|
424
|
-
|
|
425
420
|
const langraphConfig = this.getLangraphConfig(sessionId);
|
|
421
|
+
const state = await this.compiledGraph.getState(langraphConfig);
|
|
426
422
|
|
|
427
|
-
|
|
423
|
+
// Initialize state update with defaults
|
|
424
|
+
let stateUpdate: Partial<State<z.infer<typeof this.memorySchema>>> = {
|
|
425
|
+
sessionId,
|
|
426
|
+
sessionType: SessionType.TEXT,
|
|
427
|
+
};
|
|
428
428
|
|
|
429
|
+
// Process special trigger types (dashboard/voice messages)
|
|
429
430
|
if (triggerName === KnownTriggerNames.DASHBOARD_MESSAGE || triggerName === KnownTriggerNames.VOICE_MESSAGE) {
|
|
430
|
-
// Parse attachments
|
|
431
|
+
// Parse attachments and create message
|
|
431
432
|
const attachmentsString = parseAttachments(triggerBody);
|
|
432
433
|
const finalContent = combineContentWithAttachments(triggerBody.content || '', attachmentsString);
|
|
433
434
|
if (finalContent) {
|
|
434
|
-
messages = [new HumanMessage({ content: finalContent, id: uuidv4() })];
|
|
435
|
+
stateUpdate.messages = [new HumanMessage({ content: finalContent, id: uuidv4() })];
|
|
435
436
|
}
|
|
436
437
|
triggerBody.content = finalContent;
|
|
437
|
-
sessionType = triggerName === KnownTriggerNames.VOICE_MESSAGE ? SessionType.VOICE : SessionType.TEXT;
|
|
438
|
+
stateUpdate.sessionType = triggerName === KnownTriggerNames.VOICE_MESSAGE ? SessionType.VOICE : SessionType.TEXT;
|
|
438
439
|
}
|
|
439
440
|
|
|
440
|
-
//
|
|
441
|
+
// Merge current state with any updates we've made so far
|
|
442
|
+
const currentState = { ...state.values, ...stateUpdate };
|
|
443
|
+
|
|
444
|
+
// Emit trigger event and let handlers modify the state
|
|
441
445
|
const results = await this.emit(AgentEvents.TRIGGER_EVENT, {
|
|
442
446
|
triggerName,
|
|
443
447
|
triggerBody,
|
|
444
|
-
state:
|
|
448
|
+
state: currentState,
|
|
445
449
|
});
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
} else {
|
|
455
|
-
messages = messages.length > 0 ? messages : [new HumanMessage({ content: JSON.stringify(triggerBody), id: uuidv4() })];
|
|
450
|
+
|
|
451
|
+
// Process handler results
|
|
452
|
+
const handlerResult = results.find((r) => r !== undefined);
|
|
453
|
+
if (handlerResult) {
|
|
454
|
+
if (!handlerResult.isQualified) {
|
|
455
|
+
logger.debug({ message: '[Trigger] Disqualified', triggerName, triggerBody, sessionId });
|
|
456
|
+
await this.interruptSessionManager.release(sessionId);
|
|
457
|
+
return;
|
|
456
458
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
messages =
|
|
459
|
+
// Merge handler's state updates
|
|
460
|
+
if (handlerResult.state) {
|
|
461
|
+
stateUpdate = { ...stateUpdate, ...handlerResult.state };
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// If no messages were set by handler or initial processing, create default message
|
|
466
|
+
if (!stateUpdate.messages || stateUpdate.messages.length === 0) {
|
|
467
|
+
if (appName && triggerTypeToDefaultMessage[appName]?.[triggerName]) {
|
|
468
|
+
stateUpdate.messages = triggerTypeToDefaultMessage[appName][triggerName](triggerBody);
|
|
469
|
+
} else {
|
|
470
|
+
stateUpdate.messages = [new HumanMessage({ content: JSON.stringify(triggerBody), id: uuidv4() })];
|
|
467
471
|
}
|
|
468
472
|
}
|
|
469
473
|
|
|
470
|
-
logger.info({ msg: '[Trigger] Received', triggerName, sessionId });
|
|
471
|
-
state = await this.compiledGraph.getState(langraphConfig);
|
|
474
|
+
logger.info({ msg: '[Trigger] Received', triggerName, triggerBody, sessionId });
|
|
472
475
|
|
|
473
476
|
// 2) Decide how to invoke: resume/update/goto/normal
|
|
474
|
-
let nodeToBeInvoked = state.next
|
|
477
|
+
let nodeToBeInvoked = state.next.length > 0 ? (state.values.goto ? state.values.goto : state.next[0]) : this.startingNodeId;
|
|
475
478
|
if (!nodeToBeInvoked) throw new Error('No node to be invoked');
|
|
476
479
|
|
|
477
480
|
const suffixes = Object.values(internalNodesSuffix);
|
|
478
481
|
nodeToBeInvoked = nodeToBeInvoked.replace(new RegExp(suffixes.join('|'), 'g'), '');
|
|
479
482
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
);
|
|
483
|
+
// Create a history step if not provided by the handler
|
|
484
|
+
if (!stateUpdate.history) {
|
|
485
|
+
const messageIds = stateUpdate.messages?.map((m) => m.id!) || [];
|
|
486
|
+
const historyStep = createTriggerHistoryStep(state.values.history, nodeToBeInvoked, messageIds, triggerName, triggerBody, appName);
|
|
487
|
+
// History reducer expects an array or single item
|
|
488
|
+
stateUpdate.history = [historyStep];
|
|
489
|
+
}
|
|
488
490
|
|
|
489
491
|
// 3) Handle interrupts/goto, else normal invoke
|
|
490
492
|
let res;
|
|
491
493
|
const lastTask = state.tasks[state.tasks.length - 1];
|
|
492
494
|
const hasInterrupt = lastTask?.interrupts?.length > 0;
|
|
493
495
|
if (hasInterrupt) {
|
|
494
|
-
const interruptValue =
|
|
495
|
-
if (interruptValue
|
|
496
|
-
// For HUMAN_IN_THE_LOOP, use resume with the
|
|
496
|
+
const interruptValue = lastTask!.interrupts[0].value as InterruptPayload;
|
|
497
|
+
if (interruptValue.type === InterruptType.HUMAN_IN_THE_LOOP) {
|
|
498
|
+
// For HUMAN_IN_THE_LOOP, use resume with the state update
|
|
497
499
|
res = await this.compiledGraph.invoke(
|
|
498
500
|
new Command({
|
|
499
|
-
resume: {
|
|
501
|
+
resume: { ...stateUpdate, goto: null },
|
|
500
502
|
}),
|
|
501
503
|
langraphConfig,
|
|
502
504
|
);
|
|
503
505
|
} else if (interruptValue?.type === InterruptType.NEW_TRIGGERS) {
|
|
504
|
-
// For NEW_TRIGGERS,
|
|
505
|
-
const finalState = {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
506
|
+
// For NEW_TRIGGERS, merge with updateStateObject if present
|
|
507
|
+
const finalState: any = { ...stateUpdate };
|
|
508
|
+
const updatedInterruptState = interruptValue.updateStateObject;
|
|
509
|
+
if (updatedInterruptState) {
|
|
510
|
+
// Merge messages and history using the reducers' logic
|
|
511
|
+
finalState.messages = [...(updatedInterruptState.messages || []), ...(stateUpdate.messages || [])];
|
|
512
|
+
finalState.history = [...(updatedInterruptState.history || []), ...(stateUpdate.history || [])];
|
|
513
|
+
// Merge other fields from updateStateObject
|
|
514
|
+
Object.keys(updatedInterruptState).forEach((key) => {
|
|
515
|
+
if (key !== 'messages' && key !== 'history') {
|
|
516
|
+
finalState[key] = updatedInterruptState[key as keyof typeof updatedInterruptState];
|
|
517
|
+
}
|
|
518
|
+
});
|
|
510
519
|
}
|
|
511
520
|
|
|
512
521
|
// Then use update with the full object and empty resume
|
|
@@ -518,26 +527,19 @@ export class Agent {
|
|
|
518
527
|
langraphConfig,
|
|
519
528
|
);
|
|
520
529
|
}
|
|
521
|
-
} else if (state.values.
|
|
530
|
+
} else if (state.values.goto) {
|
|
522
531
|
res = await this.compiledGraph.invoke(
|
|
523
532
|
new Command({
|
|
524
533
|
update: {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
memory: memoryUpdate,
|
|
528
|
-
history: historyStep,
|
|
529
|
-
sessionId,
|
|
530
|
-
sessionType,
|
|
534
|
+
...stateUpdate,
|
|
535
|
+
goto: null, //reset the goto
|
|
531
536
|
},
|
|
532
|
-
goto: state.values.
|
|
537
|
+
goto: state.values.goto,
|
|
533
538
|
}),
|
|
534
539
|
langraphConfig,
|
|
535
540
|
);
|
|
536
541
|
} else {
|
|
537
|
-
res = await this.compiledGraph.invoke(
|
|
538
|
-
{ messages, memory: memoryUpdate, history: historyStep, sessionId, sessionType },
|
|
539
|
-
langraphConfig,
|
|
540
|
-
);
|
|
542
|
+
res = await this.compiledGraph.invoke(stateUpdate, langraphConfig);
|
|
541
543
|
}
|
|
542
544
|
const nextMessage = await this.interruptSessionManager.dequeue(sessionId);
|
|
543
545
|
if (nextMessage) {
|
|
@@ -566,8 +568,24 @@ export class Agent {
|
|
|
566
568
|
await this.interruptSessionManager.release(sessionId);
|
|
567
569
|
|
|
568
570
|
const state = await this.compiledGraph.getState(this.getLangraphConfig(sessionId));
|
|
569
|
-
this.emit(AgentEvents.ERROR, {
|
|
570
|
-
|
|
571
|
+
const results = await this.emit(AgentEvents.ERROR, {
|
|
572
|
+
error: err instanceof Error ? err : new Error(JSON.stringify(err)),
|
|
573
|
+
state: state.values,
|
|
574
|
+
});
|
|
575
|
+
const handlerResult = results.find((r) => r !== undefined);
|
|
576
|
+
let throwError = handlerResult === undefined;
|
|
577
|
+
if (handlerResult) {
|
|
578
|
+
// Merge handler's state updates
|
|
579
|
+
if (handlerResult.state) {
|
|
580
|
+
handlerResult.state = { ...state.values, ...handlerResult.state };
|
|
581
|
+
await this.compiledGraph.updateState(this.getLangraphConfig(sessionId), handlerResult.state);
|
|
582
|
+
}
|
|
583
|
+
// If one handler returns throwError, we throw the error
|
|
584
|
+
throwError = throwError || handlerResult.throwError;
|
|
585
|
+
}
|
|
586
|
+
if (throwError) {
|
|
587
|
+
throw err;
|
|
588
|
+
}
|
|
571
589
|
}
|
|
572
590
|
}
|
|
573
591
|
|
|
@@ -801,12 +819,19 @@ export class Agent {
|
|
|
801
819
|
|
|
802
820
|
this.voiceSessions.set(params.sessionId, voiceSession);
|
|
803
821
|
|
|
822
|
+
const state = await this.compiledGraph.getState(this.getLangraphConfig(params.sessionId));
|
|
823
|
+
|
|
804
824
|
// Emit voice session start event
|
|
805
|
-
await this.emit(AgentEvents.VOICE_SESSION_START, {
|
|
806
|
-
sessionId: params.sessionId,
|
|
825
|
+
const results = await this.emit(AgentEvents.VOICE_SESSION_START, {
|
|
807
826
|
metadata: params.metadata,
|
|
827
|
+
state: state.values as State<z.infer<typeof this.memorySchema>>,
|
|
808
828
|
});
|
|
809
829
|
|
|
830
|
+
const handlerResult = results.find((r) => r !== undefined);
|
|
831
|
+
if (handlerResult && handlerResult.state) {
|
|
832
|
+
await this.compiledGraph.updateState(this.getLangraphConfig(params.sessionId), handlerResult.state);
|
|
833
|
+
}
|
|
834
|
+
|
|
810
835
|
return voiceSession;
|
|
811
836
|
}
|
|
812
837
|
|
|
@@ -852,6 +877,7 @@ export class Agent {
|
|
|
852
877
|
return { error: 'Failed to execute tool' };
|
|
853
878
|
}
|
|
854
879
|
}
|
|
880
|
+
|
|
855
881
|
public async getCurrentNode(sessionId: string) {
|
|
856
882
|
const langConfig = this.getLangraphConfig(sessionId);
|
|
857
883
|
const currentNodeId = (await this.compiledGraph.getState(langConfig))?.tasks?.[0]?.name;
|
|
@@ -13,7 +13,7 @@ export const createLogicalRouter = ({ edges, agent }: { edges: LogicalConditionE
|
|
|
13
13
|
logger.debug({ msg: `[Router] Evaluating logical conditions for ${edges.length} edges` });
|
|
14
14
|
|
|
15
15
|
if (state.goto) {
|
|
16
|
-
|
|
16
|
+
logger.debug({ msg: `[Router] Jumping to node`, node: state.goto });
|
|
17
17
|
return state.goto;
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -30,12 +30,17 @@ export const createLogicalRouter = ({ edges, agent }: { edges: LogicalConditionE
|
|
|
30
30
|
logger.debug({ msg: '[LogicalCondition] Evaluating condition', edge });
|
|
31
31
|
|
|
32
32
|
// Emit ON_LOGICAL_CONDITION event
|
|
33
|
-
await agent.emit(AgentEvents.ON_LOGICAL_CONDITION, {
|
|
33
|
+
const onLogicalConditionRes = await agent.emit(AgentEvents.ON_LOGICAL_CONDITION, {
|
|
34
34
|
edge,
|
|
35
35
|
state,
|
|
36
36
|
condition: edge.condition,
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
+
const handlerResult = onLogicalConditionRes.find((r) => r !== undefined);
|
|
40
|
+
if (handlerResult && handlerResult.state) {
|
|
41
|
+
state = { ...state, ...handlerResult.state };
|
|
42
|
+
}
|
|
43
|
+
|
|
39
44
|
// Customer is responsible for providing valid JavaScript syntax
|
|
40
45
|
// We execute their condition in a sandboxed VM with timeout protection
|
|
41
46
|
const conditionCode = `
|
|
@@ -90,7 +95,7 @@ export const createLogicalRouter = ({ edges, agent }: { edges: LogicalConditionE
|
|
|
90
95
|
});
|
|
91
96
|
|
|
92
97
|
// Emit ON_LOGICAL_CONDITION_RESULT event
|
|
93
|
-
await agent.emit(AgentEvents.ON_LOGICAL_CONDITION_RESULT, {
|
|
98
|
+
const onLogicalConditionResultRes = await agent.emit(AgentEvents.ON_LOGICAL_CONDITION_RESULT, {
|
|
94
99
|
edge,
|
|
95
100
|
state,
|
|
96
101
|
condition: edge.condition,
|
|
@@ -98,6 +103,21 @@ export const createLogicalRouter = ({ edges, agent }: { edges: LogicalConditionE
|
|
|
98
103
|
executionTimeMs: executionTime,
|
|
99
104
|
});
|
|
100
105
|
|
|
106
|
+
let goto = null;
|
|
107
|
+
const onLogicalConditionResultHandlerResult = onLogicalConditionResultRes.find((r) => r !== undefined);
|
|
108
|
+
if (onLogicalConditionResultHandlerResult && onLogicalConditionResultHandlerResult.state) {
|
|
109
|
+
state = { ...state, ...onLogicalConditionResultHandlerResult.state };
|
|
110
|
+
goto = state.goto;
|
|
111
|
+
state.goto = null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
await agent.updateState({ sessionId: state.sessionId, state });
|
|
115
|
+
|
|
116
|
+
if (goto) {
|
|
117
|
+
logger.debug({ msg: `[Router] Jumping to node`, node: goto });
|
|
118
|
+
return goto;
|
|
119
|
+
}
|
|
120
|
+
|
|
101
121
|
if (booleanResult) {
|
|
102
122
|
if (edge.source == edge.target) {
|
|
103
123
|
logger.info({ msg: `[Router] Stay at node ${edge.source}`, node: edge.source, condition: edge.condition });
|
|
@@ -128,7 +148,7 @@ export const createLogicalRouter = ({ edges, agent }: { edges: LogicalConditionE
|
|
|
128
148
|
});
|
|
129
149
|
|
|
130
150
|
// Emit ON_LOGICAL_CONDITION_RESULT event with error
|
|
131
|
-
await agent.emit(AgentEvents.ON_LOGICAL_CONDITION_RESULT, {
|
|
151
|
+
const onLogicalConditionResultRes = await agent.emit(AgentEvents.ON_LOGICAL_CONDITION_RESULT, {
|
|
132
152
|
edge,
|
|
133
153
|
state,
|
|
134
154
|
condition: edge.condition,
|
|
@@ -137,8 +157,20 @@ export const createLogicalRouter = ({ edges, agent }: { edges: LogicalConditionE
|
|
|
137
157
|
error: errorObj,
|
|
138
158
|
});
|
|
139
159
|
|
|
160
|
+
const onLogicalConditionResultHandlerResult = onLogicalConditionResultRes.find((r) => r !== undefined);
|
|
161
|
+
let throwError = false;
|
|
162
|
+
if (onLogicalConditionResultHandlerResult && onLogicalConditionResultHandlerResult.state) {
|
|
163
|
+
state = { ...state, ...onLogicalConditionResultHandlerResult.state };
|
|
164
|
+
}
|
|
165
|
+
throwError = onLogicalConditionResultHandlerResult?.throwError == true;
|
|
166
|
+
|
|
167
|
+
await agent.updateState({ sessionId: state.sessionId, state });
|
|
168
|
+
|
|
140
169
|
// Continue to next edge instead of failing completely
|
|
141
170
|
// This allows other conditions to be evaluated even if one fails
|
|
171
|
+
if (throwError) {
|
|
172
|
+
throw errorObj;
|
|
173
|
+
}
|
|
142
174
|
continue;
|
|
143
175
|
}
|
|
144
176
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { BaseMessage } from '@langchain/core/messages';
|
|
2
|
-
import { HistoryStep } from '../types/Agent.types';
|
|
3
1
|
import { State } from '../types/LangGraph.types';
|
|
4
2
|
import { LogicalConditionEdge } from '../types/Flows.types';
|
|
3
|
+
import { TriggerEvent } from '../types/Agent.types';
|
|
5
4
|
|
|
6
5
|
export enum AgentEvents {
|
|
7
6
|
AI_MESSAGE = 'AI_MESSAGE',
|
|
@@ -17,18 +16,16 @@ export type AgentEventRequestPayloads<Memory> = {
|
|
|
17
16
|
message: string;
|
|
18
17
|
state: State<Memory>;
|
|
19
18
|
};
|
|
20
|
-
[AgentEvents.TRIGGER_EVENT]: {
|
|
21
|
-
triggerName: string;
|
|
22
|
-
triggerBody: any;
|
|
19
|
+
[AgentEvents.TRIGGER_EVENT]: TriggerEvent & {
|
|
23
20
|
state: State<Memory>;
|
|
24
21
|
};
|
|
25
22
|
[AgentEvents.VOICE_SESSION_START]: {
|
|
26
|
-
sessionId: string;
|
|
27
23
|
metadata?: Record<string, any>;
|
|
24
|
+
state: State<Memory>;
|
|
28
25
|
};
|
|
29
26
|
[AgentEvents.ERROR]: {
|
|
30
27
|
error: Error;
|
|
31
|
-
state
|
|
28
|
+
state: State<Memory>;
|
|
32
29
|
};
|
|
33
30
|
[AgentEvents.ON_LOGICAL_CONDITION]: {
|
|
34
31
|
edge: LogicalConditionEdge;
|
|
@@ -46,16 +43,25 @@ export type AgentEventRequestPayloads<Memory> = {
|
|
|
46
43
|
};
|
|
47
44
|
|
|
48
45
|
export type AgentEventResponsePayloads<Memory> = {
|
|
49
|
-
[AgentEvents.AI_MESSAGE]:
|
|
46
|
+
[AgentEvents.AI_MESSAGE]: {
|
|
47
|
+
state?: Partial<State<Memory>>;
|
|
48
|
+
};
|
|
50
49
|
[AgentEvents.TRIGGER_EVENT]: {
|
|
51
50
|
isQualified: boolean;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
};
|
|
57
|
-
[AgentEvents.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
51
|
+
state?: Partial<State<Memory>>;
|
|
52
|
+
};
|
|
53
|
+
[AgentEvents.VOICE_SESSION_START]: {
|
|
54
|
+
state?: Partial<State<Memory>>;
|
|
55
|
+
};
|
|
56
|
+
[AgentEvents.ERROR]: {
|
|
57
|
+
throwError: boolean;
|
|
58
|
+
state?: Partial<State<Memory>>;
|
|
59
|
+
};
|
|
60
|
+
[AgentEvents.ON_LOGICAL_CONDITION]: {
|
|
61
|
+
state?: Partial<State<Memory>>;
|
|
62
|
+
};
|
|
63
|
+
[AgentEvents.ON_LOGICAL_CONDITION_RESULT]: {
|
|
64
|
+
state?: Partial<State<Memory>>;
|
|
65
|
+
throwError?: boolean;
|
|
66
|
+
};
|
|
61
67
|
};
|
package/src/index.ts
CHANGED
|
@@ -75,6 +75,7 @@ export {
|
|
|
75
75
|
AppTriggerHistoryStep,
|
|
76
76
|
CustomActionInvocationHistoryStep,
|
|
77
77
|
SessionType,
|
|
78
|
+
TriggerEvent,
|
|
78
79
|
} from './types/Agent.types';
|
|
79
80
|
export type { AgentInvokeParams, MindedSDKConfig } from './types/Agent.types';
|
|
80
81
|
export type { Environment } from './types/Platform.types';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { State } from '../types/LangGraph.types';
|
|
2
2
|
import { GraphInterrupt } from '@langchain/langgraph';
|
|
3
3
|
import { logger } from '../utils/logger';
|
|
4
|
+
import { TriggerEvent } from '../types/Agent.types';
|
|
4
5
|
|
|
5
6
|
export enum InterruptType {
|
|
6
7
|
NEW_TRIGGERS = 'NEW_TRIGGERS',
|
|
@@ -9,13 +10,10 @@ export enum InterruptType {
|
|
|
9
10
|
|
|
10
11
|
export const QUEUE_INTERRUPT_DETECTED_BEFORE_SPEECH = 'QUEUE_INTERRUPT_DETECTED_BEFORE_SPEECH';
|
|
11
12
|
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
triggerName: string;
|
|
15
|
-
appName?: string;
|
|
16
|
-
}
|
|
13
|
+
export type QueuedMessage = TriggerEvent;
|
|
14
|
+
|
|
17
15
|
export interface InterruptPayload {
|
|
18
|
-
type: InterruptType
|
|
16
|
+
type: InterruptType;
|
|
19
17
|
updateStateObject?: Partial<State>;
|
|
20
18
|
id?: string;
|
|
21
19
|
}
|