@aj-archipelago/cortex 1.3.65 → 1.3.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/helper-apps/cortex-autogen2/Dockerfile +88 -21
  2. package/helper-apps/cortex-autogen2/docker-compose.yml +15 -8
  3. package/helper-apps/cortex-autogen2/host.json +5 -0
  4. package/helper-apps/cortex-autogen2/pyproject.toml +82 -25
  5. package/helper-apps/cortex-autogen2/requirements.txt +84 -14
  6. package/helper-apps/cortex-autogen2/services/redis_publisher.py +129 -3
  7. package/helper-apps/cortex-autogen2/task_processor.py +432 -116
  8. package/helper-apps/cortex-autogen2/tools/__init__.py +2 -0
  9. package/helper-apps/cortex-autogen2/tools/azure_blob_tools.py +32 -0
  10. package/helper-apps/cortex-autogen2/tools/azure_foundry_agents.py +50 -14
  11. package/helper-apps/cortex-autogen2/tools/file_tools.py +169 -44
  12. package/helper-apps/cortex-autogen2/tools/google_cse.py +117 -0
  13. package/helper-apps/cortex-autogen2/tools/search_tools.py +655 -98
  14. package/lib/entityConstants.js +1 -1
  15. package/lib/pathwayManager.js +42 -8
  16. package/lib/pathwayTools.js +3 -3
  17. package/lib/util.js +58 -2
  18. package/package.json +1 -1
  19. package/pathways/system/entity/memory/sys_memory_format.js +1 -0
  20. package/pathways/system/entity/memory/sys_memory_manager.js +3 -3
  21. package/pathways/system/entity/sys_entity_start.js +1 -1
  22. package/pathways/system/entity/tools/sys_tool_bing_search_afagent.js +2 -0
  23. package/pathways/system/entity/tools/sys_tool_codingagent.js +2 -2
  24. package/pathways/system/entity/tools/sys_tool_google_search.js +3 -3
  25. package/pathways/system/entity/tools/sys_tool_grok_x_search.js +12 -2
  26. package/pathways/system/workspaces/run_workspace_prompt.js +0 -3
  27. package/server/executeWorkspace.js +381 -0
  28. package/server/graphql.js +5 -180
  29. package/server/pathwayResolver.js +3 -3
  30. package/server/plugins/apptekTranslatePlugin.js +2 -2
  31. package/server/plugins/azureFoundryAgentsPlugin.js +1 -1
  32. package/tests/unit/core/parser.test.js +0 -1
  33. package/tests/unit/core/pathwayManagerWithFiles.test.js +256 -0
  34. package/tests/unit/graphql_executeWorkspace_transformation.test.js +244 -0
  35. package/tests/unit/server/graphql.test.js +122 -1
