jsfe 0.8.0 → 0.9.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/README.md CHANGED
@@ -34,17 +34,20 @@ const engine = new WorkflowEngine(
34
34
  );
35
35
 
36
36
  // 2. Initialize a session for each user
37
- const sessionContext = engine.initSession(yourLogger, 'user-123', 'session-456');
37
+ let sessionContext = engine.initSession(yourLogger, 'user-123', 'session-456');
38
38
 
39
- // 3. Plug-in the engine to process User message
39
+ // 3. Process user message and update session context
40
40
  const userEntry = {
41
41
  role: 'user',
42
42
  content: 'I need help with my account',
43
43
  };
44
- const result = await engine.updateActivity(userEntry, sessionContext);
45
- if (result) {
46
- // Intent detected and handled no need to proceed to normal response generation
47
- return result;
44
+ const updatedSessionContext = await engine.updateActivity(userEntry, sessionContext);
45
+ sessionContext = updatedSessionContext; // CRITICAL: Always update your session reference
46
+
47
+ if (updatedSessionContext.response) {
48
+ // Intent detected and handled - return the workflow response
49
+ // If flow activated you should not proceed to your normal handling
50
+ return updatedSessionContext.response;
48
51
  }
49
52
 
50
53
  // 4. Call your normal Generate reply process as usual
@@ -53,9 +56,9 @@ const reply = await yourConversationalReply(input);
53
56
  // 5. Update the engine's context with the generated reply
54
57
  const assistantEntry = {
55
58
  role: 'assistant',
56
- content: 'I can help you with your account. What specific issue are you experiencing?',
59
+ content: reply, // The reply generated by your process
57
60
  };
58
- await engine.updateActivity(assistantEntry, sessionContext);
61
+ sessionContext = await engine.updateActivity(assistantEntry, sessionContext);
59
62
 
60
63
  // Return the generated reply to the user
61
64
  return reply;
@@ -91,10 +94,10 @@ The WorkflowEngine constructor accepts the following parameters in order, each s
91
94
  - **Validation**: Parameter schemas validated against OpenAI Function Calling Standard
92
95
  - **Security**: Tools define their own security levels and authentication requirements
93
96
 
94
- **5. APPROVED_FUNCTIONS** (Map<string, Function>)
97
+ **5. APPROVED_FUNCTIONS** (Object)
95
98
  - **Purpose**: Secure registry of pre-approved local JavaScript functions
96
- - **Security**: Only functions in this map can be executed by local-type tools
97
- - **Format**: `Map` where keys are function names and values are the actual functions
99
+ - **Security**: Only functions in this object can be executed by local-type tools
100
+ - **Format**: Plain object where keys are function names and values are the actual functions
98
101
  - **Validation**: Functions must match tool definitions in toolsRegistry
99
102
 
100
103
  **6. globalVariables** (Record<string, unknown>, optional)
@@ -102,6 +105,31 @@ The WorkflowEngine constructor accepts the following parameters in order, each s
102
105
  - **Scope**: Available to all flows in the session via variable interpolation
103
106
  - **Security**: Safe sharing of host application data with workflows
104
107
  - **Examples**: User ID, session ID, application configuration, environmental data
108
+ - **Nature**: **STATIC** - Set during engine initialization, same for all sessions
109
+
110
+ ### Dynamic Session Data: The `cargo` Property
111
+
112
+ Unlike `globalVariables` which are static and shared across all sessions, each `EngineSessionContext` has a `cargo` property for **dynamic, session-specific data sharing**:
113
+
114
+ ```javascript
115
+ // After initSession, you can set dynamic session data
116
+ let sessionContext = engine.initSession(logger, 'user-123', 'session-456');
117
+
118
+ // Set dynamic data that workflows can access
119
+ sessionContext.cargo.userPreferences = { theme: 'dark', language: 'en' };
120
+ sessionContext.cargo.currentOrder = { id: 'ORD-123', total: 99.99 };
121
+ sessionContext.cargo.temporaryData = 'Any dynamic content';
122
+
123
+ // Workflows can reference cargo data with variable interpolation
124
+ // Example in flow step: "Your order {{cargo.currentOrder.id}} total is ${{cargo.currentOrder.total}}"
125
+ ```
126
+
127
+ **Key Differences: `globalVariables` vs `cargo`**
128
+ - **globalVariables**: Static, engine-wide, set at initialization, same for all sessions
129
+ - **cargo**: Dynamic, session-specific, can be modified anytime during conversation
130
+ - **Use Cases**:
131
+ - `globalVariables`: API keys, system config, app metadata
132
+ - `cargo`: User data, conversation state, temporary values, personalization
105
133
 
106
134
  **7. validateOnInit** (boolean, optional)
107
135
  - **Purpose**: Enable comprehensive flow and tool validation during initialization
@@ -254,7 +282,7 @@ const toolsRegistry = [
254
282
 
255
283
  #### 3. **Approved Functions Registry** - Secure Local Functions
256
284
  ```javascript
257
- const APPROVED_FUNCTIONS = new Map();
285
+ const APPROVED_FUNCTIONS = {};
258
286
 
259
287
  // Define secure local functions
260
288
  async function processPayment(args) {
@@ -263,7 +291,7 @@ async function processPayment(args) {
263
291
  }
264
292
 
265
293
  // Register approved functions
266
- APPROVED_FUNCTIONS.set('processPayment', processPayment);
294
+ APPROVED_FUNCTIONS['processPayment'] = processPayment;
267
295
  ```
268
296
 
269
297
  #### 4. **Global Variables** - Secure Sharing of Local Data
@@ -278,8 +306,62 @@ const globalVariables = {
278
306
  ### Session Management
279
307
 
280
308
  - Each user requires a unique session context via `initSession(logger, userId, sessionId)`
309
+ - **CRITICAL**: `updateActivity()` returns an updated `EngineSessionContext` - you must always update your session reference
281
310
  - The `EngineSessionContext` object should be persisted by your application
282
311
  - Pass the same session context to `updateActivity` for conversation continuity
312
+ - **Session Isolation**: Each user/session must have its own context to prevent state contamination
313
+
314
+ #### Session Cargo for Dynamic Data
315
+
316
+ Each session context includes a `cargo` property for dynamic, session-specific data that workflows can access:
317
+
318
+ ```javascript
319
+ // Initialize session
320
+ let sessionContext = engine.initSession(logger, 'user-123', 'session-456');
321
+
322
+ // Set dynamic session data that workflows can reference
323
+ sessionContext.cargo.userProfile = {
324
+ name: 'John Doe',
325
+ tier: 'premium',
326
+ preferences: { notifications: true }
327
+ };
328
+
329
+ // Workflows can access cargo data: "Welcome {{cargo.userProfile.name}}!"
330
+ sessionContext = await engine.updateActivity(userEntry, sessionContext);
331
+ ```
332
+
333
+ #### Correct Session Update Pattern
334
+
335
+ ```javascript
336
+ // Initialize session
337
+ let sessionContext = engine.initSession(logger, 'user-123', 'session-456');
338
+
339
+ // For every updateActivity call, capture the returned context
340
+ sessionContext = await engine.updateActivity(userEntry, sessionContext);
341
+
342
+ // Check for workflow response
343
+ if (sessionContext.response) {
344
+ return sessionContext.response;
345
+ }
346
+
347
+
348
+ // Continue with regular conversation...
349
+ sessionContext = await engine.updateActivity(assistantEntry, sessionContext);
350
+ ```
351
+
352
+ #### ❌ Common Mistake - Not Updating Session Reference
353
+ ```javascript
354
+ // WRONG - This will cause session corruption
355
+ const sessionContext = engine.initSession(logger, 'user-123', 'session-456');
356
+ await engine.updateActivity(userEntry, sessionContext); // Session state lost!
357
+ ```
358
+
359
+ #### ✅ Correct Pattern - Always Update Session Reference
360
+ ```javascript
361
+ // CORRECT - Session state maintained
362
+ let sessionContext = engine.initSession(logger, 'user-123', 'session-456');
363
+ sessionContext = await engine.updateActivity(userEntry, sessionContext); // Session updated
364
+ ```
283
365
 
284
366
  ### Context Entry Types
285
367
 
@@ -299,6 +381,63 @@ interface ContextEntry {
299
381
  }
300
382
  ```
301
383
 
384
+ ## UpdateActivity Method
385
+
386
+ The `updateActivity` method is the primary interface for processing user inputs and assistant responses. It returns an updated `EngineSessionContext` that must be captured and used for subsequent calls.
387
+
388
+ ### Method Signature
389
+
390
+ ```typescript
391
+ async updateActivity(
392
+ contextEntry: ContextEntry,
393
+ engineSessionContext: EngineSessionContext
394
+ ): Promise<EngineSessionContext>
395
+ ```
396
+
397
+ ### Return Value
398
+
399
+ The method returns an updated `EngineSessionContext` containing:
400
+
401
+ - **Updated flow state**: Modified flow stacks, variables, and execution context
402
+ - **Response data**: If a workflow was triggered, `sessionContext.response` contains the result
403
+ - **Session metadata**: Updated timestamps, accumulated messages, and transaction data
404
+
405
+ ### Integration Pattern
406
+
407
+ ```javascript
408
+ // Process user input
409
+ let sessionContext = await engine.updateActivity({
410
+ role: 'user',
411
+ content: userMessage,
412
+ timestamp: Date.now()
413
+ }, sessionContext);
414
+
415
+ // Check if a workflow was activated
416
+ if (sessionContext.response) {
417
+ // Workflow handled the input - return the workflow response
418
+ return sessionContext.response;
419
+ }
420
+
421
+ // No workflow triggered - proceed with normal conversation
422
+ const aiResponse = await yourAIFunction(userMessage);
423
+
424
+ // Update context with AI response
425
+ sessionContext = await engine.updateActivity({
426
+ role: 'assistant',
427
+ content: aiResponse,
428
+ timestamp: Date.now()
429
+ }, sessionContext);
430
+
431
+ return aiResponse;
432
+ ```
433
+
434
+ ### Critical Notes
435
+
436
+ - **Always capture the return value**: Failing to update your session reference will cause state corruption
437
+ - **Session isolation**: Each user/conversation needs its own session context
438
+ - **Response checking**: Check `sessionContext.response` to determine if a workflow handled the input
439
+ - **Persistence**: Your application should persist the updated session context between requests
440
+
302
441
  ## Architecture Overview
303
442
 
304
443
  ### Stack-of-Stacks Design
@@ -543,6 +682,128 @@ The Flow Engine implements a sophisticated "stack-of-stacks" architecture that a
543
682
  - ✅ **FLOW** - Sub-flow execution with multiple call types
544
683
  - ✅ **SWITCH** - Enhanced conditional branching with expressions
545
684
 
685
+ ### Expression System
686
+
687
+ The JSFE engine features a **powerful, unified JavaScript expression evaluator** that provides consistent syntax and behavior across all contexts. This system supports the full range of JavaScript expressions while maintaining security through a trusted developer model.
688
+
689
+ #### Core Features
690
+ - ✅ **Full JavaScript Expression Support** - All standard operators, functions, and syntax
691
+ - ✅ **Type Preservation** - Numeric, boolean, and object types maintained correctly
692
+ - ✅ **Template Interpolation** - `{{expression}}` syntax for string templates
693
+ - ✅ **Direct Expression Evaluation** - Pure expressions return their native JavaScript types
694
+ - ✅ **Unified Security Model** - Same safety framework across all evaluation contexts
695
+
696
+ #### Expression Types
697
+
698
+ **Single Expressions** (preserve JavaScript types):
699
+ ```javascript
700
+ // SET steps with pure expressions return native types
701
+ { type: "SET", variable: "count", value: "{{attempt_count + 1}}" } // Returns number
702
+ { type: "SET", variable: "isValid", value: "{{age >= 18 && verified}}" } // Returns boolean
703
+ { type: "SET", variable: "user", value: "{{api_response.user}}" } // Returns object
704
+ ```
705
+
706
+ **Template Strings** (convert to strings for interpolation):
707
+ ```javascript
708
+ // String templates with embedded expressions
709
+ { type: "SAY", value: "Attempt {{attempt_count + 1}}/{{max_attempts}}" } // String result
710
+ { type: "SAY", value: "Welcome {{user.name}} ({{user.verified ? 'Verified' : 'Unverified'}})" }
711
+ ```
712
+
713
+ #### Supported JavaScript Features
714
+ - **Arithmetic**: `+`, `-`, `*`, `/`, `%`, `**` (exponentiation)
715
+ - **Comparison**: `===`, `!==`, `>`, `<`, `>=`, `<=`, `==`, `!=`
716
+ - **Logical**: `&&`, `||`, `!`, `??` (nullish coalescing)
717
+ - **Ternary**: `condition ? true_value : false_value`
718
+ - **Object/Array Access**: `obj.prop`, `obj['key']`, `arr[index]`
719
+ - **Method Calls**: `str.toUpperCase()`, `arr.length`, `Math.round(num)`
720
+ - **Template Literals**: `` `Hello ${name}` `` (within expressions)
721
+
722
+ #### Security Model
723
+ - **Developer Trust**: Workflows authored by developers, not end users
724
+ - **Parameter Filtering**: Only valid JavaScript identifiers allowed as parameters
725
+ - **No eval()**: Direct Function constructor with parameter validation
726
+ - **User Input Safety**: User inputs are treated as values, not code
727
+
728
+ ### Internationalization and Localization
729
+
730
+ The JSFE engine supports multiple languages through localized properties in flow definitions and steps. This allows you to create workflows that automatically display messages in the user's preferred language.
731
+
732
+ #### Flow-Level Localization
733
+
734
+ Add localized prompts to your flow definitions using the `prompt_xx` pattern:
735
+
736
+ ```javascript
737
+ const flowsMenu = [
738
+ {
739
+ id: "payment-flow",
740
+ name: "ProcessPayment",
741
+ prompt: "Process a payment", // Default (English)
742
+ prompt_es: "Procesar un pago", // Spanish
743
+ prompt_fr: "Traiter un paiement", // French
744
+ prompt_de: "Eine Zahlung bearbeiten", // German
745
+ description: "Handle payment processing",
746
+ steps: [ /* ... */ ]
747
+ }
748
+ ];
749
+ ```
750
+
751
+ #### Step-Level Localization
752
+
753
+ Add localized messages to individual steps using the `value_xx` pattern:
754
+
755
+ ```javascript
756
+ {
757
+ type: "SAY-GET",
758
+ variable: "amount",
759
+ value: "Please enter the payment amount:", // Default (English)
760
+ value_es: "Por favor ingrese el monto del pago:", // Spanish
761
+ value_fr: "Veuillez saisir le montant du paiement:", // French
762
+ value_de: "Bitte geben Sie den Zahlungsbetrag ein:" // German
763
+ }
764
+ ```
765
+
766
+ #### Language Selection
767
+
768
+ Set the user's preferred language when initializing the engine:
769
+
770
+ ```javascript
771
+ const engine = new WorkflowEngine(
772
+ logger,
773
+ aiCallback,
774
+ flowsMenu,
775
+ toolsRegistry,
776
+ APPROVED_FUNCTIONS,
777
+ globalVariables,
778
+ true, // validateOnInit
779
+ 'es', // language - Spanish
780
+ messageRegistry,
781
+ guidanceConfig
782
+ );
783
+ ```
784
+
785
+ #### Supported Language Codes
786
+
787
+ - `en` - English (default)
788
+ - `es` - Spanish (Español)
789
+ - `pt` - Portuguese (Português)
790
+ - `fr` - French (Français)
791
+ - `de` - German (Deutsch)
792
+ - Any ISO 639-1 language code
793
+
794
+ #### Language Fallback
795
+
796
+ If a localized property is not found for the specified language, the engine automatically falls back to the default `prompt` or `value` property. This ensures your flows always work even if some translations are missing.
797
+
798
+ ```javascript
799
+ // Example: User has language='es' but only English is available
800
+ {
801
+ type: "SAY",
802
+ value: "Welcome to our service!" // Will be used as fallback
803
+ // value_es is missing, so English value is used
804
+ }
805
+ ```
806
+
546
807
  ### Expression Template System
547
808
 
548
809
  The engine supports safe JavaScript expressions within `{{}}` templates:
@@ -623,13 +884,61 @@ For demos, tests, or developer convenience, you can set `aiCallback` to `null` w
623
884
 
624
885
  This makes it easy to run demos and tests without requiring a real AI intent detection function. In production, always provide a real `aiCallback` for robust intent detection.
625
886
 
887
+ ### Test Suite Patterns
888
+
889
+ When creating test suites, follow these patterns to ensure session isolation and prevent state contamination:
890
+
891
+ #### Proper Test Isolation Pattern
892
+
893
+ ```javascript
894
+ // ❌ Wrong - Shared session causes contamination
895
+ const globalSession = engine.initSession(logger, 'test-user', 'test-session');
896
+
897
+ for (const testCase of testCases) {
898
+ // This will cause session corruption!
899
+ await runTest(testCase, globalSession);
900
+ }
901
+
902
+ // ✅ Correct - Fresh session per test
903
+ for (let i = 0; i < testCases.length; i++) {
904
+ const testCase = testCases[i];
905
+
906
+ // Create fresh session for each test
907
+ let sessionContext = engine.initSession(logger, 'test-user', `test-session-${i+1}`);
908
+
909
+ await runTest(testCase, sessionContext);
910
+ }
911
+ ```
912
+
913
+ #### Test Function Pattern
914
+
915
+ ```javascript
916
+ async function runTest(inputs, sessionContext) {
917
+ for (const input of inputs) {
918
+ // Always update session context
919
+ sessionContext = await engine.updateActivity({
920
+ role: 'user',
921
+ content: input,
922
+ timestamp: Date.now()
923
+ }, sessionContext);
924
+
925
+ // Check for workflow response
926
+ if (sessionContext.response) {
927
+ console.log('Workflow Response:', sessionContext.response);
928
+ }
929
+ }
930
+ }
931
+ ```
932
+
933
+ This pattern ensures that each test runs in complete isolation, preventing the session corruption that can occur when tests share state.
934
+
626
935
  ## Security & Compliance
627
936
 
628
937
  ### Expression Security
629
- - ✅ **Safe Expression Evaluation** - No eval(), no code injection
630
- - ✅ **Safe Method Allowlist** - Only pre-approved methods allowed
631
- - ✅ **Pattern-Based Security** - Block dangerous operations
632
- - ✅ **Variable Path Validation** - Secure nested property access
938
+ - ✅ **Direct JavaScript Evaluation** - Native JavaScript execution with security framework
939
+ - ✅ **Controlled Access** - Only exported variables and functions are accessable JavaScript identifiers allowed as parameters
940
+ scope
941
+ - ✅ **User Input Safety** - User inputs treated as values, with protection against code injection
633
942
 
634
943
  ### Transaction Management
635
944
  - ✅ **Comprehensive Audit Trail** - Every action logged
@@ -765,4 +1074,81 @@ The engine supports completely generic response transformation through declarati
765
1074
  }
766
1075
  }
767
1076
  }
1077
+ ```
1078
+
1079
+ ## Troubleshooting
1080
+
1081
+ ### Common Session Management Issues
1082
+
1083
+ #### Problem: "engineSessionContext.flowStacks was invalid, initialized fresh flowStacks"
1084
+
1085
+ **Cause**: Session context corruption due to improper session management.
1086
+
1087
+ **Solutions**:
1088
+ 1. **Always update session reference**:
1089
+ ```javascript
1090
+ // ❌ Wrong
1091
+ await engine.updateActivity(entry, sessionContext);
1092
+
1093
+ // ✅ Correct
1094
+ sessionContext = await engine.updateActivity(entry, sessionContext);
1095
+ ```
1096
+
1097
+ 2. **Use unique sessions per user/conversation**:
1098
+ ```javascript
1099
+ // ❌ Wrong - sharing sessions
1100
+ const sharedSession = engine.initSession(logger, 'shared', 'shared');
1101
+
1102
+ // ✅ Correct - isolated sessions
1103
+ const userSession = engine.initSession(logger, `user-${userId}`, `session-${sessionId}`);
1104
+ ```
1105
+
1106
+ 3. **Initialize fresh sessions for testing**:
1107
+ ```javascript
1108
+ // For each test case
1109
+ let sessionContext = engine.initSession(logger, 'test-user', `test-session-${testIndex}`);
1110
+ ```
1111
+
1112
+ #### Problem: Workflows not triggering or mock responses
1113
+
1114
+ **Cause**: Corrupted session state or improper context handling.
1115
+
1116
+ **Solution**: Ensure proper session lifecycle:
1117
+ ```javascript
1118
+ // Initialize once per user/conversation
1119
+ let sessionContext = engine.initSession(logger, userId, sessionId);
1120
+
1121
+ // Update for every interaction
1122
+ sessionContext = await engine.updateActivity(userInput, sessionContext);
1123
+
1124
+ // Check workflow response
1125
+ if (sessionContext.response) {
1126
+ return sessionContext.response;
1127
+ }
1128
+
1129
+ // Continue with regular conversation
1130
+ sessionContext = await engine.updateActivity(assistantResponse, sessionContext);
1131
+ ```
1132
+
1133
+ #### Problem: State bleeding between users/sessions
1134
+
1135
+ **Cause**: Shared session contexts between different users or conversations.
1136
+
1137
+ **Solution**: Maintain strict session isolation:
1138
+ ```javascript
1139
+ // Create session per user
1140
+ const userSessions = new Map();
1141
+
1142
+ function getOrCreateSession(userId, sessionId) {
1143
+ const key = `${userId}-${sessionId}`;
1144
+ if (!userSessions.has(key)) {
1145
+ userSessions.set(key, engine.initSession(logger, userId, sessionId));
1146
+ }
1147
+ return userSessions.get(key);
1148
+ }
1149
+
1150
+ // Update and store session
1151
+ let sessionContext = getOrCreateSession(userId, sessionId);
1152
+ sessionContext = await engine.updateActivity(entry, sessionContext);
1153
+ userSessions.set(`${userId}-${sessionId}`, sessionContext);
768
1154
  ```
package/dist/index.d.ts CHANGED
@@ -46,6 +46,7 @@ export interface Logger {
46
46
  warn(message: string, ...args: unknown[]): void;
47
47
  error(message: string, ...args: unknown[]): void;
48
48
  debug(message: string, ...args: unknown[]): void;
49
+ setLevel?(level: string): void;
49
50
  }
50
51
  export type MessageTemplates = Record<string, string>;
51
52
  export type MessageRegistry = Record<string, MessageTemplates>;
@@ -136,6 +137,8 @@ export interface EngineSessionContext {
136
137
  assistant?: ContextEntry;
137
138
  };
138
139
  globalVariables: Record<string, unknown>;
140
+ response?: string | null;
141
+ completedTransactions?: TransactionData[];
139
142
  cargo: Record<string, unknown> | undefined;
140
143
  }
141
144
  export interface FlowStep {
@@ -186,7 +189,7 @@ export interface TransactionStep {
186
189
  timestamp: Date;
187
190
  retryCount?: number;
188
191
  }
189
- export interface TransactionObj {
192
+ export interface TransactionData {
190
193
  id: string;
191
194
  flowName: string;
192
195
  initiator: string;
@@ -199,12 +202,15 @@ export interface TransactionObj {
199
202
  rolledBackAt?: Date;
200
203
  failureReason?: string;
201
204
  metadata: Record<string, unknown>;
202
- addStep: (step: FlowStep, result: unknown, duration: number, status?: 'success' | 'error') => void;
203
- addError: (step: FlowStep, error: Error, duration: number) => void;
204
- sanitizeForLog: (data: unknown) => unknown;
205
- rollback: () => void;
206
- complete: () => void;
207
- fail: (reason: string) => void;
205
+ }
206
+ export declare class TransactionManager {
207
+ static create(flowName: string, initiator: string, userId: string): TransactionData;
208
+ static addStep(transaction: TransactionData, step: FlowStep, result: unknown, duration: number, status?: 'success' | 'error'): void;
209
+ static addError(transaction: TransactionData, step: FlowStep, error: Error, duration: number): void;
210
+ static sanitizeForLog(data: unknown): unknown;
211
+ static rollback(transaction: TransactionData): void;
212
+ static complete(transaction: TransactionData): void;
213
+ static fail(transaction: TransactionData, reason: string): void;
208
214
  }
209
215
  export interface FlowFrame {
210
216
  flowName: string;
@@ -214,7 +220,7 @@ export interface FlowFrame {
214
220
  contextStack: ContextEntry[];
215
221
  inputStack: unknown[];
216
222
  variables: Record<string, unknown>;
217
- transaction: TransactionObj;
223
+ transaction: TransactionData;
218
224
  userId: string;
219
225
  startTime: number;
220
226
  pendingVariable?: string;
@@ -224,28 +230,7 @@ export interface FlowFrame {
224
230
  parentTransaction?: string;
225
231
  justResumed?: boolean;
226
232
  }
227
- export interface Engine {
228
- flowStacks: FlowFrame[][];
229
- flowsMenu?: FlowDefinition[];
230
- toolsRegistry?: ToolDefinition[];
231
- language?: string;
232
- messageRegistry?: MessageRegistry;
233
- guidanceConfig?: GuidanceConfig;
234
- globalVariables?: Record<string, unknown>;
235
- hasAccumulatedMessages: () => boolean;
236
- getAndClearAccumulatedMessages?: (engineSessionContext?: EngineSessionContext) => string[];
237
- addAccumulatedMessage?: (message: string, engineSessionContext?: EngineSessionContext) => void;
238
- sessionId?: string;
239
- APPROVED_FUNCTIONS?: ApprovedFunctions;
240
- aiCallback: AiCallbackFunction;
241
- lastChatTurn: {
242
- user?: ContextEntry;
243
- assistant?: ContextEntry;
244
- };
245
- initSession?: (hostLogger: Logger | null, userId: string, sessionId: string) => EngineSessionContext;
246
- updateActivity?: (contextEntry: ContextEntry, engineSessionContext?: EngineSessionContext) => Promise<string | null>;
247
- cargo: Record<string, unknown> | undefined;
248
- }
233
+ export type Engine = WorkflowEngine;
249
234
  export interface FlowDefinition {
250
235
  id: string;
251
236
  name: string;
@@ -258,6 +243,7 @@ export interface FlowDefinition {
258
243
  [key: `prompt_${string}`]: string | undefined;
259
244
  description: string;
260
245
  version: string;
246
+ interruptable?: boolean;
261
247
  steps: FlowStep[];
262
248
  variables?: Record<string, {
263
249
  type: string;
@@ -334,8 +320,7 @@ export interface AuthConfig {
334
320
  header?: string;
335
321
  }
336
322
  export interface ApprovedFunctions {
337
- get(functionName: string): ((...args: unknown[]) => unknown) | undefined;
338
- [functionName: string]: unknown;
323
+ [functionName: string]: ((...args: unknown[]) => unknown) | undefined;
339
324
  }
340
325
  export interface SystemContext {
341
326
  [key: string]: string | number | boolean | null | undefined;
@@ -447,8 +432,6 @@ export declare class WorkflowEngine implements Engine {
447
432
  flowsMenu: FlowDefinition[];
448
433
  toolsRegistry: ToolDefinition[];
449
434
  APPROVED_FUNCTIONS: ApprovedFunctions;
450
- flowStacks: FlowFrame[][];
451
- globalAccumulatedMessages: string[];
452
435
  sessionId: string;
453
436
  createdAt: Date;
454
437
  lastActivity: Date;
@@ -457,7 +440,10 @@ export declare class WorkflowEngine implements Engine {
457
440
  guidanceConfig?: GuidanceConfig;
458
441
  globalVariables?: Record<string, unknown>;
459
442
  aiCallback: AiCallbackFunction;
460
- lastChatTurn: {
443
+ private sessionContext;
444
+ get flowStacks(): FlowFrame[][];
445
+ get globalAccumulatedMessages(): string[];
446
+ get lastChatTurn(): {
461
447
  user?: ContextEntry;
462
448
  assistant?: ContextEntry;
463
449
  };
@@ -491,9 +477,9 @@ export declare class WorkflowEngine implements Engine {
491
477
  * const session = engine.initSession(yourLogger, 'user-123', 'session-456');
492
478
  */
493
479
  initSession(hostLogger: Logger | null, userId: string, sessionId: string): EngineSessionContext;
494
- updateActivity(contextEntry: ContextEntry, engineSessionContext?: EngineSessionContext): Promise<string | null>;
495
- addAccumulatedMessage(message: string, engineSessionContext?: EngineSessionContext): void;
496
- getAndClearAccumulatedMessages(engineSessionContext?: EngineSessionContext): string[];
480
+ updateActivity(contextEntry: ContextEntry, engineSessionContext: EngineSessionContext): Promise<EngineSessionContext>;
481
+ addAccumulatedMessage(message: string): void;
482
+ getAndClearAccumulatedMessages(): string[];
497
483
  hasAccumulatedMessages(): boolean;
498
484
  initializeFlowStacks(): void;
499
485
  getCurrentStack(): FlowFrame[];
@@ -501,7 +487,7 @@ export declare class WorkflowEngine implements Engine {
501
487
  getCurrentFlowFrame(): FlowFrame;
502
488
  createNewStack(): void;
503
489
  pushToCurrentStack(flowFrame: FlowFrame): void;
504
- popFromCurrentStack(): FlowFrame | undefined;
490
+ popFromCurrentStack(): FlowFrame;
505
491
  getFlowForInput(input: string): Promise<FlowDefinition | null>;
506
492
  /**
507
493
  * Performs comprehensive validation of all flows during engine initialization
@@ -571,7 +557,7 @@ export declare class WorkflowEngine implements Engine {
571
557
  validateAllFlows(options?: any): any;
572
558
  /**
573
559
  * Gets the current variable scope available at a specific step
574
- * This includes variables defined in flow definition + variables created by previous steps
560
+ * This includes variables defined in flow definition + variables created by previous steps + parent flow variables
575
561
  */
576
562
  private _getCurrentStepScope;
577
563
  /**