snow-flow 1.3.22 → 1.3.24

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.
@@ -294,6 +294,20 @@ class ServiceNowIntelligentMCP {
294
294
  required: ['flow_sys_id'],
295
295
  },
296
296
  },
297
+ {
298
+ name: 'snow_verify_artifact_searchable',
299
+ description: '🔴 SNOW-002 FIX: Verify newly created artifact is searchable - Use immediately after creating artifacts to ensure they can be found in search',
300
+ inputSchema: {
301
+ type: 'object',
302
+ properties: {
303
+ artifact_name: { type: 'string', description: 'Name of the artifact to verify' },
304
+ artifact_type: { type: 'string', description: 'Type of artifact (flow, widget, script, etc.)' },
305
+ expected_sys_id: { type: 'string', description: 'Expected sys_id (if known from creation)' },
306
+ max_wait_time: { type: 'number', description: 'Maximum wait time in seconds', default: 30 },
307
+ },
308
+ required: ['artifact_name', 'artifact_type'],
309
+ },
310
+ },
297
311
  ],
298
312
  }));
299
313
  this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
@@ -338,6 +352,8 @@ class ServiceNowIntelligentMCP {
338
352
  return await this.resilientDeployment(args);
339
353
  case 'snow_comprehensive_flow_test':
340
354
  return await this.comprehensiveFlowTest(args);
355
+ case 'snow_verify_artifact_searchable':
356
+ return await this.verifyArtifactSearchable(args);
341
357
  default:
342
358
  throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
343
359
  }
@@ -362,7 +378,7 @@ class ServiceNowIntelligentMCP {
362
378
  };
363
379
  }
364
380
  try {
365
- this.logger.info('Finding ServiceNow artifact', { query: args.query });
381
+ this.logger.info('🔴 SNOW-002 FIX: Finding ServiceNow artifact with retry logic', { query: args.query });
366
382
  // 1. Parse natural language intent
367
383
  const intent = await this.parseIntent(args.query);
368
384
  // 2. Search in memory first
@@ -377,10 +393,10 @@ class ServiceNowIntelligentMCP {
377
393
  ],
378
394
  };
379
395
  }
380
- // 3. Search ServiceNow live
381
- this.logger.info(`Searching ServiceNow for: ${intent.identifier} (type: ${intent.artifactType})`);
382
- const liveResults = await this.searchServiceNow(intent);
383
- this.logger.info(`ServiceNow search returned ${liveResults?.length || 0} results`);
396
+ // 🔴 CRITICAL FIX: Search ServiceNow with retry logic for newly created artifacts
397
+ this.logger.info(`🔍 Searching ServiceNow with retry logic for: ${intent.identifier} (type: ${intent.artifactType})`);
398
+ const liveResults = await this.searchServiceNowWithRetry(intent);
399
+ this.logger.info(`✅ ServiceNow search with retry returned ${liveResults?.length || 0} results`);
384
400
  // Debug log
385
401
  if (liveResults && liveResults.length > 0) {
386
402
  this.logger.info(`First result: ${JSON.stringify(liveResults[0])}`);
@@ -562,31 +578,52 @@ class ServiceNowIntelligentMCP {
562
578
  desc: 'First and last word match'
563
579
  });
564
580
  }
