@playdrop/playdrop-cli 0.12.13 → 0.12.15

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "0.12.8",
3
- "build": 5,
3
+ "build": 7,
4
4
  "runtimeSdkVersion": "0.12.5",
5
5
  "runtimeSdkBuild": 2,
6
6
  "clients": {
@@ -66,7 +66,7 @@ function extensionForMediaType(mediaType) {
66
66
  }
67
67
  function createWorkerInstrumentScreenshotCollector(workspaceDir) {
68
68
  const resolvedWorkspace = node_path_1.default.resolve(workspaceDir);
69
- const parser = new types_1.ClaudeChromeScreenshotStreamParser((screenshot) => {
69
+ const store = (screenshot) => {
70
70
  const buffer = Buffer.from(screenshot.dataBase64, 'base64');
71
71
  if (buffer.length <= 0) {
72
72
  throw new Error(`invalid_claude_screenshot_payload:${screenshot.screenshotId}`);
@@ -89,10 +89,14 @@ function createWorkerInstrumentScreenshotCollector(workspaceDir) {
89
89
  height: screenshot.height,
90
90
  };
91
91
  writeJsonAtomic(screenshotIndexPath(resolvedWorkspace), index);
92
- });
92
+ };
93
+ // Both parsers watch the same stream: their tool names and id namespaces are disjoint, so a
94
+ // session only ever populates the store through the browser integration it actually used.
95
+ const chromeParser = new types_1.ClaudeChromeScreenshotStreamParser(store);
96
+ const playwrightParser = new types_1.PlaywrightMcpScreenshotStreamParser(store);
93
97
  return {
94
- push: (chunk) => parser.push(chunk),
95
- finish: () => parser.finish(),
98
+ push: (chunk) => chromeParser.push(chunk) + playwrightParser.push(chunk),
99
+ finish: () => chromeParser.finish() + playwrightParser.finish(),
96
100
  };
97
101
  }
98
102
  function normalizeEvidenceName(value) {
@@ -68,10 +68,13 @@ export declare function buildClaudeExecArgs(input: {
68
68
  model: string;
69
69
  effort?: string | null;
70
70
  chrome?: boolean;
71
+ playwrightMcp?: boolean;
71
72
  workspaceDir?: string | null;
72
73
  denyReadRoots?: string[] | null;
73
74
  pluginDir?: string | null;
74
75
  }): string[];
76
+ export declare const PLAYWRIGHT_MCP_VERSION = "0.0.78";
77
+ export declare function buildPlaywrightMcpServerConfig(): Record<string, unknown>;
75
78
  export declare function buildClaudePermissionSettings(input?: {
76
79
  workspaceDir?: string | null;
77
80
  denyReadRoots?: string[];
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.DEFAULT_TRANSCRIPT_FLUSH_INTERVAL_MS = exports.DEFAULT_CODEX_LOG_TAIL_CHARS = exports.DEFAULT_WORKER_TOKEN_CAP = exports.DEFAULT_CODEX_TIMEOUT_MS = void 0;
6
+ exports.PLAYWRIGHT_MCP_VERSION = exports.DEFAULT_TRANSCRIPT_FLUSH_INTERVAL_MS = exports.DEFAULT_CODEX_LOG_TAIL_CHARS = exports.DEFAULT_WORKER_TOKEN_CAP = exports.DEFAULT_CODEX_TIMEOUT_MS = void 0;
7
7
  exports.readPositiveEnvInt = readPositiveEnvInt;
8
8
  exports.readEnvBoolean = readEnvBoolean;
9
9
  exports.extractAgentTokenUsage = extractAgentTokenUsage;
@@ -16,6 +16,7 @@ exports.runLoggedProcess = runLoggedProcess;
16
16
  exports.buildCodexExecArgs = buildCodexExecArgs;
17
17
  exports.readCodexSandboxMode = readCodexSandboxMode;
18
18
  exports.buildClaudeExecArgs = buildClaudeExecArgs;
19
+ exports.buildPlaywrightMcpServerConfig = buildPlaywrightMcpServerConfig;
19
20
  exports.buildClaudePermissionSettings = buildClaudePermissionSettings;
20
21
  exports.buildClaudeDeniedPermissionRules = buildClaudeDeniedPermissionRules;
21
22
  exports.buildWorkerChildEnv = buildWorkerChildEnv;
@@ -347,7 +348,14 @@ async function runLoggedProcess(input) {
347
348
  return;
348
349
  }
349
350
  const chunks = pendingTranscriptChunks.splice(0, pendingTranscriptChunks.length);
350
- await input.onTranscriptChunks(chunks);
351
+ try {
352
+ await input.onTranscriptChunks(chunks);
353
+ }
354
+ catch (error) {
355
+ // Transcript upload is telemetry, never task-critical: requeue in order and retry with the next flush.
356
+ pendingTranscriptChunks.unshift(...chunks);
357
+ console.error(`warning: transcript chunk upload failed, will retry with next flush: ${error instanceof Error ? error.message : String(error)}`);
358
+ }
351
359
  };
352
360
  const queueTranscriptFlush = () => {
353
361
  transcriptFlushPromise = transcriptFlushPromise.then(flushTranscriptChunks, flushTranscriptChunks);
@@ -416,6 +424,13 @@ async function runLoggedProcess(input) {
416
424
  }
417
425
  void (async () => {
418
426
  await queueTranscriptFlush();
427
+ if (pendingTranscriptChunks.length > 0) {
428
+ // Last flush failed: retry once, then drop with a warning. Missing transcript chunks never fail the task.
429
+ await flushTranscriptChunks();
430
+ }
431
+ if (pendingTranscriptChunks.length > 0) {
432
+ console.error(`warning: dropped ${pendingTranscriptChunks.length} transcript chunk(s) after final retry; transcript is telemetry, continuing`);
433
+ }
419
434
  if (transcriptFlushError) {
420
435
  throwLoggedProcessError(transcriptFlushError);
421
436
  }
@@ -474,18 +489,23 @@ function buildClaudeExecArgs(input) {
474
489
  if (!model) {
475
490
  throw new Error('agent_task_assignment_model_missing');
476
491
  }
492
+ if (input.chrome && input.playwrightMcp) {
493
+ throw new Error('claude_exec_browser_mode_conflict');
494
+ }
477
495
  const permissionSettings = buildClaudePermissionSettings({
478
496
  workspaceDir: input.workspaceDir,
479
497
  denyReadRoots: input.denyReadRoots ?? [],
480
498
  });
481
- const permissionMode = input.chrome ? 'bypassPermissions' : 'acceptEdits';
499
+ const permissionMode = input.chrome || input.playwrightMcp ? 'bypassPermissions' : 'acceptEdits';
482
500
  const tools = input.chrome
483
501
  ? [...CLAUDE_BASE_TOOL_NAMES, ...CLAUDE_CHROME_TOOL_NAMES].join(',')
484
502
  : CLAUDE_BASE_TOOL_NAMES.join(',');
485
503
  const allowedPermissionRules = buildClaudeAllowedPermissionRules(input.workspaceDir);
486
504
  const allowedTools = input.chrome
487
505
  ? [...allowedPermissionRules, ...CLAUDE_CHROME_TOOL_NAMES]
488
- : allowedPermissionRules;
506
+ : input.playwrightMcp
507
+ ? [...allowedPermissionRules, PLAYWRIGHT_MCP_ALLOWED_TOOL_RULE]
508
+ : allowedPermissionRules;
489
509
  const args = [
490
510
  '-p',
491
511
  '--safe-mode',
@@ -511,6 +531,9 @@ function buildClaudeExecArgs(input) {
511
531
  if (input.chrome) {
512
532
  args.unshift('--chrome');
513
533
  }
534
+ if (input.playwrightMcp) {
535
+ args.push('--mcp-config', JSON.stringify(buildPlaywrightMcpServerConfig()));
536
+ }
514
537
  const pluginDir = typeof input.pluginDir === 'string' ? input.pluginDir.trim() : '';
515
538
  if (pluginDir) {
516
539
  args.push('--plugin-dir', pluginDir);
@@ -529,6 +552,20 @@ const CLAUDE_BASE_TOOL_NAMES = [
529
552
  'Glob',
530
553
  'Grep',
531
554
  ];
555
+ // Pinned deliberately: agent-CLI and MCP versions on workers only change through releases,
556
+ // never through @latest drift (see the 2026-07-13 auto-update incident).
557
+ exports.PLAYWRIGHT_MCP_VERSION = '0.0.78';
558
+ const PLAYWRIGHT_MCP_ALLOWED_TOOL_RULE = 'mcp__playwright';
559
+ function buildPlaywrightMcpServerConfig() {
560
+ return {
561
+ mcpServers: {
562
+ playwright: {
563
+ command: 'npx',
564
+ args: ['-y', `@playwright/mcp@${exports.PLAYWRIGHT_MCP_VERSION}`, '--browser', 'chrome', '--isolated'],
565
+ },
566
+ },
567
+ };
568
+ }
532
569
  const CLAUDE_CHROME_TOOL_NAMES = [
533
570
  'mcp__claude-in-chrome__browser_batch',
534
571
  'mcp__claude-in-chrome__computer',
@@ -2102,7 +2102,7 @@ async function runClaude(input) {
2102
2102
  envName: input.envName,
2103
2103
  });
2104
2104
  const denyReadRoots = resolveClaudeDenyReadRoots(input.workspaceDir);
2105
- const screenshotCollector = input.enableChrome
2105
+ const screenshotCollector = input.enableChrome || input.enablePlaywrightMcp
2106
2106
  ? (0, instrument_evidence_1.createWorkerInstrumentScreenshotCollector)(input.workspaceDir)
2107
2107
  : null;
2108
2108
  const result = await (0, runtime_1.runLoggedProcess)({
@@ -2113,6 +2113,7 @@ async function runClaude(input) {
2113
2113
  denyReadRoots,
2114
2114
  pluginDir: input.taskPluginRoot,
2115
2115
  chrome: input.enableChrome,
2116
+ playwrightMcp: input.enablePlaywrightMcp,
2116
2117
  }),
2117
2118
  cwd: input.workspaceDir,
2118
2119
  env: (0, runtime_1.buildWorkerChildEnv)({
@@ -4090,7 +4091,8 @@ async function startWorker(options = {}) {
4090
4091
  binDir,
4091
4092
  eventDir,
4092
4093
  prompt,
4093
- enableChrome: task.kind === 'GAME_REVIEW' || task.kind === 'GAME_EVAL',
4094
+ enableChrome: false,
4095
+ enablePlaywrightMcp: task.kind === 'GAME_REVIEW' || task.kind === 'GAME_EVAL',
4094
4096
  envName: env,
4095
4097
  workerUsername: username,
4096
4098
  taskId: task.id,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "0.12.8",
3
- "build": 5,
3
+ "build": 7,
4
4
  "runtimeSdkVersion": "0.12.5",
5
5
  "runtimeSdkBuild": 2,
6
6
  "clients": {
@@ -26,6 +26,9 @@ export type InstrumentEvidenceManifest = {
26
26
  controlledTabId: number;
27
27
  captures: Partial<Record<InstrumentEvidenceName, InstrumentEvidenceCapture>>;
28
28
  };
29
+ export declare const PLAYWRIGHT_MCP_SCREENSHOT_TOOL = "mcp__playwright__browser_take_screenshot";
30
+ export declare const PLAYWRIGHT_MCP_CONTROLLED_TAB_ID = 1;
31
+ export declare function isInstrumentScreenshotId(value: string): boolean;
29
32
  export declare class ClaudeChromeScreenshotStreamParser {
30
33
  private readonly onScreenshot;
31
34
  private buffer;
@@ -38,4 +41,17 @@ export declare class ClaudeChromeScreenshotStreamParser {
38
41
  private recordToolUse;
39
42
  private recordToolResults;
40
43
  }
44
+ export declare class PlaywrightMcpScreenshotStreamParser {
45
+ private readonly onScreenshot;
46
+ private buffer;
47
+ private readonly pendingScreenshotToolUseIds;
48
+ private readonly handledToolUseIds;
49
+ private sequence;
50
+ constructor(onScreenshot: (screenshot: ClaudeChromeScreenshot) => void);
51
+ push(chunk: string): number;
52
+ finish(): number;
53
+ private processLine;
54
+ private recordToolUse;
55
+ private recordToolResults;
56
+ }
41
57
  //# sourceMappingURL=instrument-evidence.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"instrument-evidence.d.ts","sourceRoot":"","sources":["../src/instrument-evidence.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,yBAAyB,iDAK5B,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhF,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,YAAY,GAAG,WAAW,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,YAAY,GAAG,WAAW,CAAC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,sBAAsB,EAAE,yBAAyB,CAAC,CAAC,CAAC;CAC9E,CAAC;AAmDF,qBAAa,kCAAkC;IAKjC,OAAO,CAAC,QAAQ,CAAC,YAAY;IAJzC,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAA+B;IACzE,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAqB;gBAE7B,YAAY,EAAE,CAAC,UAAU,EAAE,sBAAsB,KAAK,IAAI;IAEvF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAqB3B,MAAM,IAAI,MAAM;IAMhB,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,iBAAiB;CAiE1B"}
1
+ {"version":3,"file":"instrument-evidence.d.ts","sourceRoot":"","sources":["../src/instrument-evidence.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,yBAAyB,iDAK5B,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhF,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,YAAY,GAAG,WAAW,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,YAAY,GAAG,WAAW,CAAC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,sBAAsB,EAAE,yBAAyB,CAAC,CAAC,CAAC;CAC9E,CAAC;AAMF,eAAO,MAAM,8BAA8B,6CAA6C,CAAC;AACzF,eAAO,MAAM,gCAAgC,IAAI,CAAC;AAGlD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE/D;AA+CD,qBAAa,kCAAkC;IAKjC,OAAO,CAAC,QAAQ,CAAC,YAAY;IAJzC,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAA+B;IACzE,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAqB;gBAE7B,YAAY,EAAE,CAAC,UAAU,EAAE,sBAAsB,KAAK,IAAI;IAEvF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAqB3B,MAAM,IAAI,MAAM;IAMhB,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,iBAAiB;CAiE1B;AAMD,qBAAa,mCAAmC;IAMlC,OAAO,CAAC,QAAQ,CAAC,YAAY;IALzC,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAqB;IACjE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAqB;IACvD,OAAO,CAAC,QAAQ,CAAK;gBAEQ,YAAY,EAAE,CAAC,UAAU,EAAE,sBAAsB,KAAK,IAAI;IAEvF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAqB3B,MAAM,IAAI,MAAM;IAMhB,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,iBAAiB;CA8C1B"}
@@ -18,7 +18,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
18
18
  var instrument_evidence_exports = {};
19
19
  __export(instrument_evidence_exports, {
20
20
  ClaudeChromeScreenshotStreamParser: () => ClaudeChromeScreenshotStreamParser,
21
- INSTRUMENT_EVIDENCE_NAMES: () => INSTRUMENT_EVIDENCE_NAMES
21
+ INSTRUMENT_EVIDENCE_NAMES: () => INSTRUMENT_EVIDENCE_NAMES,
22
+ PLAYWRIGHT_MCP_CONTROLLED_TAB_ID: () => PLAYWRIGHT_MCP_CONTROLLED_TAB_ID,
23
+ PLAYWRIGHT_MCP_SCREENSHOT_TOOL: () => PLAYWRIGHT_MCP_SCREENSHOT_TOOL,
24
+ PlaywrightMcpScreenshotStreamParser: () => PlaywrightMcpScreenshotStreamParser,
25
+ isInstrumentScreenshotId: () => isInstrumentScreenshotId
22
26
  });
23
27
  module.exports = __toCommonJS(instrument_evidence_exports);
24
28
  const INSTRUMENT_EVIDENCE_NAMES = [
@@ -30,6 +34,12 @@ const INSTRUMENT_EVIDENCE_NAMES = [
30
34
  const SCREENSHOT_ID_PATTERN = /^ss_[A-Za-z0-9_-]+$/;
31
35
  const SCREENSHOT_ID_IN_TEXT_PATTERN = /\bID:\s*(ss_[A-Za-z0-9_-]+)\b/g;
32
36
  const MAX_STREAM_LINE_LENGTH = 20 * 1024 * 1024;
37
+ const PLAYWRIGHT_MCP_SCREENSHOT_TOOL = "mcp__playwright__browser_take_screenshot";
38
+ const PLAYWRIGHT_MCP_CONTROLLED_TAB_ID = 1;
39
+ const PLAYWRIGHT_SCREENSHOT_ID_PATTERN = /^pw-[1-9][0-9]*$/;
40
+ function isInstrumentScreenshotId(value) {
41
+ return SCREENSHOT_ID_PATTERN.test(value) || PLAYWRIGHT_SCREENSHOT_ID_PATTERN.test(value);
42
+ }
33
43
  function asRecord(value) {
34
44
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
35
45
  }
@@ -208,8 +218,128 @@ class ClaudeChromeScreenshotStreamParser {
208
218
  return emitted;
209
219
  }
210
220
  }
221
+ class PlaywrightMcpScreenshotStreamParser {
222
+ onScreenshot;
223
+ buffer = "";
224
+ pendingScreenshotToolUseIds = /* @__PURE__ */ new Set();
225
+ handledToolUseIds = /* @__PURE__ */ new Set();
226
+ sequence = 0;
227
+ constructor(onScreenshot) {
228
+ this.onScreenshot = onScreenshot;
229
+ }
230
+ push(chunk) {
231
+ if (!chunk) {
232
+ return 0;
233
+ }
234
+ this.buffer += chunk;
235
+ if (this.buffer.length > MAX_STREAM_LINE_LENGTH && !this.buffer.includes("\n")) {
236
+ throw new Error("claude_stream_json_line_too_large");
237
+ }
238
+ let emitted = 0;
239
+ for (; ; ) {
240
+ const newlineIndex = this.buffer.indexOf("\n");
241
+ if (newlineIndex < 0) {
242
+ break;
243
+ }
244
+ const line = this.buffer.slice(0, newlineIndex);
245
+ this.buffer = this.buffer.slice(newlineIndex + 1);
246
+ emitted += this.processLine(line);
247
+ }
248
+ return emitted;
249
+ }
250
+ finish() {
251
+ const line = this.buffer;
252
+ this.buffer = "";
253
+ return line.trim() ? this.processLine(line) : 0;
254
+ }
255
+ processLine(line) {
256
+ const trimmed = line.trim();
257
+ if (!trimmed) {
258
+ return 0;
259
+ }
260
+ let payload;
261
+ try {
262
+ const parsed = JSON.parse(trimmed);
263
+ const record = asRecord(parsed);
264
+ if (!record) {
265
+ throw new Error("invalid_claude_stream_json");
266
+ }
267
+ payload = record;
268
+ } catch (error) {
269
+ throw new Error(`invalid_claude_stream_json:${error instanceof Error ? error.message : String(error)}`);
270
+ }
271
+ if (payload["type"] === "assistant") {
272
+ this.recordToolUse(payload);
273
+ return 0;
274
+ }
275
+ if (payload["type"] === "user") {
276
+ return this.recordToolResults(payload);
277
+ }
278
+ return 0;
279
+ }
280
+ recordToolUse(payload) {
281
+ const message = asRecord(payload["message"]);
282
+ const content = Array.isArray(message?.["content"]) ? message["content"] : [];
283
+ for (const value of content) {
284
+ const item = asRecord(value);
285
+ const id = typeof item?.["id"] === "string" ? item["id"].trim() : "";
286
+ const name = typeof item?.["name"] === "string" ? item["name"].trim() : "";
287
+ if (item?.["type"] !== "tool_use" || !id || name !== PLAYWRIGHT_MCP_SCREENSHOT_TOOL) {
288
+ continue;
289
+ }
290
+ this.pendingScreenshotToolUseIds.add(id);
291
+ }
292
+ }
293
+ recordToolResults(payload) {
294
+ const message = asRecord(payload["message"]);
295
+ const content = Array.isArray(message?.["content"]) ? message["content"] : [];
296
+ let emitted = 0;
297
+ for (const value of content) {
298
+ const toolResult = asRecord(value);
299
+ if (toolResult?.["type"] !== "tool_result") {
300
+ continue;
301
+ }
302
+ const toolUseId = typeof toolResult["tool_use_id"] === "string" ? toolResult["tool_use_id"].trim() : "";
303
+ if (!this.pendingScreenshotToolUseIds.has(toolUseId) || this.handledToolUseIds.has(toolUseId)) {
304
+ continue;
305
+ }
306
+ this.handledToolUseIds.add(toolUseId);
307
+ this.pendingScreenshotToolUseIds.delete(toolUseId);
308
+ const resultContent = Array.isArray(toolResult["content"]) ? toolResult["content"] : [];
309
+ const images = resultContent.map(asRecord).filter((item) => item?.["type"] === "image");
310
+ if (images.length === 0) {
311
+ continue;
312
+ }
313
+ if (images.length > 1) {
314
+ throw new Error(`playwright_screenshot_multiple_images:${toolUseId}`);
315
+ }
316
+ const source = asRecord(images[0]?.["source"]);
317
+ const mediaType = source?.["media_type"];
318
+ const dataBase64 = typeof source?.["data"] === "string" ? source["data"] : "";
319
+ if (mediaType !== "image/jpeg" && mediaType !== "image/png" || !dataBase64) {
320
+ throw new Error(`invalid_playwright_screenshot_payload:${toolUseId}`);
321
+ }
322
+ this.sequence += 1;
323
+ this.onScreenshot({
324
+ screenshotId: `pw-${this.sequence}`,
325
+ tabId: PLAYWRIGHT_MCP_CONTROLLED_TAB_ID,
326
+ toolUseId,
327
+ mediaType,
328
+ dataBase64,
329
+ width: null,
330
+ height: null
331
+ });
332
+ emitted += 1;
333
+ }
334
+ return emitted;
335
+ }
336
+ }
211
337
  // Annotate the CommonJS export names for ESM import in node:
212
338
  0 && (module.exports = {
213
339
  ClaudeChromeScreenshotStreamParser,
214
- INSTRUMENT_EVIDENCE_NAMES
340
+ INSTRUMENT_EVIDENCE_NAMES,
341
+ PLAYWRIGHT_MCP_CONTROLLED_TAB_ID,
342
+ PLAYWRIGHT_MCP_SCREENSHOT_TOOL,
343
+ PlaywrightMcpScreenshotStreamParser,
344
+ isInstrumentScreenshotId
215
345
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playdrop/playdrop-cli",
3
- "version": "0.12.13",
3
+ "version": "0.12.15",
4
4
  "description": "Official Playdrop CLI for publishing browser games, creator apps, and AI-generated game assets on playdrop.ai",
5
5
  "homepage": "https://www.playdrop.ai",
6
6
  "repository": {