tracegist-mcp-bridge 0.2.7 → 0.2.8

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.
@@ -957,9 +957,12 @@ async function transcribeLiveVoice(voiceBlobDataUrl) {
957
957
  return { error: "No transcription available (set OPENROUTER_API_KEY or install Whisper)" };
958
958
  }
959
959
 
960
- /** Push a live event into the ring buffer with auto-incrementing seq. */
960
+ /** Push a live event into the ring buffer with auto-incrementing seq.
961
+ * Seq is always higher than any existing event (including extension-originated ones). */
961
962
  function pushLiveEvent(eventType, data) {
962
- liveEventSeq++;
963
+ // Ensure our seq is always higher than the max seq in the buffer
964
+ const maxExistingSeq = liveEvents.length > 0 ? liveEvents[liveEvents.length - 1].seq : 0;
965
+ liveEventSeq = Math.max(liveEventSeq, maxExistingSeq) + 1;
963
966
  liveEvents.push({ seq: liveEventSeq, ts: Date.now(), eventType, data });
964
967
  if (liveEvents.length > LIVE_SHADOW_EVENT_BUFFER_SIZE) {
965
968
  const evictCount = Math.floor(LIVE_SHADOW_EVENT_BUFFER_SIZE * 0.25);
@@ -1045,6 +1048,7 @@ function handleExtensionMessage(msg) {
1045
1048
  startedAt: Date.now(),
1046
1049
  };
1047
1050
  liveEvents.length = 0;
1051
+ liveEventSeq = 0;
1048
1052
  pendingQuestions.length = 0;
1049
1053
  questionResponses.clear();
1050
1054
  liveInteractions.length = 0;
@@ -1089,6 +1093,54 @@ function handleExtensionMessage(msg) {
1089
1093
  highlightCount: msg.highlightCaptures?.length || 0,
1090
1094
  hint: "Use get_tester_response to retrieve the full response with transcription and images.",
1091
1095
  });
1096
+ // Eagerly transcribe the voice response and log the interaction immediately
1097
+ // (don't wait for get_tester_response to be called)
1098
+ if (msg.voiceBlobDataUrl) {
1099
+ transcribeLiveVoice(msg.voiceBlobDataUrl)
1100
+ .then((result) => {
1101
+ // Store transcription with the response for later retrieval
1102
+ const stored = questionResponses.get(msg.questionId);
1103
+ if (stored && !stored.dismissed) {
1104
+ stored.transcription = result.transcription || null;
1105
+ }
1106
+ // Log the interaction with transcription
1107
+ liveInteractions.push({
1108
+ type: "tester-response",
1109
+ questionId: msg.questionId,
1110
+ timestamp: msg.timestamp || Date.now(),
1111
+ transcription: result.transcription || null,
1112
+ screenshot: !!msg.screenshot,
1113
+ highlightCount: msg.highlightCaptures?.length || 0,
1114
+ });
1115
+ // Push transcription as a live event so polling agents see it
1116
+ pushLiveEvent("question-response-transcribed", {
1117
+ questionId: msg.questionId,
1118
+ transcription: result.transcription || null,
1119
+ error: result.error || undefined,
1120
+ });
1121
+ })
1122
+ .catch((err) => {
1123
+ console.error(`[${BRIDGE_NAME}] Question response transcription failed:`, err);
1124
+ liveInteractions.push({
1125
+ type: "tester-response",
1126
+ questionId: msg.questionId,
1127
+ timestamp: msg.timestamp || Date.now(),
1128
+ transcription: null,
1129
+ screenshot: !!msg.screenshot,
1130
+ highlightCount: msg.highlightCaptures?.length || 0,
1131
+ });
1132
+ });
1133
+ } else {
1134
+ // No voice — log interaction immediately
1135
+ liveInteractions.push({
1136
+ type: "tester-response",
1137
+ questionId: msg.questionId,
1138
+ timestamp: msg.timestamp || Date.now(),
1139
+ transcription: null,
1140
+ screenshot: !!msg.screenshot,
1141
+ highlightCount: msg.highlightCaptures?.length || 0,
1142
+ });
1143
+ }
1092
1144
  break;
1093
1145
  }
1094
1146
 