@@ -0,0 +1,381 @@
1
+ // executeWorkspace.js
2
+ // Handles the executeWorkspace GraphQL query resolver and related functionality
3
+ //
4
+ // This module contains the implementation of the executeWorkspace resolver, which is responsible
5
+ // for executing user-defined pathways (workspaces) with various execution modes:
6
+ // - Sequential execution of all prompts (default)
7
+ // - Parallel execution of specific named prompts
8
+ // - Parallel execution of all prompts (wildcard mode)
9
+ //
10
+ // The resolver supports both legacy pathway formats and new dynamic pathways with cortexPathwayName.
11
+
12
+ import { v4 as uuidv4 } from 'uuid';
13
+ import logger from '../lib/logger.js';
14
+ import { callPathway } from '../lib/pathwayTools.js';
15
+ import { getPathwayTypeDef, userPathwayInputParameters } from './typeDef.js';
16
+
17
+ // Helper function to resolve file hashes and add them to chatHistory
18
+ const resolveAndAddFileContent = async (pathways, pathwayArgs, requestId, config) => {
19
+ let fileContentAdded = false;
20
+
21
+ // Check if any pathway has file hashes
22
+ const pathwaysWithFiles = Array.isArray(pathways) ? pathways : [pathways];
23
+
24
+ for (const pathway of pathwaysWithFiles) {
25
+ if (pathway.fileHashes && pathway.fileHashes.length > 0) {
26
+ try {
27
+ const { resolveFileHashesToContent } = await import('../lib/util.js');
28
+ const fileContent = await resolveFileHashesToContent(pathway.fileHashes, config);
29
+
30
+ // Add file content to chatHistory if not already present (only do this once)
31
+ if (!fileContentAdded) {
32
+ // Initialize chatHistory if it doesn't exist
33
+ if (!pathwayArgs.chatHistory) {
34
+ pathwayArgs.chatHistory = [];
35
+ }
36
+
37
+ // Find the last user message or create one
38
+ let lastUserMessage = null;
39
+ for (let i = pathwayArgs.chatHistory.length - 1; i >= 0; i--) {
40
+ if (pathwayArgs.chatHistory[i].role === 'user') {
41
+ lastUserMessage = pathwayArgs.chatHistory[i];
42
+ break;
43
+ }
44
+ }
45
+
46
+ if (!lastUserMessage) {
47
+ lastUserMessage = {
48
+ role: 'user',
49
+ content: []
50
+ };
51
+ pathwayArgs.chatHistory.push(lastUserMessage);
52
+ }
53
+
54
+ // Ensure content is an array
55
+ if (!Array.isArray(lastUserMessage.content)) {
56
+ lastUserMessage.content = [
57
+ JSON.stringify({
58
+ type: "text",
59
+ text: lastUserMessage.content || ""
60
+ })
61
+ ];
62
+ }
63
+
64
+ // Add file content
65
+ lastUserMessage.content.push(...fileContent);
66
+ fileContentAdded = true;
67
+ }
68
+ } catch (error) {
69
+ logger.error(`[${requestId}] Failed to resolve file hashes for pathway ${pathway.name || 'unnamed'}: ${error.message}`);
70
+ // Continue execution without files
71
+ }
72
+
73
+ // Only process files once for multiple pathways
74
+ if (fileContentAdded) break;
75
+ }
76
+ }
77
+
78
+ return fileContentAdded;
79
+ };
80
+
81
+ // Helper function to execute pathway with cortex pathway name or fallback to legacy
82
+ const executePathwayWithFallback = async (pathway, pathwayArgs, contextValue, info, requestId, originalPrompt = null, config) => {
83
+ const cortexPathwayName = (originalPrompt && typeof originalPrompt === 'object' && originalPrompt.cortexPathwayName)
84
+ ? originalPrompt.cortexPathwayName
85
+ : null;
86
+
87
+ if (cortexPathwayName) {
88
+ // Use the specific cortex pathway
89
+ // Transform parameters for cortex pathway
90
+ const cortexArgs = {
91
+ model: pathway.model || pathwayArgs.model || "labeeb-agent", // Use pathway model or default
92
+ chatHistory: [],
93
+ systemPrompt: pathway.systemPrompt
94
+ };
95
+
96
+ // If we have existing chatHistory, use it as base
97
+ if (pathwayArgs.chatHistory && pathwayArgs.chatHistory.length > 0) {
98
+ cortexArgs.chatHistory = JSON.parse(JSON.stringify(pathwayArgs.chatHistory));
99
+ }
100
+
101
+ // If we have text parameter, we need to add it to the chatHistory
102
+ if (pathwayArgs.text) {
103
+ // Find the last user message or create a new one
104
+ let lastUserMessage = null;
105
+ for (let i = cortexArgs.chatHistory.length - 1; i >= 0; i--) {
106
+ if (cortexArgs.chatHistory[i].role === 'user') {
107
+ lastUserMessage = cortexArgs.chatHistory[i];
108
+ break;
109
+ }
110
+ }
111
+
112
+ if (lastUserMessage) {
113
+ // Ensure content is an array
114
+ if (!Array.isArray(lastUserMessage.content)) {
115
+ lastUserMessage.content = [JSON.stringify({
116
+ type: "text",
117
+ text: lastUserMessage.content || ""
118
+ })];
119
+ }
120
+
121
+ // Add the text parameter as a text content item
122
+ const textFromPrompt = originalPrompt?.prompt || pathwayArgs.text;
123
+ lastUserMessage.content.unshift(JSON.stringify({
124
+ type: "text",
125
+ text: `${pathwayArgs.text}\n\n${textFromPrompt}`
126
+ }));
127
+ } else {
128
+ // Create new user message with text
129
+ const textFromPrompt = originalPrompt?.prompt || pathwayArgs.text;
130
+ cortexArgs.chatHistory.push({
131
+ role: 'user',
132
+ content: [JSON.stringify({
133
+ type: "text",
134
+ text: `${pathwayArgs.text}\n\n${textFromPrompt}`
135
+ })]
136
+ });
137
+ }
138
+ }
139
+
140
+ // Create a pathwayResolver to capture extended data like artifacts
141
+ const { PathwayResolver } = await import('./pathwayResolver.js');
142
+ const cortexPathway = config.get(`pathways.${cortexPathwayName}`);
143
+ if (!cortexPathway) {
144
+ throw new Error(`Cortex pathway ${cortexPathwayName} not found`);
145
+ }
146
+
147
+ const pathwayResolver = new PathwayResolver({
148
+ config,
149
+ pathway: cortexPathway,
150
+ args: cortexArgs
151
+ });
152
+
153
+ const result = await callPathway(cortexPathwayName, cortexArgs, pathwayResolver);
154
+
155
+ // Extract resultData from pathwayResolver (includes artifacts and other extended data)
156
+ const resultData = pathwayResolver.pathwayResultData
157
+ ? JSON.stringify(pathwayResolver.pathwayResultData)
158
+ : null;
159
+
160
+ // Return result with extended data
161
+ return {
162
+ result,
163
+ resultData,
164
+ warnings: pathwayResolver.warnings,
165
+ errors: pathwayResolver.errors
166
+ };
167
+ } else {
168
+ // Fallback to original pathway execution for legacy prompts
169
+ const pathwayContext = { ...contextValue, pathway };
170
+ return await pathway.rootResolver(null, pathwayArgs, pathwayContext, info);
171
+ }
172
+ };
173
+
174
+ // Main executeWorkspace resolver
175
+ export const executeWorkspaceResolver = async (_, args, contextValue, info, config, pathwayManager) => {
176
+ const startTime = Date.now();
177
+ const requestId = uuidv4();
178
+ const { userId, pathwayName, promptNames, ...pathwayArgs } = args;
179
+
180
+ logger.info(`>>> [${requestId}] executeWorkspace started - userId: ${userId}, pathwayName: ${pathwayName}, promptNames: ${promptNames?.join(',') || 'none'}`);
181
+
182
+ try {
183
+ contextValue.config = config;
184
+
185
+ // Get the base pathway from the user
186
+ const pathways = await pathwayManager.getLatestPathways();
187
+
188
+ if (!pathways[userId] || !pathways[userId][pathwayName]) {
189
+ const error = new Error(`Pathway '${pathwayName}' not found for user '${userId}'`);
190
+ logger.error(`!!! [${requestId}] ${error.message} - Available users: ${Object.keys(pathways).join(', ')}`);
191
+ throw error;
192
+ }
193
+
194
+ const basePathway = pathways[userId][pathwayName];
195
+
196
+ // If promptNames is specified, use getPathways to get individual pathways and execute in parallel
197
+ if (promptNames && promptNames.length > 0) {
198
+
199
+ // Check if the prompts are in legacy format (array of strings)
200
+ // If so, we can't use promptNames filtering and need to ask user to republish
201
+ if (pathwayManager.isLegacyPromptFormat(userId, pathwayName)) {
202
+ const error = new Error(
203
+ `The pathway '${pathwayName}' uses legacy prompt format (array of strings) which doesn't support the promptNames parameter. ` +
204
+ `Please unpublish and republish your workspace to upgrade to the new format that supports named prompts.`
205
+ );
206
+ logger.error(`!!! [${requestId}] ${error.message}`);
207
+ throw error;
208
+ }
209
+
210
+ // Handle wildcard case - execute all prompts in parallel
211
+ if (promptNames.includes('*')) {
212
+ logger.info(`[${requestId}] Executing all prompts in parallel (wildcard specified)`);
213
+ const individualPathways = await pathwayManager.getPathways(basePathway);
214
+
215
+ if (individualPathways.length === 0) {
216
+ const error = new Error(`No prompts found in pathway '${pathwayName}'`);
217
+ logger.error(`!!! [${requestId}] ${error.message}`);
218
+ throw error;
219
+ }
220
+
221
+ // Resolve file content for any pathways that have file hashes
222
+ await resolveAndAddFileContent(individualPathways, pathwayArgs, requestId, config);
223
+
224
+ // Execute all pathways in parallel
225
+ const results = await Promise.all(
226
+ individualPathways.map(async (pathway, index) => {
227
+ try {
228
+ // Check if the prompt has a cortexPathwayName (new format)
229
+ const originalPrompt = basePathway.prompt[index];
230
+
231
+ const result = await executePathwayWithFallback(pathway, pathwayArgs, contextValue, info, requestId, originalPrompt, config);
232
+
233
+ return {
234
+ result: result.result,
235
+ resultData: result.resultData,
236
+ warnings: result.warnings,
237
+ errors: result.errors,
238
+ promptName: pathway.name || `prompt_${index + 1}`
239
+ };
240
+ } catch (error) {
241
+ logger.error(`!!! [${requestId}] Error in pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'} - ${error.message}`);
242
+ throw error;
243
+ }
244
+ })
245
+ );
246
+
247
+ const duration = Date.now() - startTime;
248
+ logger.info(`<<< [${requestId}] executeWorkspace completed successfully in ${duration}ms - returned ${results.length} results`);
249
+
250
+ // Return a single result with JSON stringified array of results
251
+ return {
252
+ debug: `Executed ${results.length} prompts in parallel`,
253
+ result: JSON.stringify(results),
254
+ resultData: null,
255
+ previousResult: null,
256
+ warnings: [],
257
+ errors: [],
258
+ contextId: requestId,
259
+ tool: 'executeWorkspace'
260
+ };
261
+ } else {
262
+ // Handle specific prompt names
263
+ logger.info(`[${requestId}] Executing specific prompts: ${promptNames.join(', ')}`);
264
+ const individualPathways = await pathwayManager.getPathways(basePathway, promptNames);
265
+
266
+ if (individualPathways.length === 0) {
267
+ const error = new Error(`No prompts found matching the specified names: ${promptNames.join(', ')}`);
268
+ logger.error(`!!! [${requestId}] ${error.message}`);
269
+ throw error;
270
+ }
271
+
272
+ // Resolve file content for any pathways that have file hashes
273
+ await resolveAndAddFileContent(individualPathways, pathwayArgs, requestId, config);
274
+
275
+ // Execute all pathways in parallel
276
+ const results = await Promise.all(
277
+ individualPathways.map(async (pathway, index) => {
278
+ try {
279
+ // Find the original prompt by name to get the cortexPathwayName
280
+ const originalPrompt = basePathway.prompt.find(p =>
281
+ (typeof p === 'object' && p.name === pathway.name) ||
282
+ (typeof p === 'string' && pathway.name === `prompt_${basePathway.prompt.indexOf(p)}`)
283
+ );
284
+
285
+ const result = await executePathwayWithFallback(pathway, pathwayArgs, contextValue, info, requestId, originalPrompt, config);
286
+
287
+ return {
288
+ result: result.result,
289
+ resultData: result.resultData,
290
+ warnings: result.warnings,
291
+ errors: result.errors,
292
+ promptName: pathway.name || `prompt_${index + 1}`
293
+ };
294
+ } catch (error) {
295
+ logger.error(`!!! [${requestId}] Error in pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'} - ${error.message}`);
296
+ throw error;
297
+ }
298
+ })
299
+ );
300
+
301
+ const duration = Date.now() - startTime;
302
+ logger.info(`<<< [${requestId}] executeWorkspace completed successfully in ${duration}ms - returned ${results.length} results`);
303
+
304
+ // Return a single result with JSON stringified array of results (consistent with wildcard case)
305
+ return {
306
+ debug: `Executed ${results.length} specific prompts in parallel: ${promptNames.join(', ')}`,
307
+ result: JSON.stringify(results),
308
+ resultData: null,
309
+ previousResult: null,
310
+ warnings: [],
311
+ errors: [],
312
+ contextId: requestId,
313
+ tool: 'executeWorkspace'
314
+ };
315
+ }
316
+ }
317
+
318
+ // Default behavior: execute all prompts in sequence
319
+ logger.info(`[${requestId}] Executing prompts in sequence`);
320
+ const userPathway = await pathwayManager.getPathway(userId, pathwayName);
321
+ contextValue.pathway = userPathway;
322
+
323
+ // Handle file hashes if present in the pathway
324
+ await resolveAndAddFileContent(userPathway, pathwayArgs, requestId, config);
325
+
326
+ // Check if any prompt has cortexPathwayName (for dynamic pathways)
327
+ let result;
328
+ if (userPathway.prompt && Array.isArray(userPathway.prompt)) {
329
+ const firstPrompt = userPathway.prompt[0];
330
+
331
+ result = await executePathwayWithFallback(userPathway, pathwayArgs, contextValue, info, requestId, firstPrompt, config);
332
+ } else {
333
+ // No prompt array, use legacy execution
334
+ result = await userPathway.rootResolver(null, pathwayArgs, contextValue, info);
335
+ }
336
+ const duration = Date.now() - startTime;
337
+ logger.info(`<<< [${requestId}] executeWorkspace completed successfully in ${duration}ms - returned 1 result`);
338
+ return result; // Return single result directly
339
+
340
+ } catch (error) {
341
+ const duration = Date.now() - startTime;
342
+ logger.error(`!!! [${requestId}] executeWorkspace failed after ${duration}ms`);
343
+ logger.error(`!!! [${requestId}] Error type: ${error.constructor.name}`);
344
+ logger.error(`!!! [${requestId}] Error message: ${error.message}`);
345
+ logger.error(`!!! [${requestId}] Error stack: ${error.stack}`);
346
+
347
+ // Log additional context for debugging "memory access out of bounds" errors
348
+ if (error.message && error.message.includes('memory')) {
349
+ logger.error(`!!! [${requestId}] MEMORY ERROR DETECTED - Additional context:`);
350
+ logger.error(`!!! [${requestId}] - Node.js version: ${process.version}`);
351
+ logger.error(`!!! [${requestId}] - Memory usage: ${JSON.stringify(process.memoryUsage())}`);
352
+ logger.error(`!!! [${requestId}] - Args size estimate: ${JSON.stringify(args).length} chars`);
353
+ logger.error(`!!! [${requestId}] - PathwayArgs keys: ${Object.keys(pathwayArgs).join(', ')}`);
354
+ }
355
+
356
+ throw error;
357
+ }
358
+ };
359
+
360
+ // Type definitions for executeWorkspace
361
+ export const getExecuteWorkspaceTypeDefs = () => {
362
+ return `
363
+ ${getPathwayTypeDef('ExecuteWorkspace', 'String')}
364
+
365
+ type ExecuteWorkspaceResult {
366
+ debug: String
367
+ result: String
368
+ resultData: String
369
+ previousResult: String
370
+ warnings: [String]
371
+ errors: [String]
372
+ contextId: String
373
+ tool: String
374
+ }
375
+
376
+ extend type Query {
377
+ executeWorkspace(userId: String!, pathwayName: String!, ${userPathwayInputParameters}): ExecuteWorkspaceResult
378
+ }
379
+ `;
380
+ };
381
+
package/server/graphql.js CHANGED
@@ -15,7 +15,6 @@ import { WebSocketServer } from 'ws';
15
15
  import responseCachePlugin from '@apollo/server-plugin-response-cache';
