tracegist-mcp-bridge 0.2.8 → 0.2.12
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/README.md +4 -0
- package/bin/lib.mjs +53 -8
- package/bin/tracegist-mcp-bridge.mjs +127 -59
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,6 +43,10 @@ Set `TRACEGIST_DIR` to change where the bridge looks for packages (defaults to `
|
|
|
43
43
|
- `read_tracegist_package_file` — read any text file from inside the ZIP without writing to disk (e.g. `network/api-requests.jsonl`, the Playwright repro script)
|
|
44
44
|
- `extract_tracegist_package_file` — write a binary or text file from the ZIP to a local directory
|
|
45
45
|
- `transcribe_tracegist_package_voice_notes` — transcribe voice notes using local Python Whisper
|
|
46
|
+
- `watch_live_session` — poll real-time session events while recording is active
|
|
47
|
+
- `get_live_screenshot` — request and retrieve a screenshot of the current tab
|
|
48
|
+
- `ask_tester_question` — send a question to the tester (shown as a browser toast, max 200 chars)
|
|
49
|
+
- `get_tester_response` — retrieve the tester's voice + screenshot answer
|
|
46
50
|
|
|
47
51
|
## Package contents
|
|
48
52
|
|
package/bin/lib.mjs
CHANGED
|
@@ -11,14 +11,17 @@ export function normalizeZipBaseName(entryName) {
|
|
|
11
11
|
return path.posix.basename(entryName).toLowerCase();
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export function renderPackagesText(packages, directory) {
|
|
14
|
+
export function renderPackagesText(packages, directory, totalCount = null, hasMore = false, offset = 0) {
|
|
15
15
|
if (packages.length === 0) {
|
|
16
16
|
return [
|
|
17
17
|
`No TraceGist package zips found in ${directory}.`,
|
|
18
18
|
"Expected naming pattern: tracegist-...-package.zip",
|
|
19
19
|
].join("\n");
|
|
20
20
|
}
|
|
21
|
-
const
|
|
21
|
+
const countLabel = totalCount != null
|
|
22
|
+
? `Showing ${packages.length} of ${totalCount} TraceGist package(s) in ${directory}:`
|
|
23
|
+
: `Found ${packages.length} TraceGist package(s) in ${directory}:`;
|
|
24
|
+
const lines = [countLabel, ""];
|
|
22
25
|
for (const pkg of packages) {
|
|
23
26
|
lines.push(
|
|
24
27
|
`- ${pkg.name}`,
|
|
@@ -28,6 +31,16 @@ export function renderPackagesText(packages, directory) {
|
|
|
28
31
|
"",
|
|
29
32
|
);
|
|
30
33
|
}
|
|
34
|
+
if (hasMore) {
|
|
35
|
+
lines.push(
|
|
36
|
+
`More packages available. Use offset: ${offset + packages.length} to see the next page.`,
|
|
37
|
+
"",
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
lines.push(
|
|
41
|
+
"Each package includes a ready-to-run Playwright repro script.",
|
|
42
|
+
"Call get_tracegist_handoff_markdown (no section param) on a package to see the TOC with repro script paths.",
|
|
43
|
+
);
|
|
31
44
|
return lines.join("\n");
|
|
32
45
|
}
|
|
33
46
|
|
|
@@ -74,7 +87,8 @@ const SECTION_HINTS = {
|
|
|
74
87
|
"user interaction timeline": "Full click/fill/navigation sequence — key for reproduction",
|
|
75
88
|
"session context": "Page URL, title, session metadata",
|
|
76
89
|
"session environment": "Browser, viewport, OS — match for reproduction",
|
|
77
|
-
"package files":
|
|
90
|
+
"package files":
|
|
91
|
+
"ACTION: Read the Python repro script path here — use read_tracegist_package_file before writing any automation",
|
|
78
92
|
"marker-to-file mapping": "Which screenshots/voice files belong to each marker",
|
|
79
93
|
"backend log correlation": "Absolute timestamps for server-side log alignment",
|
|
80
94
|
"full session console timeline": "All console output (deep exports only)",
|
|
@@ -89,20 +103,54 @@ const SECTION_HINTS = {
|
|
|
89
103
|
"marker visual evidence": "Screenshot references for each marker",
|
|
90
104
|
"environment at marker time": "Environment snapshot at a specific marker",
|
|
91
105
|
"marker timeline logs (context window)": "Logs within the ±5 s marker window",
|
|
106
|
+
"webapp testing reproduction":
|
|
107
|
+
"ACTION: Ready-to-run Python repro script path + key selectors — read before writing any automation",
|
|
108
|
+
"tester intent summary": "Classified intent: specifications, issues found, observations",
|
|
109
|
+
"iterative context": "Previous session reference, verification checklist for build-test cycle",
|
|
110
|
+
"tier 0: quick summary": "Session overview, intent, reproduction command (~200 tokens)",
|
|
111
|
+
"tier 1: findings": "Marker timeline with structured findings (~500 tokens)",
|
|
112
|
+
"tier 2: full context": "Interaction timeline, network/console context, environment",
|
|
113
|
+
"tier 3: deep diagnostics": "Full session timelines (deep profile only)",
|
|
92
114
|
};
|
|
93
115
|
|
|
94
116
|
function getSectionHint(sectionName) {
|
|
95
117
|
return SECTION_HINTS[sectionName.toLowerCase()] || "Additional section";
|
|
96
118
|
}
|
|
97
119
|
|
|
98
|
-
export function renderSectionToc(sections, zipPath) {
|
|
120
|
+
export function renderSectionToc(sections, zipPath, manifest = null) {
|
|
99
121
|
const totalChars = sections.reduce((sum, s) => sum + s.content.length, 0);
|
|
100
122
|
const lines = [
|
|
101
123
|
`Handoff document for ${path.basename(zipPath)} — ${sections.length} sections, ${totalChars.toLocaleString()} chars total.`,
|
|
102
124
|
"",
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
// Prominent callout BEFORE the table — shown before any section is read
|
|
128
|
+
const pythonScript = manifest?.pythonPlaywrightScriptPath;
|
|
129
|
+
const tsScript = manifest?.playwrightScriptPath;
|
|
130
|
+
if (pythonScript || tsScript) {
|
|
131
|
+
lines.push(
|
|
132
|
+
"**Playwright repro script is ready — read it before writing any automation code:**",
|
|
133
|
+
);
|
|
134
|
+
if (pythonScript) {
|
|
135
|
+
lines.push(
|
|
136
|
+
`- Python (webapp-testing skill): \`read_tracegist_package_file(zipPath, "${pythonScript}")\``,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (tsScript) {
|
|
140
|
+
lines.push(
|
|
141
|
+
`- TypeScript (Playwright Test): \`read_tracegist_package_file(zipPath, "${tsScript}")\``,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
lines.push("");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
lines.push(
|
|
148
|
+
"**Reading guide:** For analysis, start with Session Triage → Marker Timeline → Notable Anomalies.",
|
|
149
|
+
"For reproduction, start with the Playwright repro script above → Session Environment → User Interaction Timeline.",
|
|
150
|
+
"",
|
|
103
151
|
"| # | Section | Size | Purpose |",
|
|
104
152
|
"|---|---------|------|---------|",
|
|
105
|
-
|
|
153
|
+
);
|
|
106
154
|
for (let i = 0; i < sections.length; i++) {
|
|
107
155
|
const hint = getSectionHint(sections[i].name);
|
|
108
156
|
lines.push(
|
|
@@ -110,9 +158,6 @@ export function renderSectionToc(sections, zipPath) {
|
|
|
110
158
|
);
|
|
111
159
|
}
|
|
112
160
|
lines.push(
|
|
113
|
-
"",
|
|
114
|
-
"**Reading guide:** For analysis, start with Session Triage → Marker Timeline → Notable Anomalies.",
|
|
115
|
-
"For reproduction, start with Session Environment → User Interaction Timeline → Package Files (Playwright script path).",
|
|
116
161
|
"",
|
|
117
162
|
'Pass `section` with a section name (e.g. "Marker Timeline") to retrieve its full content.',
|
|
118
163
|
);
|
|
@@ -85,7 +85,7 @@ async function checkWhisperDependencies() {
|
|
|
85
85
|
return warnings;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
async function listTraceGistPackages(directory, limit) {
|
|
88
|
+
async function listTraceGistPackages(directory, limit, offset = 0) {
|
|
89
89
|
const entries = await fs.readdir(directory, { withFileTypes: true });
|
|
90
90
|
const zipNames = entries
|
|
91
91
|
.filter((entry) => entry.isFile() && isTraceGistPackageFile(entry.name))
|
|
@@ -105,7 +105,9 @@ async function listTraceGistPackages(directory, limit) {
|
|
|
105
105
|
);
|
|
106
106
|
|
|
107
107
|
packages.sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt));
|
|
108
|
-
|
|
108
|
+
const totalCount = packages.length;
|
|
109
|
+
const sliced = packages.slice(offset, offset + limit);
|
|
110
|
+
return { packages: sliced, totalCount, hasMore: offset + limit < totalCount };
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
async function readZipEntries(zipPath) {
|
|
@@ -569,14 +571,26 @@ server.registerTool(
|
|
|
569
571
|
.max(200)
|
|
570
572
|
.optional()
|
|
571
573
|
.describe("Maximum number of packages to return (default: 20)."),
|
|
574
|
+
offset: z
|
|
575
|
+
.number()
|
|
576
|
+
.int()
|
|
577
|
+
.min(0)
|
|
578
|
+
.optional()
|
|
579
|
+
.describe("Number of packages to skip for pagination (default: 0)."),
|
|
572
580
|
}),
|
|
573
581
|
},
|
|
574
|
-
async ({ directory, limit = 20 }) => {
|
|
582
|
+
async ({ directory, limit = 20, offset = 0 }) => {
|
|
575
583
|
try {
|
|
576
584
|
const searchDir = directory || DEFAULT_DOWNLOADS_DIR;
|
|
577
|
-
const packages = await listTraceGistPackages(
|
|
585
|
+
const { packages, totalCount, hasMore } = await listTraceGistPackages(
|
|
586
|
+
searchDir,
|
|
587
|
+
limit,
|
|
588
|
+
offset,
|
|
589
|
+
);
|
|
578
590
|
return {
|
|
579
|
-
content: [
|
|
591
|
+
content: [
|
|
592
|
+
{ type: "text", text: renderPackagesText(packages, searchDir, totalCount, hasMore, offset) },
|
|
593
|
+
],
|
|
580
594
|
};
|
|
581
595
|
} catch (err) {
|
|
582
596
|
return toolError(err);
|
|
@@ -715,7 +729,7 @@ server.registerTool(
|
|
|
715
729
|
content: [
|
|
716
730
|
{
|
|
717
731
|
type: "text",
|
|
718
|
-
text: `# Source: ${handoffEntryName}\n\n${renderSectionToc(sections, zipPath)}`,
|
|
732
|
+
text: `# Source: ${handoffEntryName}\n\n${renderSectionToc(sections, zipPath, manifest)}`,
|
|
719
733
|
},
|
|
720
734
|
],
|
|
721
735
|
};
|
|
@@ -928,7 +942,9 @@ async function transcribeLiveVoice(voiceBlobDataUrl) {
|
|
|
928
942
|
const text = result.choices?.[0]?.message?.content?.trim();
|
|
929
943
|
return text ? { transcription: text } : { error: "Empty transcription response" };
|
|
930
944
|
} catch (err) {
|
|
931
|
-
return {
|
|
945
|
+
return {
|
|
946
|
+
error: `OpenRouter transcription failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
947
|
+
};
|
|
932
948
|
}
|
|
933
949
|
}
|
|
934
950
|
|
|
@@ -950,7 +966,9 @@ async function transcribeLiveVoice(voiceBlobDataUrl) {
|
|
|
950
966
|
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
951
967
|
}
|
|
952
968
|
} catch (err) {
|
|
953
|
-
return {
|
|
969
|
+
return {
|
|
970
|
+
error: `Whisper transcription failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
971
|
+
};
|
|
954
972
|
}
|
|
955
973
|
}
|
|
956
974
|
|
|
@@ -1233,24 +1251,33 @@ server.resource(
|
|
|
1233
1251
|
);
|
|
1234
1252
|
|
|
1235
1253
|
// Tool: watch_live_session
|
|
1236
|
-
server.
|
|
1254
|
+
server.registerTool(
|
|
1237
1255
|
"watch_live_session",
|
|
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).",
|
|
1249
1256
|
{
|
|
1250
|
-
|
|
1251
|
-
.number
|
|
1252
|
-
|
|
1253
|
-
|
|
1257
|
+
description:
|
|
1258
|
+
"Watch a live TraceGist shadowing session in real-time. Returns buffered events since the given sequence number.\n\n" +
|
|
1259
|
+
"IMPORTANT POLLING BEHAVIOR:\n" +
|
|
1260
|
+
"- You MUST keep calling this tool every 2-4 seconds in a loop until sessionActive becomes false.\n" +
|
|
1261
|
+
"- Do NOT stop polling just because there are no new events — the tester is still active.\n" +
|
|
1262
|
+
"- When you see 'voice-transcription' events, the tester recorded a voice marker — read the transcription and react to it.\n" +
|
|
1263
|
+
"- When you see 'question-response-received' events, use get_tester_response to retrieve the full answer with images.\n" +
|
|
1264
|
+
"- When you see 'question-response-transcribed' events, the tester's voice response has been transcribed — read it for context.\n" +
|
|
1265
|
+
"- Marker events with hasPendingVoice=true will have their voiceTranscription field populated once transcription completes.\n" +
|
|
1266
|
+
"- The tester may set voice markers at any time to communicate with you — keep watching for them.\n" +
|
|
1267
|
+
"- If you need clarification, use ask_tester_question to show a toast notification to the tester.\n" +
|
|
1268
|
+
"- Only stop polling when sessionActive is false (the recording ended).",
|
|
1269
|
+
annotations: {
|
|
1270
|
+
readOnlyHint: true,
|
|
1271
|
+
destructiveHint: false,
|
|
1272
|
+
idempotentHint: true,
|
|
1273
|
+
openWorldHint: false,
|
|
1274
|
+
},
|
|
1275
|
+
inputSchema: z.object({
|
|
1276
|
+
since_seq: z
|
|
1277
|
+
.number()
|
|
1278
|
+
.optional()
|
|
1279
|
+
.describe("Return events after this sequence number. Omit for all buffered events."),
|
|
1280
|
+
}),
|
|
1254
1281
|
},
|
|
1255
1282
|
async ({ since_seq }) => {
|
|
1256
1283
|
if (!liveSessionActive && liveEvents.length === 0) {
|
|
@@ -1273,7 +1300,11 @@ server.tool(
|
|
|
1273
1300
|
}
|
|
1274
1301
|
}
|
|
1275
1302
|
const enrichedEvents = events.map((e) => {
|
|
1276
|
-
if (
|
|
1303
|
+
if (
|
|
1304
|
+
e.eventType === "marker" &&
|
|
1305
|
+
e.data?.markerId &&
|
|
1306
|
+
transcriptionsByMarkerId.has(e.data.markerId)
|
|
1307
|
+
) {
|
|
1277
1308
|
return {
|
|
1278
1309
|
...e,
|
|
1279
1310
|
data: {
|
|
@@ -1306,8 +1337,7 @@ server.tool(
|
|
|
1306
1337
|
},
|
|
1307
1338
|
{
|
|
1308
1339
|
tool: "get_live_screenshot",
|
|
1309
|
-
description:
|
|
1310
|
-
"Capture a screenshot of the tester's current browser tab.",
|
|
1340
|
+
description: "Capture a screenshot of the tester's current browser tab.",
|
|
1311
1341
|
},
|
|
1312
1342
|
];
|
|
1313
1343
|
if (pendingQuestions.length > 0) {
|
|
@@ -1319,7 +1349,10 @@ server.tool(
|
|
|
1319
1349
|
}
|
|
1320
1350
|
} else {
|
|
1321
1351
|
// Session ended — include full interaction log and voice transcriptions
|
|
1322
|
-
if (
|
|
1352
|
+
if (
|
|
1353
|
+
liveInteractions.length > 0 ||
|
|
1354
|
+
liveEvents.some((e) => e.eventType === "voice-transcription")
|
|
1355
|
+
) {
|
|
1323
1356
|
responseData.sessionEndedNote =
|
|
1324
1357
|
"Session has ended. The interactionLog and voiceMarkerTranscriptions below contain all agent-tester exchanges from this session. " +
|
|
1325
1358
|
"Include these in your analysis — they represent collaborative context between agent and tester. " +
|
|
@@ -1353,20 +1386,29 @@ server.tool(
|
|
|
1353
1386
|
);
|
|
1354
1387
|
|
|
1355
1388
|
// Tool: ask_tester_question
|
|
1356
|
-
server.
|
|
1389
|
+
server.registerTool(
|
|
1357
1390
|
"ask_tester_question",
|
|
1358
|
-
"Ask the tester a short, concise question during a live shadowing session. " +
|
|
1359
|
-
"The question appears as a toast notification in their browser. " +
|
|
1360
|
-
"The tester responds with a voice marker (Alt+Shift+M) and optional highlight captures (Alt+Shift+S). " +
|
|
1361
|
-
"Use get_tester_response to check for their answer.",
|
|
1362
1391
|
{
|
|
1363
|
-
|
|
1364
|
-
.
|
|
1365
|
-
.
|
|
1366
|
-
.
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1392
|
+
description:
|
|
1393
|
+
"Ask the tester a short, concise question during a live shadowing session. " +
|
|
1394
|
+
"The question appears as a toast notification in their browser. " +
|
|
1395
|
+
"The tester responds with a voice marker (Alt+Shift+M) and optional highlight captures (Alt+Shift+S). " +
|
|
1396
|
+
"Use get_tester_response to check for their answer.",
|
|
1397
|
+
annotations: {
|
|
1398
|
+
readOnlyHint: false,
|
|
1399
|
+
destructiveHint: false,
|
|
1400
|
+
idempotentHint: false,
|
|
1401
|
+
openWorldHint: true,
|
|
1402
|
+
},
|
|
1403
|
+
inputSchema: z.object({
|
|
1404
|
+
question: z
|
|
1405
|
+
.string()
|
|
1406
|
+
.max(200)
|
|
1407
|
+
.describe(
|
|
1408
|
+
"Short, concise question for the tester (max 200 chars). " +
|
|
1409
|
+
"Example: 'Can you click the Save button again?' or 'Does the error appear with a different email?'",
|
|
1410
|
+
),
|
|
1411
|
+
}),
|
|
1370
1412
|
},
|
|
1371
1413
|
async ({ question }) => {
|
|
1372
1414
|
if (!liveSessionActive || !activeConnection) {
|
|
@@ -1425,16 +1467,25 @@ server.tool(
|
|
|
1425
1467
|
);
|
|
1426
1468
|
|
|
1427
1469
|
// Tool: get_tester_response
|
|
1428
|
-
server.
|
|
1470
|
+
server.registerTool(
|
|
1429
1471
|
"get_tester_response",
|
|
1430
|
-
"Check for the tester's response to a question asked during live shadowing. " +
|
|
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.",
|
|
1436
1472
|
{
|
|
1437
|
-
|
|
1473
|
+
description:
|
|
1474
|
+
"Check for the tester's response to a question asked during live shadowing. " +
|
|
1475
|
+
"Returns the tester's screenshot, highlight captures, and transcribed voice note.\n\n" +
|
|
1476
|
+
"IMPORTANT: The tester needs time to record their answer (10-60 seconds). " +
|
|
1477
|
+
"Instead of calling this tool repeatedly, prefer polling watch_live_session — " +
|
|
1478
|
+
"you will see a 'question-response-received' event when the response arrives. " +
|
|
1479
|
+
"Then call this tool once to get the full response with transcription and images.",
|
|
1480
|
+
annotations: {
|
|
1481
|
+
readOnlyHint: true,
|
|
1482
|
+
destructiveHint: false,
|
|
1483
|
+
idempotentHint: true,
|
|
1484
|
+
openWorldHint: false,
|
|
1485
|
+
},
|
|
1486
|
+
inputSchema: z.object({
|
|
1487
|
+
question_id: z.string().describe("The question ID returned by ask_tester_question."),
|
|
1488
|
+
}),
|
|
1438
1489
|
},
|
|
1439
1490
|
async ({ question_id }) => {
|
|
1440
1491
|
const response = questionResponses.get(question_id);
|
|
@@ -1448,8 +1499,7 @@ server.tool(
|
|
|
1448
1499
|
{
|
|
1449
1500
|
type: "text",
|
|
1450
1501
|
text: [
|
|
1451
|
-
"No response yet." +
|
|
1452
|
-
(waitingSec !== null ? ` Waiting for ${waitingSec}s.` : ""),
|
|
1502
|
+
"No response yet." + (waitingSec !== null ? ` Waiting for ${waitingSec}s.` : ""),
|
|
1453
1503
|
"The tester may still be recording their voice answer.",
|
|
1454
1504
|
"",
|
|
1455
1505
|
"TIP: Instead of polling this tool, go back to polling watch_live_session — " +
|
|
@@ -1552,10 +1602,19 @@ server.tool(
|
|
|
1552
1602
|
);
|
|
1553
1603
|
|
|
1554
1604
|
// Tool: get_live_screenshot
|
|
1555
|
-
server.
|
|
1605
|
+
server.registerTool(
|
|
1556
1606
|
"get_live_screenshot",
|
|
1557
|
-
|
|
1558
|
-
|
|
1607
|
+
{
|
|
1608
|
+
description:
|
|
1609
|
+
"Capture a screenshot of the tester's current browser tab during a live shadowing session.",
|
|
1610
|
+
annotations: {
|
|
1611
|
+
readOnlyHint: true,
|
|
1612
|
+
destructiveHint: false,
|
|
1613
|
+
idempotentHint: true,
|
|
1614
|
+
openWorldHint: false,
|
|
1615
|
+
},
|
|
1616
|
+
inputSchema: z.object({}),
|
|
1617
|
+
},
|
|
1559
1618
|
async () => {
|
|
1560
1619
|
if (!liveSessionActive || !activeConnection) {
|
|
1561
1620
|
return toolError("No active live shadowing session or extension not connected.");
|
|
@@ -1606,13 +1665,22 @@ server.tool(
|
|
|
1606
1665
|
);
|
|
1607
1666
|
|
|
1608
1667
|
// Tool: get_live_session_summary
|
|
1609
|
-
server.
|
|
1668
|
+
server.registerTool(
|
|
1610
1669
|
"get_live_session_summary",
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1670
|
+
{
|
|
1671
|
+
description:
|
|
1672
|
+
"Get a summary of the live shadowing session including all agent-tester interactions. " +
|
|
1673
|
+
"Call this after the session ends (sessionActive becomes false) to get the complete interaction log " +
|
|
1674
|
+
"for incorporating into your analysis. This includes all questions asked, tester responses, " +
|
|
1675
|
+
"voice transcriptions, and voice marker transcriptions from the session.",
|
|
1676
|
+
annotations: {
|
|
1677
|
+
readOnlyHint: true,
|
|
1678
|
+
destructiveHint: false,
|
|
1679
|
+
idempotentHint: true,
|
|
1680
|
+
openWorldHint: false,
|
|
1681
|
+
},
|
|
1682
|
+
inputSchema: z.object({}),
|
|
1683
|
+
},
|
|
1616
1684
|
async () => {
|
|
1617
1685
|
if (!liveSessionMeta && liveInteractions.length === 0) {
|
|
1618
1686
|
return toolError("No live session data available. Start a live shadowing session first.");
|