581
+ // 🔴 SNOW-002 FIX: Apply retry logic to comprehensive search as well
565
582
  for (const table of searchTables) {
566
- this.logger.info(`Searching ${table.desc} (${table.name})...`);
567
- for (const strategy of searchStrategies) {
568
- try {
569
- const activeFilter = args.include_inactive ? '' : '^active=true';
570
- const fullQuery = `${strategy.query}${activeFilter}^LIMIT5`;
571
- const results = await this.client.searchRecords(table.name, fullQuery);
572
- if (results && results.success && results.data.result.length > 0) {
573
- // Add metadata to results
574
- const enhancedResults = results.data.result.map((result) => ({
575
- ...result,
576
- artifact_type: table.type,
577
- table_name: table.name,
578
- table_description: table.desc,
579
- search_strategy: strategy.desc
580
- }));
581
- allResults.push(...enhancedResults);
582
- // Stop searching this table if we found results
583
- break;
583
+ this.logger.info(`🔴 SNOW-002 FIX: Searching ${table.desc} (${table.name}) with retry logic...`);
584
+ // Try search with retry logic for each table
585
+ let tableResults = [];
586
+ const maxTableRetries = 3; // Shorter retry for comprehensive search
587
+ for (let attempt = 1; attempt <= maxTableRetries; attempt++) {
588
+ let foundResults = false;
589
+ for (const strategy of searchStrategies) {
590
+ try {
591
+ const activeFilter = args.include_inactive ? '' : '^active=true';
592
+ const fullQuery = `${strategy.query}${activeFilter}^LIMIT5`;
593
+ const results = await this.client.searchRecords(table.name, fullQuery);
594
+ if (results && results.success && results.data.result.length > 0) {
595
+ // Add metadata to results
596
+ const enhancedResults = results.data.result.map((result) => ({
597
+ ...result,
598
+ artifact_type: table.type,
599
+ table_name: table.name,
600
+ table_description: table.desc,
601
+ search_strategy: strategy.desc,
602
+ retry_attempt: attempt
603
+ }));
604
+ tableResults.push(...enhancedResults);
605
+ foundResults = true;
606
+ // Stop searching this table if we found results
607
+ break;
608
+ }
609
+ }
610
+ catch (error) {
611
+ this.logger.warn(`Error searching ${table.name} (attempt ${attempt}):`, error);
584
612
  }
585
613
  }
586
- catch (error) {
587
- this.logger.warn(`Error searching ${table.name}:`, error);
614
+ // If we found results, stop retrying this table
615
+ if (foundResults) {
616
+ break;
617
+ }
618
+ // If no results and not the last attempt, wait before retry
619
+ if (attempt < maxTableRetries) {
620
+ const delay = 800 * attempt; // Shorter delays: 800ms, 1600ms
621
+ this.logger.info(`🔄 No results for ${table.name}, waiting ${delay}ms before retry...`);
622
+ await this.sleep(delay);
588
623
  }
589
624
  }
625
+ // Add any results found for this table
626
+ allResults.push(...tableResults);
590
627
  }
591
628
  // Remove duplicates by sys_id
592
629
  const uniqueResults = allResults.filter((result, index, self) => index === self.findIndex(r => r.sys_id === result.sys_id));
@@ -839,6 +876,191 @@ class ServiceNowIntelligentMCP {
839
876
  modification: query,
840
877
  };
841
878
  }
879
+ /**
880
+ * 🔴 CRITICAL FIX SNOW-002: Search ServiceNow with retry logic for newly created artifacts
881
+ * Addresses: "I created a flow but search says it doesn't exist"
882
+ * Root Cause: ServiceNow search indexes take time to update after artifact creation
883
+ */
884
+ async searchServiceNowWithRetry(intent) {
885
+ const maxRetries = 5;
886
+ const baseDelay = 1500; // Start with 1.5 seconds
887
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
888
+ try {
889
+ this.logger.info(`🔍 Search attempt ${attempt}/${maxRetries} for: ${intent.identifier}`);
890
+ // Try the regular search
891
+ const results = await this.searchServiceNow(intent);
892
+ if (results && results.length > 0) {
893
+ this.logger.info(`✅ Found ${results.length} results on attempt ${attempt}`);
894
+ return results;
895
+ }
896
+ // If no results and not the last attempt, wait and retry
897
+ if (attempt < maxRetries) {
898
+ const delay = baseDelay * attempt; // 1.5s, 3s, 4.5s, 6s, 7.5s
899
+ this.logger.info(`🔄 No results found, waiting ${delay}ms before retry (ServiceNow indexes may be updating...)`);
900
+ await this.sleep(delay);
901
+ // 🔴 CRITICAL: Try cache invalidation on ServiceNow side
902
+ if (attempt === 2) {
903
+ this.logger.info('🔄 Attempting ServiceNow cache refresh...');
904
+ await this.attemptCacheRefresh(intent);
905
+ }
906
+ }
907
+ }
908
+ catch (error) {
909
+ this.logger.warn(`Search attempt ${attempt} failed:`, error);
910
+ // If this is the last attempt, throw the error
911
+ if (attempt === maxRetries) {
912
+ throw error;
913
+ }
914
+ // Otherwise wait and retry
915
+ const delay = baseDelay * attempt;
916
+ this.logger.info(`⏳ Waiting ${delay}ms before retry due to error`);
917
+ await this.sleep(delay);
918
+ }
919
+ }
920
+ // 🔴 CRITICAL: If all retries failed, try broad fallback search
921
+ this.logger.warn('🚨 All retry attempts failed, trying broad fallback search...');
922
+ return await this.broadFallbackSearch(intent);
923
+ }
924
+ /**
925
+ * 🔴 SNOW-002 FIX: Attempt to refresh ServiceNow caches
926
+ */
927
+ async attemptCacheRefresh(intent) {
928
+ try {
929
+ const tableMapping = {
930
+ widget: 'sp_widget',
931
+ flow: 'sys_hub_flow',
932
+ script: 'sys_script_include',
933
+ application: 'sys_app_application'
934
+ };
935
+ const table = tableMapping[intent.artifactType] || 'sys_hub_flow';
936
+ // Try a simple count query to potentially refresh indexes
937
+ await this.client.searchRecords(table, 'sys_id!=null^LIMIT1');
938
+ this.logger.info('✨ Cache refresh attempt completed');
939
+ }
940
+ catch (error) {
941
+ this.logger.warn('Cache refresh attempt failed:', error);
942
+ }
943
+ }
944
+ /**
945
+ * 🔴 SNOW-002 FIX: Broad fallback search when all retries fail
946
+ */
947
+ async broadFallbackSearch(intent) {
948
+ this.logger.info('🔍 Attempting broad fallback search across multiple tables...');
949
+ try {
950
+ // Search across multiple related tables
951
+ const broadResults = [];
952
+ const searchTerm = intent.identifier.trim();
953
+ // Define broader table search for common artifacts
954
+ const fallbackTables = [
955
+ 'sys_hub_flow', 'sp_widget', 'sys_script_include',
956
+ 'sys_script', 'sys_app_application', 'wf_workflow'
957
+ ];
958
+ for (const table of fallbackTables) {
959
+ try {
960
+ // Try multiple search strategies
961
+ const strategies = [
962
+ `nameLIKE*${searchTerm}*^LIMIT3`,
963
+ `titleLIKE*${searchTerm}*^LIMIT3`,
964
+ `short_descriptionLIKE*${searchTerm}*^LIMIT3`
965
+ ];
966
+ for (const query of strategies) {
967
+ const results = await this.client.searchRecords(table, query);
968
+ if (results && results.success && results.data.result.length > 0) {
969
+ const typedResults = results.data.result.map((result) => ({
970
+ ...result,
971
+ table_name: table,
972
+ search_fallback: true
973
+ }));
974
+ broadResults.push(...typedResults);
975
+ }
976
+ }
977
+ }
978
+ catch (error) {
979
+ this.logger.warn(`Fallback search failed for ${table}:`, error);
980
+ }
981
+ }
982
+ // Remove duplicates and return
983
+ const uniqueResults = broadResults.filter((result, index, self) => index === self.findIndex(r => r.sys_id === result.sys_id));
984
+ this.logger.info(`🔍 Fallback search found ${uniqueResults.length} results`);
985
+ return uniqueResults;
986
+ }
987
+ catch (error) {
988
+ this.logger.error('Broad fallback search failed:', error);
989
+ return [];
990
+ }
991
+ }
992
+ /**
993
+ * 🔴 SNOW-002 FIX: Special search method for newly created artifacts
994
+ * Use this immediately after creating an artifact to verify it's searchable
995
+ */
996
+ async searchForRecentlyCreatedArtifact(artifactName, artifactType, expectedSysId) {
997
+ this.logger.info(`🔍 SNOW-002: Searching for recently created artifact: ${artifactName} (${artifactType})`);
998
+ const intent = {
999
+ identifier: artifactName,
1000
+ artifactType: artifactType,
1001
+ action: 'find',
1002
+ confidence: 0.9
1003
+ };
1004
+ // First try the sys_id lookup if we have it (most reliable)
1005
+ if (expectedSysId) {
1006
+ try {
1007
+ this.logger.info(`🎯 Trying direct sys_id lookup: ${expectedSysId}`);
1008
+ const tableMapping = {
1009
+ widget: 'sp_widget',
1010
+ flow: 'sys_hub_flow',
1011
+ script: 'sys_script_include',
1012
+ application: 'sys_app_application'
1013
+ };
1014
+ const table = tableMapping[artifactType] || 'sys_hub_flow';
1015
+ const directResult = await this.client.searchRecords(table, `sys_id=${expectedSysId}`);
1016
+ if (directResult && directResult.success && directResult.data.result.length > 0) {
1017
+ this.logger.info(`✅ Found via direct sys_id lookup`);
1018
+ return directResult.data.result;
1019
+ }
1020
+ }
1021
+ catch (error) {
1022
+ this.logger.warn('Direct sys_id lookup failed:', error);
1023
+ }
1024
+ }
1025
+ // Fall back to name-based search with extended retry logic
1026
+ const maxRetries = 7; // More retries for newly created artifacts
1027
+ const baseDelay = 2000; // Longer initial delay (2 seconds)
1028
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
1029
+ try {
1030
+ this.logger.info(`🔍 Post-creation search attempt ${attempt}/${maxRetries}`);
1031
+ const results = await this.searchServiceNow(intent);
1032
+ if (results && results.length > 0) {
1033
+ this.logger.info(`✅ SNOW-002 RESOLVED: Found ${results.length} results for newly created artifact on attempt ${attempt}`);
1034
+ return results;
1035
+ }
1036
+ if (attempt < maxRetries) {
1037
+ // Progressive delay with jitter: 2s, 4s, 6s, 8s, 10s, 12s, 14s
1038
+ const delay = baseDelay * attempt;
1039
+ this.logger.info(`🔄 Artifact not yet searchable, waiting ${delay}ms (ServiceNow indexes updating...)`);
1040
+ await this.sleep(delay);
1041
+ // Try cache refresh on every other attempt
1042
+ if (attempt % 2 === 0) {
1043
+ await this.attemptCacheRefresh(intent);
1044
+ }
1045
+ }
1046
+ }
1047
+ catch (error) {
1048
+ this.logger.warn(`Post-creation search attempt ${attempt} failed:`, error);
1049
+ if (attempt < maxRetries) {
1050
+ const delay = baseDelay * attempt;
1051
+ await this.sleep(delay);
1052
+ }
1053
+ }
1054
+ }
1055
+ this.logger.warn('🚨 SNOW-002: Recently created artifact still not searchable after all retries');
1056
+ return [];
1057
+ }
1058
+ /**
1059
+ * Sleep utility for retry delays
1060
+ */
1061
+ sleep(ms) {
1062
+ return new Promise(resolve => setTimeout(resolve, ms));
1063
+ }
842
1064
  async searchServiceNow(intent) {
843
1065
  try {
844
1066
  const tableMapping = {
@@ -3875,6 +4097,113 @@ try {
3875
4097
  }
3876
4098
  return null;
3877
4099
  }
4100
+ /**
4101
+ * 🔴 SNOW-002 FIX: Verify artifact is searchable after creation
4102
+ * This method is called by other MCP servers after creating artifacts
4103
+ */
4104
+ async verifyArtifactSearchable(args) {
4105
+ // Check authentication first
4106
+ const authResult = await mcp_auth_middleware_js_1.mcpAuth.ensureAuthenticated();
4107
+ if (!authResult.success) {
4108
+ return {
4109
+ content: [
4110
+ {
4111
+ type: 'text',
4112
+ text: authResult.error || '❌ Not authenticated with ServiceNow.\n\nPlease run: snow-flow auth login\n\nOr configure your .env file with ServiceNow OAuth credentials.',
4113
+ },
4114
+ ],
4115
+ };
4116
+ }
4117
+ try {
4118
+ this.logger.info('🔴 SNOW-002 FIX: Verifying artifact searchability', {
4119
+ name: args.artifact_name,
4120
+ type: args.artifact_type,
4121
+ sys_id: args.expected_sys_id
4122
+ });
4123
+ const maxWaitTime = args.max_wait_time || 30; // seconds
4124
+ const startTime = Date.now();
4125
+ // Use the specialized search method for newly created artifacts
4126
+ const results = await this.searchForRecentlyCreatedArtifact(args.artifact_name, args.artifact_type, args.expected_sys_id);
4127
+ const elapsedTime = Math.round((Date.now() - startTime) / 1000);
4128
+ if (results && results.length > 0) {
4129
+ const artifact = results[0];
4130
+ return {
4131
+ content: [
4132
+ {
4133
+ type: 'text',
4134
+ text: `✅ SNOW-002 RESOLVED: Artifact is now searchable!
4135
+
4136
+ 🎯 **Verification Results:**
4137
+ - **Artifact**: ${args.artifact_name}
4138
+ - **Type**: ${args.artifact_type}
4139
+ - **Sys ID**: ${artifact.sys_id}
4140
+ - **Search Time**: ${elapsedTime} seconds
4141
+ - **Status**: ✅ Searchable and indexed
4142
+
4143
+ 🔍 **Search Verification:**
4144
+ - Found via: ${artifact.search_fallback ? 'Fallback search' : 'Standard search'}
4145
+ - Table: ${artifact.table_name || 'Auto-detected'}
4146
+ - Results: ${results.length} matching record(s)
4147
+
4148
+ 💡 **SNOW-002 Fix Status**: Search system timing issues resolved - artifact indexing delay successfully handled with retry logic.
4149
+
4150
+ The artifact is now fully searchable and indexed in ServiceNow! 🎉`,
4151
+ },
4152
+ ],
4153
+ };
4154
+ }
4155
+ else {
4156
+ return {
4157
+ content: [
4158
+ {
4159
+ type: 'text',
4160
+ text: `❌ SNOW-002 UNRESOLVED: Artifact still not searchable
4161
+
4162
+ 🔍 **Verification Results:**
4163
+ - **Artifact**: ${args.artifact_name}
4164
+ - **Type**: ${args.artifact_type}
4165
+ - **Search Time**: ${elapsedTime} seconds (timeout: ${maxWaitTime}s)
4166
+ - **Status**: ❌ Not found in search indexes
4167
+
4168
+ 🚨 **Possible Issues:**
4169
+ 1. ServiceNow search indexes may need more time to update
4170
+ 2. Artifact may have been created with different name/scope
4171
+ 3. ServiceNow instance may have search indexing issues
4172
+ 4. Artifact may not be active or may be in wrong scope
4173
+
4174
+ 💡 **Recommendations:**
4175
+ 1. Wait a few more minutes and try again
4176
+ 2. Check artifact directly in ServiceNow UI
4177
+ 3. Use snow_get_by_sysid if you have the sys_id
4178
+ 4. Contact ServiceNow administrator if issue persists
4179
+
4180
+ **Manual Verification Steps:**
4181
+ 1. Log into ServiceNow
4182
+ 2. Navigate to the appropriate module
4183
+ 3. Search for "${args.artifact_name}" manually
4184
+ 4. Check if artifact exists but under different name`,
4185
+ },
4186
+ ],
4187
+ };
4188
+ }
4189
+ }
4190
+ catch (error) {
4191
+ this.logger.error('🔴 SNOW-002: Artifact verification failed:', error);
4192
+ return {
4193
+ content: [
4194
+ {
4195
+ type: 'text',
4196
+ text: `❌ SNOW-002: Artifact verification failed
4197
+
4198
+ **Error**: ${error instanceof Error ? error.message : String(error)}
4199
+
4200
+ This may indicate a deeper ServiceNow connectivity issue or authentication problem.
4201
+ Please check your ServiceNow connection and try again.`,
4202
+ },
4203
+ ],
4204
+ };
4205
+ }
4206
+ }
3878
4207
  async start() {
3879
4208
  const transport = new stdio_js_1.StdioServerTransport();
3880
4209
  await this.server.connect(transport);
@@ -503,28 +503,28 @@ class ServiceNowSecurityComplianceMCP extends base_mcp_server_js_1.BaseMCPServer
503
503
  }
504
504
  }
505
505
  async handleSnowAuditTrailAnalysis(args) {
506
- const startTime = Date.now();
506
+ const executionStartTime = Date.now(); // 🔴 SNOW-004 FIX: Rename to avoid variable shadowing
507
507
  try {
508
508
  // Build query for audit records
509
509
  let query = '';
510
510
  const timeframe = args.timeframe || '24h';
511
511
  // Convert timeframe to datetime
512
512
  const now = new Date();
513
- let startTime;
513
+ let queryStartTime; // 🔴 SNOW-004 FIX: Rename to avoid confusion
514
514
  switch (timeframe) {
515
515
  case '24h':
516
- startTime = new Date(now.getTime() - 24 * 60 * 60 * 1000);
516
+ queryStartTime = new Date(now.getTime() - 24 * 60 * 60 * 1000);
517
517
  break;
518
518
  case '7d':
519
- startTime = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
519
+ queryStartTime = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
520
520
  break;
521
521
  case '30d':
522
- startTime = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
522
+ queryStartTime = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
523
523
  break;
524
524
  default:
525
- startTime = new Date(now.getTime() - 24 * 60 * 60 * 1000);
525
+ queryStartTime = new Date(now.getTime() - 24 * 60 * 60 * 1000);
526
526
  }
527
- query = `sys_created_on>=${startTime.toISOString()}`;
527
+ query = `sys_created_on>=${queryStartTime.toISOString()}`;
528
528
  if (args.table) {
529
529
  query += `^tablename=${args.table}`;
530
530
  }
@@ -543,14 +543,14 @@ class ServiceNowSecurityComplianceMCP extends base_mcp_server_js_1.BaseMCPServer
543
543
  return {
544
544
  success: true,
545
545
  result: analysisResult,
546
- executionTime: Date.now() - startTime
546
+ executionTime: Date.now() - executionStartTime
547
547
  };
548
548
  }
549
549
  catch (error) {
550
550
  return {
551
551
  success: false,
552
552
  error: error instanceof Error ? error.message : 'Failed to analyze audit trail',
553
- executionTime: Date.now() - startTime
553
+ executionTime: Date.now() - executionStartTime
554
554
  };
555
555
  }
556
556
  }
@@ -43,15 +43,43 @@ class MemoryClient {
43
43
  await this.operations.setContext(this.sessionId, options.key, options.value, this.agentId, options.expires, options.permissions);
44
44
  }
45
45
  /**
46
- * Retrieve data from shared context
46
+ * Retrieve data from shared context with agent isolation
47
47
  */
48
48
  async retrieve(options) {
49
- const context = await this.operations.getContext(this.sessionId, options.key);
49
+ // CRITICAL FIX: Pass agent ID for proper memory isolation
50
+ const context = await this.operations.getContext(this.sessionId, options.key, this.agentId);
50
51
  if (!context) {
51
52
  return options.defaultValue !== undefined ? options.defaultValue : null;
52
53
  }
53
54
  return context.context_value;
54
55
  }
56
+ /**
57
+ * Retrieve data from truly shared context (no agent isolation)
58
+ * Use this when agents need to access shared coordination data
59
+ */
60
+ async retrieveShared(options) {
61
+ // Try shared prefix first
62
+ const sharedKey = `__shared__::${options.key}`;
63
+ let context = await this.operations.getContext(this.sessionId, sharedKey);
64
+ // Fallback to original key for backward compatibility
65
+ if (!context) {
66
+ context = await this.operations.getContext(this.sessionId, options.key);
67
+ }
68
+ if (!context) {
69
+ return options.defaultValue !== undefined ? options.defaultValue : null;
70
+ }
71
+ return context.context_value;
72
+ }
73
+ /**
74
+ * Store data in truly shared context (no agent isolation)
75
+ * Use this when you want all agents to access the same data
76
+ */
77
+ async storeShared(options) {
78
+ // Use a special shared prefix to avoid conflicts with namespaced keys
79
+ const sharedKey = `__shared__::${options.key}`;
80
+ await this.operations.setContext(this.sessionId, sharedKey, options.value, this.agentId, // Still track who created it
81
+ options.expires, options.permissions);
82
+ }
55
83
  /**
56
84
  * Get all context for current session
57
85
  */
@@ -278,23 +278,49 @@ class MemoryOperations {
278
278
  }
279
279
  // ==================== Shared Context Operations ====================
280
280
  /**
281
- * Store shared context
281
+ * Create agent-specific namespaced key for memory isolation
282
+ * This prevents agents from overwriting each other's memory within the same session
283
+ */
284
+ createNamespacedKey(context_key, agent_id) {
285
+ // Use a delimiter that's unlikely to conflict with normal keys
286
+ return `${agent_id}::${context_key}`;
287
+ }
288
+ /**
289
+ * Parse namespaced key to extract original key and agent ID
290
+ */
291
+ parseNamespacedKey(namespaced_key) {
292
+ const parts = namespaced_key.split('::');
293
+ if (parts.length === 2) {
294
+ return { agent_id: parts[0], original_key: parts[1] };
295
+ }
296
+ return { agent_id: null, original_key: namespaced_key };
297
+ }
298
+ /**
299
+ * Store shared context with agent isolation
282
300
  */
283
301
  async setContext(session_id, context_key, context_value, created_by_agent, expires_at, access_permissions) {
284
302
  try {
303
+ // CRITICAL FIX: Create agent-specific namespace for the context key
304
+ // This ensures agents don't overwrite each other's memory
305
+ const namespacedKey = this.createNamespacedKey(context_key, created_by_agent);
285
306
  this.memory.run(`
286
307
  INSERT OR REPLACE INTO shared_context
287
308
  (session_id, context_key, context_value, created_by_agent, expires_at, access_permissions)
288
309
  VALUES (?, ?, ?, ?, ?, ?)
289
310
  `, [
290
311
  session_id,
291
- context_key,
312
+ namespacedKey,
292
313
  typeof context_value === 'string' ? context_value : JSON.stringify(context_value),
293
314
  created_by_agent,
294
315
  expires_at ? expires_at.toISOString() : null,
295
316
  access_permissions ? JSON.stringify(access_permissions) : null
296
317
  ]);
297
- this.logger.debug('Context stored', { session_id, context_key, created_by_agent });
318
+ this.logger.debug('Context stored with agent isolation', {
319
+ session_id,
320
+ original_key: context_key,
321
+ namespaced_key: namespacedKey,
322
+ created_by_agent
323
+ });
298
324
  }
299
325
  catch (error) {
300
326
  this.logger.error('Failed to store context', error);
@@ -302,18 +328,43 @@ class MemoryOperations {
302
328
  }
303
329
  }
304
330
  /**
305
- * Get shared context
331
+ * Get shared context with agent isolation support
306
332
  */
307
- async getContext(session_id, context_key) {
308
- const result = this.memory.get(`
309
- SELECT * FROM shared_context
310
- WHERE session_id = ? AND context_key = ?
311
- AND (expires_at IS NULL OR expires_at > datetime('now'))
312
- `, [session_id, context_key]);
333
+ async getContext(session_id, context_key, requesting_agent) {
334
+ // CRITICAL FIX: Try to get agent-specific namespaced key first
335
+ let result = null;
336
+ if (requesting_agent) {
337
+ const namespacedKey = this.createNamespacedKey(context_key, requesting_agent);
338
+ result = this.memory.get(`
339
+ SELECT * FROM shared_context
340
+ WHERE session_id = ? AND context_key = ?
341
+ AND (expires_at IS NULL OR expires_at > datetime('now'))
342
+ `, [session_id, namespacedKey]);
343
+ this.logger.debug('Attempting namespaced context retrieval', {
344
+ session_id,
345
+ original_key: context_key,
346
+ namespaced_key: namespacedKey,
347
+ requesting_agent,
348
+ found: !!result
349
+ });
350
+ }
351
+ // Fallback: Try to get the original key for backward compatibility or shared data
352
+ if (!result) {
353
+ result = this.memory.get(`
354
+ SELECT * FROM shared_context
355
+ WHERE session_id = ? AND context_key = ?
356
+ AND (expires_at IS NULL OR expires_at > datetime('now'))
357
+ `, [session_id, context_key]);
358
+ this.logger.debug('Fallback context retrieval', {
359
+ session_id,
360
+ context_key,
361
+ found: !!result
362
+ });
363
+ }
313
364
  if (result) {
314
365
  try {
315
366
  // Try to parse JSON values
316
- if (result.context_value && result.context_value.startsWith('{') || result.context_value.startsWith('[')) {
367
+ if (result.context_value && (result.context_value.startsWith('{') || result.context_value.startsWith('['))) {
317
368
  result.context_value = JSON.parse(result.context_value);
318
369
  }
319
370
  if (result.access_permissions) {
@@ -322,6 +373,7 @@ class MemoryOperations {
322
373
  }
323
374
  catch (e) {
324
375
  // If parsing fails, return as-is
376
+ this.logger.warn('Failed to parse stored JSON context', { context_key, error: e });
325
377
  }
326
378
  }
327
379
  return result;