tracegist-mcp-bridge 0.2.6 → 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.
- package/bin/tracegist-mcp-bridge.mjs +271 -42
- package/package.json +1 -1
|
@@ -866,6 +866,14 @@ const pendingQuestions = [];
|
|
|
866
866
|
/** @type {Map<string, object>} */
|
|
867
867
|
const questionResponses = new Map();
|
|
868
868
|
|
|
869
|
+
/**
|
|
870
|
+
* Persistent log of all agent-tester interactions during the live session.
|
|
871
|
+
* Survives until the next session-start. Used to build the complete interaction
|
|
872
|
+
* history for the final artifact.
|
|
873
|
+
* @type {Array<{type: string, questionId: string, question?: string, timestamp: number, transcription?: string, screenshot?: boolean, highlightCount?: number, dismissed?: boolean}>}
|
|
874
|
+
*/
|
|
875
|
+
const liveInteractions = [];
|
|
876
|
+
|
|
869
877
|
/** @type {Array<{resolve: Function, reject: Function, timeout: ReturnType<typeof setTimeout>}>} */
|
|
870
878
|
const screenshotWaiters = [];
|
|
871
879
|
|
|
@@ -949,9 +957,12 @@ async function transcribeLiveVoice(voiceBlobDataUrl) {
|
|
|
949
957
|
return { error: "No transcription available (set OPENROUTER_API_KEY or install Whisper)" };
|
|
950
958
|
}
|
|
951
959
|
|
|
952
|
-
/** 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). */
|
|
953
962
|
function pushLiveEvent(eventType, data) {
|
|
954
|
-
|
|
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;
|
|
955
966
|
liveEvents.push({ seq: liveEventSeq, ts: Date.now(), eventType, data });
|
|
956
967
|
if (liveEvents.length > LIVE_SHADOW_EVENT_BUFFER_SIZE) {
|
|
957
968
|
const evictCount = Math.floor(LIVE_SHADOW_EVENT_BUFFER_SIZE * 0.25);
|
|
@@ -1037,8 +1048,10 @@ function handleExtensionMessage(msg) {
|
|
|
1037
1048
|
startedAt: Date.now(),
|
|
1038
1049
|
};
|
|
1039
1050
|
liveEvents.length = 0;
|
|
1051
|
+
liveEventSeq = 0;
|
|
1040
1052
|
pendingQuestions.length = 0;
|
|
1041
1053
|
questionResponses.clear();
|
|
1054
|
+
liveInteractions.length = 0;
|
|
1042
1055
|
console.error(`[${BRIDGE_NAME}] Live shadow: session started (${msg.sessionId})`);
|
|
1043
1056
|
// Notify MCP clients that resources changed
|
|
1044
1057
|
try {
|
|
@@ -1072,6 +1085,62 @@ function handleExtensionMessage(msg) {
|
|
|
1072
1085
|
});
|
|
1073
1086
|
const rIdx = pendingQuestions.findIndex((q) => q.questionId === msg.questionId);
|
|
1074
1087
|
if (rIdx >= 0) pendingQuestions.splice(rIdx, 1);
|
|
1088
|
+
// Push a live event so polling agents see the response immediately
|
|
1089
|
+
pushLiveEvent("question-response-received", {
|
|
1090
|
+
questionId: msg.questionId,
|
|
1091
|
+
hasVoice: !!msg.voiceBlobDataUrl,
|
|
1092
|
+
hasScreenshot: !!msg.screenshot,
|
|
1093
|
+
highlightCount: msg.highlightCaptures?.length || 0,
|
|
1094
|
+
hint: "Use get_tester_response to retrieve the full response with transcription and images.",
|
|
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
|
+
}
|
|
1075
1144
|
break;
|
|
1076
1145
|
}
|
|
1077
1146
|
|
|
@@ -1079,6 +1148,14 @@ function handleExtensionMessage(msg) {
|
|
|
1079
1148
|
questionResponses.set(msg.questionId, { dismissed: true });
|
|
1080
1149
|
const dIdx = pendingQuestions.findIndex((q) => q.questionId === msg.questionId);
|
|
1081
1150
|
if (dIdx >= 0) pendingQuestions.splice(dIdx, 1);
|
|
1151
|
+
// Log dismissal in interactions
|
|
1152
|
+
liveInteractions.push({
|
|
1153
|
+
type: "question-dismissed",
|
|
1154
|
+
questionId: msg.questionId,
|
|
1155
|
+
timestamp: Date.now(),
|
|
1156
|
+
dismissed: true,
|
|
1157
|
+
});
|
|
1158
|
+
pushLiveEvent("question-dismissed", { questionId: msg.questionId });
|
|
1082
1159
|
break;
|
|
1083
1160
|
}
|
|
1084
1161
|
|
|
@@ -1099,6 +1176,13 @@ function handleExtensionMessage(msg) {
|
|
|
1099
1176
|
transcription: result.transcription || null,
|
|
1100
1177
|
error: result.error || undefined,
|
|
1101
1178
|
});
|
|
1179
|
+
// Log voice marker in interactions for the final artifact
|
|
1180
|
+
liveInteractions.push({
|
|
1181
|
+
type: "voice-marker",
|
|
1182
|
+
questionId: markerId,
|
|
1183
|
+
timestamp: Date.now(),
|
|
1184
|
+
transcription: result.transcription || null,
|
|
1185
|
+
});
|
|
1102
1186
|
})
|
|
1103
1187
|
.catch((err) => {
|
|
1104
1188
|
console.error(`[${BRIDGE_NAME}] Live voice transcription failed:`, err);
|
|
@@ -1151,9 +1235,17 @@ server.resource(
|
|
|
1151
1235
|
// Tool: watch_live_session
|
|
1152
1236
|
server.tool(
|
|
1153
1237
|
"watch_live_session",
|
|
1154
|
-
"Watch a live TraceGist shadowing session. Returns buffered events since the given sequence number
|
|
1155
|
-
"
|
|
1156
|
-
"
|
|
1238
|
+
"Watch a live TraceGist shadowing session in real-time. Returns buffered events since the given sequence number.\n\n" +
|
|
1239
|
+
"IMPORTANT POLLING BEHAVIOR:\n" +
|
|
1240
|
+
"- You MUST keep calling this tool every 2-4 seconds in a loop until sessionActive becomes false.\n" +
|
|
1241
|
+
"- Do NOT stop polling just because there are no new events — the tester is still active.\n" +
|
|
1242
|
+
"- When you see 'voice-transcription' events, the tester recorded a voice marker — read the transcription and react to it.\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" +
|
|
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" +
|
|
1248
|
+
"- Only stop polling when sessionActive is false (the recording ended).",
|
|
1157
1249
|
{
|
|
1158
1250
|
since_seq: z
|
|
1159
1251
|
.number()
|
|
@@ -1172,37 +1264,88 @@ server.tool(
|
|
|
1172
1264
|
|
|
1173
1265
|
const latestSeq = liveEvents.length > 0 ? liveEvents[liveEvents.length - 1].seq : 0;
|
|
1174
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
|
+
|
|
1288
|
+
const responseData = {
|
|
1289
|
+
sessionActive: liveSessionActive,
|
|
1290
|
+
session: liveSessionMeta,
|
|
1291
|
+
events: enrichedEvents,
|
|
1292
|
+
latestSeq,
|
|
1293
|
+
eventCount: enrichedEvents.length,
|
|
1294
|
+
};
|
|
1295
|
+
|
|
1296
|
+
if (liveSessionActive) {
|
|
1297
|
+
responseData.pollingGuidance =
|
|
1298
|
+
"KEEP POLLING every 2-4 seconds. Do NOT stop — the tester is still active. " +
|
|
1299
|
+
"Watch for 'voice-transcription' events (tester voice markers) and 'question-response-received' events. " +
|
|
1300
|
+
"React to voice markers by acknowledging them or asking follow-up questions.";
|
|
1301
|
+
responseData.availableActions = [
|
|
1302
|
+
{
|
|
1303
|
+
tool: "ask_tester_question",
|
|
1304
|
+
description:
|
|
1305
|
+
"Ask the tester a short question (max 200 chars). They respond with voice + highlights.",
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
tool: "get_live_screenshot",
|
|
1309
|
+
description:
|
|
1310
|
+
"Capture a screenshot of the tester's current browser tab.",
|
|
1311
|
+
},
|
|
1312
|
+
];
|
|
1313
|
+
if (pendingQuestions.length > 0) {
|
|
1314
|
+
responseData.pendingQuestions = pendingQuestions.map((q) => ({
|
|
1315
|
+
questionId: q.questionId,
|
|
1316
|
+
question: q.question,
|
|
1317
|
+
waitingSince: Math.round((Date.now() - q.sentAt) / 1000) + "s ago",
|
|
1318
|
+
}));
|
|
1319
|
+
}
|
|
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
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
// Always include interaction count so the agent knows exchanges happened
|
|
1340
|
+
if (liveInteractions.length > 0) {
|
|
1341
|
+
responseData.interactionCount = liveInteractions.length;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1175
1344
|
return {
|
|
1176
1345
|
content: [
|
|
1177
1346
|
{
|
|
1178
1347
|
type: "text",
|
|
1179
|
-
text: JSON.stringify(
|
|
1180
|
-
{
|
|
1181
|
-
sessionActive: liveSessionActive,
|
|
1182
|
-
session: liveSessionMeta,
|
|
1183
|
-
events,
|
|
1184
|
-
latestSeq,
|
|
1185
|
-
eventCount: events.length,
|
|
1186
|
-
...(liveSessionActive
|
|
1187
|
-
? {
|
|
1188
|
-
availableActions: [
|
|
1189
|
-
{
|
|
1190
|
-
tool: "ask_tester_question",
|
|
1191
|
-
description:
|
|
1192
|
-
"Ask the tester a short question (max 200 chars). They respond with voice + highlights.",
|
|
1193
|
-
},
|
|
1194
|
-
{
|
|
1195
|
-
tool: "get_live_screenshot",
|
|
1196
|
-
description:
|
|
1197
|
-
"Capture a screenshot of the tester's current browser tab.",
|
|
1198
|
-
},
|
|
1199
|
-
],
|
|
1200
|
-
}
|
|
1201
|
-
: {}),
|
|
1202
|
-
},
|
|
1203
|
-
null,
|
|
1204
|
-
2,
|
|
1205
|
-
),
|
|
1348
|
+
text: JSON.stringify(responseData, null, 2),
|
|
1206
1349
|
},
|
|
1207
1350
|
],
|
|
1208
1351
|
};
|
|
@@ -1248,6 +1391,17 @@ server.tool(
|
|
|
1248
1391
|
pendingQuestions.shift();
|
|
1249
1392
|
}
|
|
1250
1393
|
|
|
1394
|
+
// Log the interaction
|
|
1395
|
+
liveInteractions.push({
|
|
1396
|
+
type: "agent-question",
|
|
1397
|
+
questionId,
|
|
1398
|
+
question,
|
|
1399
|
+
timestamp: Date.now(),
|
|
1400
|
+
});
|
|
1401
|
+
|
|
1402
|
+
// Push a live event so the polling loop sees it
|
|
1403
|
+
pushLiveEvent("agent-question-sent", { questionId, question });
|
|
1404
|
+
|
|
1251
1405
|
return {
|
|
1252
1406
|
content: [
|
|
1253
1407
|
{
|
|
@@ -1260,7 +1414,9 @@ server.tool(
|
|
|
1260
1414
|
"- Voice marker: Alt/Option + Shift + M (start/stop recording)",
|
|
1261
1415
|
"- Highlight captures: Alt/Option + Shift + S (draw on screen)",
|
|
1262
1416
|
"",
|
|
1263
|
-
"
|
|
1417
|
+
"IMPORTANT: Keep polling watch_live_session — you will see a 'question-response-received' event when they respond.",
|
|
1418
|
+
"Then use `get_tester_response` with this question ID to retrieve their full answer.",
|
|
1419
|
+
"The tester may take 10-30 seconds to record their voice response. Be patient.",
|
|
1264
1420
|
].join("\n"),
|
|
1265
1421
|
},
|
|
1266
1422
|
],
|
|
@@ -1272,7 +1428,11 @@ server.tool(
|
|
|
1272
1428
|
server.tool(
|
|
1273
1429
|
"get_tester_response",
|
|
1274
1430
|
"Check for the tester's response to a question asked during live shadowing. " +
|
|
1275
|
-
"Returns the tester's screenshot, highlight captures, and voice note
|
|
1431
|
+
"Returns the tester's screenshot, highlight captures, and transcribed voice note.\n\n" +
|
|
1432
|
+
"IMPORTANT: The tester needs time to record their answer (10-60 seconds). " +
|
|
1433
|
+
"Instead of calling this tool repeatedly, prefer polling watch_live_session — " +
|
|
1434
|
+
"you will see a 'question-response-received' event when the response arrives. " +
|
|
1435
|
+
"Then call this tool once to get the full response with transcription and images.",
|
|
1276
1436
|
{
|
|
1277
1437
|
question_id: z.string().describe("The question ID returned by ask_tester_question."),
|
|
1278
1438
|
},
|
|
@@ -1280,11 +1440,22 @@ server.tool(
|
|
|
1280
1440
|
const response = questionResponses.get(question_id);
|
|
1281
1441
|
|
|
1282
1442
|
if (!response) {
|
|
1443
|
+
// Check how long we've been waiting
|
|
1444
|
+
const pending = pendingQuestions.find((q) => q.questionId === question_id);
|
|
1445
|
+
const waitingSec = pending ? Math.round((Date.now() - pending.sentAt) / 1000) : null;
|
|
1283
1446
|
return {
|
|
1284
1447
|
content: [
|
|
1285
1448
|
{
|
|
1286
1449
|
type: "text",
|
|
1287
|
-
text:
|
|
1450
|
+
text: [
|
|
1451
|
+
"No response yet." +
|
|
1452
|
+
(waitingSec !== null ? ` Waiting for ${waitingSec}s.` : ""),
|
|
1453
|
+
"The tester may still be recording their voice answer.",
|
|
1454
|
+
"",
|
|
1455
|
+
"TIP: Instead of polling this tool, go back to polling watch_live_session — " +
|
|
1456
|
+
"a 'question-response-received' event will appear when the tester responds. " +
|
|
1457
|
+
"Then call this tool once to retrieve the full answer.",
|
|
1458
|
+
].join("\n"),
|
|
1288
1459
|
},
|
|
1289
1460
|
],
|
|
1290
1461
|
};
|
|
@@ -1345,20 +1516,34 @@ server.tool(
|
|
|
1345
1516
|
}
|
|
1346
1517
|
|
|
1347
1518
|
if (response.voiceBlobDataUrl) {
|
|
1348
|
-
|
|
1349
|
-
if (
|
|
1519
|
+
// Use pre-transcribed result if available (from eager transcription on response arrival)
|
|
1520
|
+
if (response.transcription) {
|
|
1350
1521
|
content.push({
|
|
1351
1522
|
type: "text",
|
|
1352
|
-
text: `Voice transcription:\n${
|
|
1523
|
+
text: `Voice transcription:\n${response.transcription}`,
|
|
1353
1524
|
});
|
|
1354
1525
|
} else {
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
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
|
+
}
|
|
1359
1539
|
}
|
|
1360
1540
|
}
|
|
1361
1541
|
|
|
1542
|
+
content.push({
|
|
1543
|
+
type: "text",
|
|
1544
|
+
text: "\nIMPORTANT: Resume polling watch_live_session to continue watching the session.",
|
|
1545
|
+
});
|
|
1546
|
+
|
|
1362
1547
|
// Clean up after retrieval
|
|
1363
1548
|
questionResponses.delete(question_id);
|
|
1364
1549
|
|
|
@@ -1420,6 +1605,50 @@ server.tool(
|
|
|
1420
1605
|
},
|
|
1421
1606
|
);
|
|
1422
1607
|
|
|
1608
|
+
// Tool: get_live_session_summary
|
|
1609
|
+
server.tool(
|
|
1610
|
+
"get_live_session_summary",
|
|
1611
|
+
"Get a summary of the live shadowing session including all agent-tester interactions. " +
|
|
1612
|
+
"Call this after the session ends (sessionActive becomes false) to get the complete interaction log " +
|
|
1613
|
+
"for incorporating into your analysis. This includes all questions asked, tester responses, " +
|
|
1614
|
+
"voice transcriptions, and voice marker transcriptions from the session.",
|
|
1615
|
+
{},
|
|
1616
|
+
async () => {
|
|
1617
|
+
if (!liveSessionMeta && liveInteractions.length === 0) {
|
|
1618
|
+
return toolError("No live session data available. Start a live shadowing session first.");
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
// Collect voice-transcription events from the event buffer
|
|
1622
|
+
const voiceTranscriptions = liveEvents
|
|
1623
|
+
.filter((e) => e.eventType === "voice-transcription" && e.data?.transcription)
|
|
1624
|
+
.map((e) => ({
|
|
1625
|
+
timestamp: e.ts,
|
|
1626
|
+
markerId: e.data.markerId,
|
|
1627
|
+
transcription: e.data.transcription,
|
|
1628
|
+
}));
|
|
1629
|
+
|
|
1630
|
+
const summary = {
|
|
1631
|
+
session: liveSessionMeta,
|
|
1632
|
+
sessionActive: liveSessionActive,
|
|
1633
|
+
interactionLog: liveInteractions,
|
|
1634
|
+
voiceMarkerTranscriptions: voiceTranscriptions,
|
|
1635
|
+
totalEvents: liveEvents.length,
|
|
1636
|
+
guidance: liveSessionActive
|
|
1637
|
+
? "Session is still active. Resume polling watch_live_session."
|
|
1638
|
+
: "Session has ended. Use this interaction log and voice transcriptions to enrich your analysis of the exported session package.",
|
|
1639
|
+
};
|
|
1640
|
+
|
|
1641
|
+
return {
|
|
1642
|
+
content: [
|
|
1643
|
+
{
|
|
1644
|
+
type: "text",
|
|
1645
|
+
text: JSON.stringify(summary, null, 2),
|
|
1646
|
+
},
|
|
1647
|
+
],
|
|
1648
|
+
};
|
|
1649
|
+
},
|
|
1650
|
+
);
|
|
1651
|
+
|
|
1423
1652
|
// ---------------------------------------------------------------------------
|
|
1424
1653
|
// Startup
|
|
1425
1654
|
// ---------------------------------------------------------------------------
|