@debugg-ai/debugg-ai-mcp 1.0.40 → 1.0.41

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.
@@ -168,10 +168,10 @@ export async function testPageChangesHandler(input, context, progressCallback) {
168
168
  if (stepsTaken > 0) {
169
169
  // Extract the latest brain.step to show what the agent is doing
170
170
  const latestStep = (exec.nodeExecutions ?? [])
171
- .filter(n => n.nodeType === 'brain.step' && n.outputData?.decision)
171
+ .filter(n => n.nodeType === 'brain.step' && n.outputData)
172
172
  .sort((a, b) => b.executionOrder - a.executionOrder)[0];
173
- if (latestStep?.outputData?.decision) {
174
- const d = latestStep.outputData.decision;
173
+ const d = latestStep?.outputData?.decision ?? latestStep?.outputData;
174
+ if (d) {
175
175
  const action = d.actionType ?? d.action_type ?? 'working';
176
176
  const intent = d.intent;
177
177
  message = intent
@@ -199,12 +199,16 @@ export async function testPageChangesHandler(input, context, progressCallback) {
199
199
  // --- Format result ---
200
200
  const outcome = finalExecution.state?.outcome ?? finalExecution.status;
201
201
  const nodes = finalExecution.nodeExecutions ?? [];
202
- // Extract step-by-step action trace from brain.step nodes
202
+ // subworkflow.run is the current graph shape — carries outcome, actionHistory, screenshot
203
+ const subworkflowNode = nodes.find(n => n.nodeType === 'subworkflow.run');
204
+ // surfer.execute_task and brain.step/brain.evaluate are older graph shapes
205
+ const surferNode = nodes.find(n => n.nodeType === 'surfer.execute_task');
206
+ // Action trace: brain.step nodes (old) → subworkflow.run actionHistory (new)
203
207
  const brainSteps = nodes
204
- .filter(n => n.nodeType === 'brain.step' && n.outputData?.decision)
208
+ .filter(n => n.nodeType === 'brain.step' && n.outputData)
205
209
  .sort((a, b) => a.executionOrder - b.executionOrder);
206
210
  const actionTrace = brainSteps.map((n, i) => {
207
- const d = n.outputData.decision;
211
+ const d = n.outputData.decision ?? n.outputData;
208
212
  return {
209
213
  step: i + 1,
210
214
  action: d.actionType ?? d.action_type,
@@ -215,33 +219,52 @@ export async function testPageChangesHandler(input, context, progressCallback) {
215
219
  durationMs: n.executionTimeMs,
216
220
  };
217
221
  });
218
- // Extract evaluation from brain.evaluate node
222
+ const subworkflowHistory = subworkflowNode?.outputData?.actionHistory;
223
+ if (actionTrace.length === 0 && Array.isArray(subworkflowHistory) && subworkflowHistory.length > 0) {
224
+ subworkflowHistory.forEach((step, i) => {
225
+ actionTrace.push({
226
+ step: i + 1,
227
+ action: step.actionType ?? step.action_type ?? step.action,
228
+ intent: step.intent,
229
+ target: step.target,
230
+ value: step.value ?? undefined,
231
+ success: step.success ?? true,
232
+ durationMs: step.durationMs ?? step.duration_ms ?? undefined,
233
+ });
234
+ });
235
+ }
236
+ // Evaluation: brain.evaluate (old) → subworkflow.run outcome/success (new)
219
237
  const evalNode = nodes.find(n => n.nodeType === 'brain.evaluate');
220
- const evaluation = evalNode?.outputData ? {
221
- passed: evalNode.outputData.passed,
222
- outcome: evalNode.outputData.outcome,
223
- reason: evalNode.outputData.reason,
224
- verifications: evalNode.outputData.verifications,
225
- } : undefined;
226
- // Also check for surfer.execute_task (older workflow graphs)
227
- const surferNode = nodes.find(n => n.nodeType === 'surfer.execute_task');
238
+ let evaluation;
239
+ if (evalNode?.outputData) {
240
+ evaluation = {
241
+ passed: evalNode.outputData.passed,
242
+ outcome: evalNode.outputData.outcome,
243
+ reason: evalNode.outputData.reason,
244
+ verifications: evalNode.outputData.verifications,
245
+ };
246
+ }
247
+ else if (subworkflowNode?.outputData) {
248
+ const sw = subworkflowNode.outputData;
249
+ evaluation = {
250
+ passed: sw.success,
251
+ outcome: sw.outcome,
252
+ reason: sw.error || undefined,
253
+ };
254
+ }
228
255
  const responsePayload = {
229
256
  outcome,
230
- success: finalExecution.state?.success ?? false,
257
+ success: finalExecution.state?.success ?? subworkflowNode?.outputData?.success ?? false,
231
258
  status: finalExecution.status,
232
- stepsTaken: finalExecution.state?.stepsTaken ?? actionTrace.length ?? 0,
259
+ stepsTaken: finalExecution.state?.stepsTaken ?? subworkflowNode?.outputData?.stepsTaken ?? actionTrace.length,
233
260
  targetUrl: originalUrl,
234
261
  executionId: executionUuid,
235
262
  durationMs: finalExecution.durationMs ?? duration,
236
263
  };
237
- // The step-by-step action trace — what the browser agent did and why
238
- if (actionTrace.length > 0) {
264
+ if (actionTrace.length > 0)
239
265
  responsePayload.actionTrace = actionTrace;
240
- }
241
- // The final evaluation — pass/fail with reasoning
242
- if (evaluation) {
266
+ if (evaluation)
243
267
  responsePayload.evaluation = evaluation;
244
- }
245
268
  if (finalExecution.state?.error)
246
269
  responsePayload.agentError = finalExecution.state.error;
247
270
  if (finalExecution.errorMessage)
@@ -262,15 +285,23 @@ export async function testPageChangesHandler(input, context, progressCallback) {
262
285
  const content = [
263
286
  { type: 'text', text: JSON.stringify(responsePayload, null, 2) },
264
287
  ];
265
- // Search all node outputs for screenshot/gif URLs not just the surfer node
266
- const SCREENSHOT_KEYS = ['finalScreenshot', 'screenshot', 'screenshotUrl', 'screenshotUri'];
288
+ // Screenshot: check for already-base64 field first (subworkflow.run), then URL-based fields
289
+ const SCREENSHOT_URL_KEYS = ['finalScreenshot', 'screenshot', 'screenshotUrl', 'screenshotUri'];
267
290
  const GIF_KEYS = ['runGif', 'gifUrl', 'gif', 'videoUrl', 'recordingUrl'];
268
- let screenshotUrl = null;
291
+ let screenshotEmbedded = false;
269
292
  let gifUrl = null;
270
- for (const node of finalExecution.nodeExecutions ?? []) {
293
+ // subworkflow.run carries screenshotB64 directly no fetch needed
294
+ const screenshotB64 = subworkflowNode?.outputData?.screenshotB64;
295
+ if (typeof screenshotB64 === 'string' && screenshotB64) {
296
+ logger.info('Embedding inline base64 screenshot from subworkflow.run');
297
+ content.push(imageContentBlock(screenshotB64, 'image/png'));
298
+ screenshotEmbedded = true;
299
+ }
300
+ let screenshotUrl = null;
301
+ for (const node of nodes) {
271
302
  const data = node.outputData ?? {};
272
- if (!screenshotUrl) {
273
- for (const key of SCREENSHOT_KEYS) {
303
+ if (!screenshotEmbedded && !screenshotUrl) {
304
+ for (const key of SCREENSHOT_URL_KEYS) {
274
305
  if (typeof data[key] === 'string' && data[key]) {
275
306
  screenshotUrl = data[key];
276
307
  break;
@@ -285,10 +316,10 @@ export async function testPageChangesHandler(input, context, progressCallback) {
285
316
  }
286
317
  }
287
318
  }
288
- if (screenshotUrl && gifUrl)
319
+ if ((screenshotEmbedded || screenshotUrl) && gifUrl)
289
320
  break;
290
321
  }
291
- if (screenshotUrl) {
322
+ if (!screenshotEmbedded && screenshotUrl) {
292
323
  logger.info(`Embedding screenshot: ${screenshotUrl}`);
293
324
  const img = await fetchImageAsBase64(screenshotUrl).catch(() => null);
294
325
  if (img)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@debugg-ai/debugg-ai-mcp",
3
- "version": "1.0.40",
3
+ "version": "1.0.41",
4
4
  "description": "Zero-Config, Fully AI-Managed End-to-End Testing for all code gen platforms.",
5
5
  "type": "module",
6
6
  "bin": {