16
16
  import { KeyvAdapter } from '@apollo/utils.keyvadapter';
17
17
  import cors from 'cors';
18
- import { v4 as uuidv4 } from 'uuid';
19
18
  import { buildModels, buildPathways } from '../config.js';
20
19
  import logger from '../lib/logger.js';
21
20
  import { buildModelEndpoints } from '../lib/requestExecutor.js';
@@ -23,8 +22,9 @@ import { startTestServer } from '../tests/helpers/server.js';
23
22
  import { requestState } from './requestState.js';
24
23
  import { cancelRequestResolver } from './resolver.js';
25
24
  import subscriptions from './subscriptions.js';
26
- import { getMessageTypeDefs, getPathwayTypeDef, userPathwayInputParameters } from './typeDef.js';
25
+ import { getMessageTypeDefs } from './typeDef.js';
27
26
  import { buildRestEndpoints } from './rest.js';
27
+ import { executeWorkspaceResolver, getExecuteWorkspaceTypeDefs } from './executeWorkspace.js';
28
28
 
29
29
  // Utility functions
30
30
  // Server plugins
@@ -74,22 +74,7 @@ const getTypedefs = (pathways, pathwayManager) => {
74
74
  cancelRequest(requestId: String!): Boolean
75
75
  }
76
76
 