@@ -1188,8 +1240,11 @@ server.tool(
1188
1240
  "- You MUST keep calling this tool every 2-4 seconds in a loop until sessionActive becomes false.\n" +
1189
1241
  "- Do NOT stop polling just because there are no new events — the tester is still active.\n" +
1190
1242
  "- When you see 'voice-transcription' events, the tester recorded a voice marker — read the transcription and react to it.\n" +
1191
- "- When you see 'question-response-received' events, use get_tester_response to retrieve the full answer.\n" +
1243
+ "- When you see 'question-response-received' events, use get_tester_response to retrieve the full answer with images.\n" +
1244
+ "- When you see 'question-response-transcribed' events, the tester's voice response has been transcribed — read it for context.\n" +
1245
+ "- Marker events with hasPendingVoice=true will have their voiceTranscription field populated once transcription completes.\n" +
1192
1246
  "- The tester may set voice markers at any time to communicate with you — keep watching for them.\n" +
1247
+ "- If you need clarification, use ask_tester_question to show a toast notification to the tester.\n" +
1193
1248
  "- Only stop polling when sessionActive is false (the recording ended).",
1194
1249
  {
1195
1250
  since_seq: z
@@ -1209,12 +1264,33 @@ server.tool(
1209
1264
 
1210
1265
  const latestSeq = liveEvents.length > 0 ? liveEvents[liveEvents.length - 1].seq : 0;
1211
1266
 
1267
+ // Enrich marker events with their transcriptions (if available in the buffer)
1268
+ // so agents don't have to correlate separate marker + voice-transcription events
1269
+ const transcriptionsByMarkerId = new Map();
1270
+ for (const e of liveEvents) {
1271
+ if (e.eventType === "voice-transcription" && e.data?.markerId) {
1272
+ transcriptionsByMarkerId.set(e.data.markerId, e.data.transcription);
1273
+ }
1274
+ }
1275
+ const enrichedEvents = events.map((e) => {
1276
+ if (e.eventType === "marker" && e.data?.markerId && transcriptionsByMarkerId.has(e.data.markerId)) {
1277
+ return {
1278
+ ...e,
1279
+ data: {
1280
+ ...e.data,
1281
+ voiceTranscription: transcriptionsByMarkerId.get(e.data.markerId),
1282
+ },
1283
+ };
1284
+ }
1285
+ return e;
1286
+ });
1287
+
1212
1288
  const responseData = {
1213
1289
  sessionActive: liveSessionActive,
1214
1290
  session: liveSessionMeta,
1215
- events,
1291
+ events: enrichedEvents,
1216
1292
  latestSeq,
1217
- eventCount: events.length,
1293
+ eventCount: enrichedEvents.length,
1218
1294
  };
1219
1295
 
1220
1296
  if (liveSessionActive) {
@@ -1241,12 +1317,23 @@ server.tool(
1241
1317
  waitingSince: Math.round((Date.now() - q.sentAt) / 1000) + "s ago",
1242
1318
  }));
1243
1319
  }
1244
- } else if (liveInteractions.length > 0) {
1245
- // Session ended — include full interaction log for the agent to incorporate
1246
- responseData.sessionEndedNote =
1247
- "Session has ended. The interactionLog below contains all agent-tester exchanges from this session. " +
1248
- "Include these in your analysis they represent collaborative context between agent and tester.";
1249
- responseData.interactionLog = liveInteractions;
1320
+ } else {
1321
+ // Session ended — include full interaction log and voice transcriptions
1322
+ if (liveInteractions.length > 0 || liveEvents.some((e) => e.eventType === "voice-transcription")) {
1323
+ responseData.sessionEndedNote =
1324
+ "Session has ended. The interactionLog and voiceMarkerTranscriptions below contain all agent-tester exchanges from this session. " +
1325
+ "Include these in your analysis — they represent collaborative context between agent and tester. " +
1326
+ "You can also call get_live_session_summary for the complete summary.";
1327
+ responseData.interactionLog = liveInteractions;
1328
+ // Include all voice marker transcriptions
1329
+ responseData.voiceMarkerTranscriptions = liveEvents
1330
+ .filter((e) => e.eventType === "voice-transcription" && e.data?.transcription)
1331
+ .map((e) => ({
1332
+ timestamp: e.ts,
1333
+ markerId: e.data.markerId,
1334
+ transcription: e.data.transcription,
1335
+ }));
1336
+ }
1250
1337
  }
1251
1338
 
1252
1339
  // Always include interaction count so the agent knows exchanges happened
@@ -1428,33 +1515,30 @@ server.tool(
1428
1515
  }
1429
1516
  }
1430
1517
 
1431
- let transcriptionText = null;
1432
1518
  if (response.voiceBlobDataUrl) {
1433
- const voiceResult = await transcribeLiveVoice(response.voiceBlobDataUrl);
1434
- if (voiceResult.transcription) {
1435
- transcriptionText = voiceResult.transcription;
1519
+ // Use pre-transcribed result if available (from eager transcription on response arrival)
1520
+ if (response.transcription) {
1436
1521
  content.push({
1437
1522
  type: "text",
1438
- text: `Voice transcription:\n${voiceResult.transcription}`,
1523
+ text: `Voice transcription:\n${response.transcription}`,
1439
1524
  });
1440
1525
  } else {
1441
- content.push({
1442
- type: "text",
1443
- text: `Voice note is attached but could not be transcribed${voiceResult.error ? ` (${voiceResult.error})` : ""}. Set OPENROUTER_API_KEY or install local Whisper for transcription.`,
1444
- });
1526
+ // Fallback: transcribe now if eager transcription hasn't completed yet
1527
+ const voiceResult = await transcribeLiveVoice(response.voiceBlobDataUrl);
1528
+ if (voiceResult.transcription) {
1529
+ content.push({
1530
+ type: "text",
1531
+ text: `Voice transcription:\n${voiceResult.transcription}`,
1532
+ });
1533
+ } else {
1534
+ content.push({
1535
+ type: "text",
1536
+ text: `Voice note is attached but could not be transcribed${voiceResult.error ? ` (${voiceResult.error})` : ""}. Set OPENROUTER_API_KEY or install local Whisper for transcription.`,
1537
+ });
1538
+ }
1445
1539
  }
1446
1540
  }
1447
1541
 
1448
- // Log the interaction for the session history
1449
- liveInteractions.push({
1450
- type: "tester-response",
1451
- questionId: question_id,
1452
- timestamp: Date.now(),
1453
- transcription: transcriptionText,
1454
- screenshot: !!response.screenshot,
1455
- highlightCount: response.highlightCaptures?.length || 0,
1456
- });
1457
-
1458
1542
  content.push({
1459
1543
  type: "text",
1460
1544
  text: "\nIMPORTANT: Resume polling watch_live_session to continue watching the session.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tracegist-mcp-bridge",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "Local-first MCP bridge for reading and transcribing TraceGist package zips.",
5
5
  "type": "module",
6
6
  "bin": {