77
- ${getPathwayTypeDef('ExecuteWorkspace', 'String')}
78
-
79
- type ExecuteWorkspaceResult {
80
- debug: String
81
- result: String
82
- resultData: String
83
- previousResult: String
84
- warnings: [String]
85
- errors: [String]
86
- contextId: String
87
- tool: String
88
- }
89
-
90
- extend type Query {
91
- executeWorkspace(userId: String!, pathwayName: String!, ${userPathwayInputParameters}): ExecuteWorkspaceResult
92
- }
77
+ ${getExecuteWorkspaceTypeDefs()}
93
78
 
94
79
  type RequestSubscription {
95
80
  requestId: String
@@ -130,171 +115,11 @@ const getResolvers = (config, pathways, pathwayManager) => {
130
115
 
131
116
  const pathwayManagerResolvers = pathwayManager?.getResolvers() || {};
132
117
 
133
- const executeWorkspaceResolver = async (_, args, contextValue, info) => {
134
- const startTime = Date.now();
135
- const requestId = uuidv4();
136
- const { userId, pathwayName, promptNames, ...pathwayArgs } = args;
137
-
138
- logger.info(`>>> [${requestId}] executeWorkspace started - userId: ${userId}, pathwayName: ${pathwayName}, promptNames: ${promptNames?.join(',') || 'none'}`);
139
-
140
- try {
141
- contextValue.config = config;
142
-
143
- // Get the base pathway from the user
144
- const pathways = await pathwayManager.getLatestPathways();
145
-
146
- if (!pathways[userId] || !pathways[userId][pathwayName]) {
147
- const error = new Error(`Pathway '${pathwayName}' not found for user '${userId}'`);
148
- logger.error(`!!! [${requestId}] ${error.message} - Available users: ${Object.keys(pathways).join(', ')}`);
149
- throw error;
150
- }
151
-
152
- const basePathway = pathways[userId][pathwayName];
153
- logger.debug(`[${requestId}] Found pathway: ${pathwayName} for user: ${userId}`);
154
-
155
- // If promptNames is specified, use getPathways to get individual pathways and execute in parallel
156
- if (promptNames && promptNames.length > 0) {
157
-
158
- // Check if the prompts are in legacy format (array of strings)
159
- // If so, we can't use promptNames filtering and need to ask user to republish
160
- if (pathwayManager.isLegacyPromptFormat(userId, pathwayName)) {
161
- const error = new Error(
162
- `The pathway '${pathwayName}' uses legacy prompt format (array of strings) which doesn't support the promptNames parameter. ` +
163
- `Please unpublish and republish your workspace to upgrade to the new format that supports named prompts.`
164
- );
165
- logger.error(`!!! [${requestId}] ${error.message}`);
166
- throw error;
167
- }
168
-
169
- // Handle wildcard case - execute all prompts in parallel
170
- if (promptNames.includes('*')) {
171
- logger.info(`[${requestId}] Executing all prompts in parallel (wildcard specified)`);
172
- const individualPathways = await pathwayManager.getPathways(basePathway);
173
-
174
- if (individualPathways.length === 0) {
175
- const error = new Error(`No prompts found in pathway '${pathwayName}'`);
176
- logger.error(`!!! [${requestId}] ${error.message}`);
177
- throw error;
178
- }
179
-
180
- // Execute all pathways in parallel
181
- logger.debug(`[${requestId}] Executing ${individualPathways.length} pathways in parallel`);
182
- const results = await Promise.all(
183
- individualPathways.map(async (pathway, index) => {
184
- try {
185
- logger.debug(`[${requestId}] Starting pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'}`);
186
- const pathwayContext = { ...contextValue, pathway };
187
- const result = await pathway.rootResolver(null, pathwayArgs, pathwayContext, info);
188
- logger.debug(`[${requestId}] Completed pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'}`);
189
- return {
190
- result: result.result,
191
- promptName: pathway.name || `prompt_${index + 1}`
192
- };
193
- } catch (error) {
194
- logger.error(`!!! [${requestId}] Error in pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'} - ${error.message}`);
195
- logger.debug(`[${requestId}] Error stack: ${error.stack}`);
196
- throw error;
197
- }
198
- })
199
- );
200
-
201
- const duration = Date.now() - startTime;
202
- logger.info(`<<< [${requestId}] executeWorkspace completed successfully in ${duration}ms - returned ${results.length} results`);
203
-
204
- // Return a single result with JSON stringified array of results
205
- return {
206
- debug: `Executed ${results.length} prompts in parallel`,
207
- result: JSON.stringify(results),
208
- resultData: null,
209
- previousResult: null,
210
- warnings: [],
211
- errors: [],
212
- contextId: requestId,
213
- tool: 'executeWorkspace'
214
- };
215
- } else {
216
- // Handle specific prompt names
217
- logger.info(`[${requestId}] Executing specific prompts: ${promptNames.join(', ')}`);
218
- const individualPathways = await pathwayManager.getPathways(basePathway, promptNames);
219
-
220
- if (individualPathways.length === 0) {
221
- const error = new Error(`No prompts found matching the specified names: ${promptNames.join(', ')}`);
222
- logger.error(`!!! [${requestId}] ${error.message}`);
223
- throw error;
224
- }
225
-
226
- // Execute all pathways in parallel
227
- logger.debug(`[${requestId}] Executing ${individualPathways.length} pathways in parallel`);
228
- const results = await Promise.all(
229
- individualPathways.map(async (pathway, index) => {
230
- try {
231
- logger.debug(`[${requestId}] Starting pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'}`);
232
- const pathwayContext = { ...contextValue, pathway };
233
- const result = await pathway.rootResolver(null, pathwayArgs, pathwayContext, info);
234
- logger.debug(`[${requestId}] Completed pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'}`);
235
- return {
236
- result: result.result,
237
- promptName: pathway.name || `prompt_${index + 1}`
238
- };
239
- } catch (error) {
240
- logger.error(`!!! [${requestId}] Error in pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'} - ${error.message}`);
241
- logger.debug(`[${requestId}] Error stack: ${error.stack}`);
242
- throw error;
243
- }
244
- })
245
- );
246
-
247
- const duration = Date.now() - startTime;
248
- logger.info(`<<< [${requestId}] executeWorkspace completed successfully in ${duration}ms - returned ${results.length} results`);
249
-
250
- // Return a single result with JSON stringified array of results (consistent with wildcard case)
251
- return {
252
- debug: `Executed ${results.length} specific prompts in parallel: ${promptNames.join(', ')}`,
253
- result: JSON.stringify(results),
254
- resultData: null,
255
- previousResult: null,
256
- warnings: [],
257
- errors: [],
258
- contextId: requestId,
259
- tool: 'executeWorkspace'
260
- };
261
- }
262
- }
263
-
264
- // Default behavior: execute all prompts in sequence
265
- logger.info(`[${requestId}] Executing prompts in sequence`);
266
- const userPathway = await pathwayManager.getPathway(userId, pathwayName);
267
- contextValue.pathway = userPathway;
268
-
269
- const result = await userPathway.rootResolver(null, pathwayArgs, contextValue, info);
270
- const duration = Date.now() - startTime;
271
- logger.info(`<<< [${requestId}] executeWorkspace completed successfully in ${duration}ms - returned 1 result`);
272
- return result; // Return single result directly
273
-
274
- } catch (error) {
275
- const duration = Date.now() - startTime;
276
- logger.error(`!!! [${requestId}] executeWorkspace failed after ${duration}ms`);
277
- logger.error(`!!! [${requestId}] Error type: ${error.constructor.name}`);
278
- logger.error(`!!! [${requestId}] Error message: ${error.message}`);
279
- logger.error(`!!! [${requestId}] Error stack: ${error.stack}`);
280
-
281
- // Log additional context for debugging "memory access out of bounds" errors
282
- if (error.message && error.message.includes('memory')) {
283
- logger.error(`!!! [${requestId}] MEMORY ERROR DETECTED - Additional context:`);
284
- logger.error(`!!! [${requestId}] - Node.js version: ${process.version}`);
285
- logger.error(`!!! [${requestId}] - Memory usage: ${JSON.stringify(process.memoryUsage())}`);
286
- logger.error(`!!! [${requestId}] - Args size estimate: ${JSON.stringify(args).length} chars`);
287
- logger.error(`!!! [${requestId}] - PathwayArgs keys: ${Object.keys(pathwayArgs).join(', ')}`);
288
- }
289
-
290
- throw error;
291
- }
292
- };
293
-
294
118
  const resolvers = {
295
119
  Query: {
296
120
  ...resolverFunctions,
297
- executeWorkspace: executeWorkspaceResolver
121
+ executeWorkspace: (parent, args, contextValue, info) =>
122
+ executeWorkspaceResolver(parent, args, contextValue, info, config, pathwayManager)
298
123
  },
299
124
  Mutation: {
300
125
  'cancelRequest': cancelRequestResolver,
@@ -293,7 +293,7 @@ class PathwayResolver {
293
293
  requestProgress = this.modelExecutor.plugin.processStreamEvent(event, requestProgress);
294
294
  } catch (error) {
295
295
  streamErrorOccurred = true;
296
- logger.error(`Stream error: ${error.message}`);
296
+ logger.error(`Stream error: ${error instanceof Error ? error.stack || error.message : JSON.stringify(error)}`);
297
297
  incomingMessage.off('data', processStream);
298
298
  return;
299
299
  }
@@ -304,7 +304,7 @@ class PathwayResolver {
304
304
  streamEnded = requestProgress.progress === 1;
305
305
  }
306
306
  } catch (error) {
307
- logger.error(`Could not publish the stream message: "${event.data}", ${error}`);
307
+ logger.error(`Could not publish the stream message: "${event.data}", ${error instanceof Error ? error.stack || error.message : JSON.stringify(error)}`);
308
308
  }
309
309
 
310
310
  }
@@ -325,7 +325,7 @@ class PathwayResolver {
325
325
  }
326
326
 
327
327
  } catch (error) {
328
- logger.error(`Could not subscribe to stream: ${error}`);
328
+ logger.error(`Could not subscribe to stream: ${error instanceof Error ? error.stack || error.message : JSON.stringify(error)}`);
329
329
  }
330
330
 
331
331
  if (streamErrorOccurred) {
@@ -95,7 +95,7 @@ class ApptekTranslatePlugin extends ModelPlugin {
95
95
  detectedLanguage = result.split('\n')[0].split(';')[0];
96
96
  } else {
97
97
  logger.error(`Apptek Language detection failed with status: ${resultResponse.status}`);
98
- logger.debug({error: resultResponse, text})
98
+ logger.debug(`Apptek language detection response: ${JSON.stringify({ status: resultResponse.status, textSnippet: text?.slice?.(0, 200) || text })}`)
99
99
  }
100
100
 
101
101
  if (!detectedLanguage) {
@@ -111,7 +111,7 @@ class ApptekTranslatePlugin extends ModelPlugin {
111
111
  text,
112
112
  });
113
113
 
114
- logger.verbose('Successfully used language pathway as fallback', {detectedLanguage});
114
+ logger.verbose(`Successfully used language pathway as fallback: ${JSON.stringify({ detectedLanguage })}`);
115
115
  if (!detectedLanguage) {
116
116
  throw new Error('Language detection failed using fallback language pathway');
117
117
  }
@@ -182,7 +182,7 @@ class AzureFoundryAgentsPlugin extends ModelPlugin {
182
182
 
183
183
  // Check if run failed
184
184
  if (runStatus.status === 'failed') {
185
- logger.error(`[Azure Foundry Agent] Run failed: ${runId}`, runStatus.lastError);
185
+ logger.error(`[Azure Foundry Agent] Run failed: ${runId} ${runStatus?.lastError ? JSON.stringify(runStatus.lastError) : ''}`);
186
186
  return null;
187
187
  }
188
188
 
@@ -90,7 +90,6 @@ test('parseJson should attempt to repair invalid JSON', async t => {
90
90
 
91
91
  const result = await parser.parseJson(invalidJson);
92
92
 
93
- console.log('parseJson result:', result); // For debugging
94
93
 
95
94
  t.not(result, null);
96
95