@playdrop/playdrop-cli 0.12.1 → 0.12.2

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,5 +1,5 @@
1
1
  {
2
- "version": "0.12.1",
2
+ "version": "0.12.2",
3
3
  "build": 1,
4
4
  "runtimeSdkVersion": "0.10.0",
5
5
  "clients": {
@@ -33,26 +33,6 @@ export type ReviewRatingCardOptions = {
33
33
  punchline?: string;
34
34
  title?: string;
35
35
  };
36
- export type ReviewListWindowsOptions = {
37
- pid: string;
38
- };
39
- export type ReviewScreenshotOptions = {
40
- metadata?: string;
41
- out: string;
42
- pid: string;
43
- windowId?: string;
44
- };
45
- export declare function buildReviewListWindowsRecorderArgs(options: ReviewListWindowsOptions): string[];
46
- export declare function buildReviewScreenshotRecorderArgs(options: ReviewScreenshotOptions): {
47
- args: string[];
48
- metadataPath: string;
49
- outputPath: string;
50
- };
51
- export declare function listReviewCaptureWindows(options: ReviewListWindowsOptions): Promise<void>;
52
- export declare function captureReviewScreenshot(options: ReviewScreenshotOptions): Promise<{
53
- metadataPath: string;
54
- outputPath: string;
55
- }>;
56
36
  export declare function validateGameReviewResult(input: ValidateGameReviewResultInput): Promise<ReviewValidationResult>;
57
37
  export declare function validateReviewResultCommand(options: ReviewValidateResultOptions): Promise<void>;
58
38
  export declare function composeReviewEvidence(options: ReviewComposeEvidenceOptions): Promise<{
@@ -4,20 +4,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.REQUIRED_REVIEW_EVIDENCE_FILES = exports.REVIEW_CRITERIA = void 0;
7
- exports.buildReviewListWindowsRecorderArgs = buildReviewListWindowsRecorderArgs;
8
- exports.buildReviewScreenshotRecorderArgs = buildReviewScreenshotRecorderArgs;
9
- exports.listReviewCaptureWindows = listReviewCaptureWindows;
10
- exports.captureReviewScreenshot = captureReviewScreenshot;
11
7
  exports.validateGameReviewResult = validateGameReviewResult;
12
8
  exports.validateReviewResultCommand = validateReviewResultCommand;
13
9
  exports.composeReviewEvidence = composeReviewEvidence;
14
10
  exports.createReviewRatingCard = createReviewRatingCard;
15
11
  const node_path_1 = __importDefault(require("node:path"));
16
- const node_child_process_1 = require("node:child_process");
17
12
  const node_fs_1 = require("node:fs");
18
13
  const sharp_1 = __importDefault(require("sharp"));
19
14
  const output_1 = require("../output");
20
- const captureListing_1 = require("./captureListing");
21
15
  exports.REVIEW_CRITERIA = [
22
16
  'Gameplay / Core Loop',
23
17
  'Depth / Replayability',
@@ -45,6 +39,7 @@ const STATE_OUTCOME = {
45
39
  PASSED: 'Passed',
46
40
  };
47
41
  exports.REQUIRED_REVIEW_EVIDENCE_FILES = [
42
+ 'first-frame.png',
48
43
  'core.png',
49
44
  'win.png',
50
45
  'loss.png',
@@ -52,7 +47,6 @@ exports.REQUIRED_REVIEW_EVIDENCE_FILES = [
52
47
  'rating-card.png',
53
48
  ];
54
49
  const REVIEW_EVIDENCE_MAX_BYTES = 10 * 1024 * 1024;
55
- const REVIEW_RECORDER_TIMEOUT_MS = 15000;
56
50
  function resolveFilePath(filePath, code) {
57
51
  const normalized = typeof filePath === 'string' ? filePath.trim() : '';
58
52
  if (!normalized) {
@@ -174,111 +168,6 @@ async function assertPngFile(filePath) {
174
168
  }
175
169
  }
176
170
  }
177
- async function assertNonBlankPngFile(filePath) {
178
- await assertPngFile(filePath);
179
- const image = (0, sharp_1.default)(filePath);
180
- const metadata = await image.metadata();
181
- if (!metadata.width || !metadata.height) {
182
- throw new Error(`review_screenshot_invalid_image:${filePath}`);
183
- }
184
- const stats = await image.stats();
185
- const colorChannels = stats.channels.slice(0, 3);
186
- const brightest = Math.max(...colorChannels.map((channel) => channel.max));
187
- if (brightest <= 5) {
188
- throw new Error(`review_screenshot_blank:${filePath}`);
189
- }
190
- }
191
- function parsePositiveInteger(value, code) {
192
- const normalized = typeof value === 'string' ? value.trim() : '';
193
- const parsed = Number.parseInt(normalized, 10);
194
- if (!Number.isSafeInteger(parsed) || parsed <= 0 || String(parsed) !== normalized) {
195
- throw new Error(code);
196
- }
197
- return parsed;
198
- }
199
- function buildReviewListWindowsRecorderArgs(options) {
200
- const pid = parsePositiveInteger(options.pid, 'invalid_review_window_pid');
201
- return ['--list-windows', '--pid', String(pid)];
202
- }
203
- function buildReviewScreenshotRecorderArgs(options) {
204
- const pid = parsePositiveInteger(options.pid, 'invalid_review_window_pid');
205
- const outputPath = resolveFilePath(options.out, 'missing_out');
206
- const metadataPath = options.metadata
207
- ? resolveFilePath(options.metadata, 'invalid_review_screenshot_metadata')
208
- : outputPath.replace(/\.png$/i, '') + '.metadata.json';
209
- const args = [
210
- '--pid',
211
- String(pid),
212
- '--screenshot',
213
- outputPath,
214
- '--metadata',
215
- metadataPath,
216
- ];
217
- if (options.windowId !== undefined && options.windowId !== '') {
218
- const windowId = parsePositiveInteger(options.windowId, 'invalid_review_window_id');
219
- args.push('--window-id', String(windowId));
220
- }
221
- return { args, metadataPath, outputPath };
222
- }
223
- function runRecorder(args) {
224
- return new Promise((resolve, reject) => {
225
- const recorderPath = (0, captureListing_1.resolveListingRecorderPath)();
226
- const child = (0, node_child_process_1.spawn)(recorderPath, args, {
227
- stdio: ['ignore', 'pipe', 'pipe'],
228
- });
229
- let stdout = '';
230
- let stderr = '';
231
- let settled = false;
232
- const timer = setTimeout(() => {
233
- if (settled) {
234
- return;
235
- }
236
- settled = true;
237
- child.kill('SIGKILL');
238
- reject(new Error(`review_recorder_timeout:${REVIEW_RECORDER_TIMEOUT_MS}`));
239
- }, REVIEW_RECORDER_TIMEOUT_MS);
240
- child.stdout.on('data', (chunk) => {
241
- stdout += chunk.toString();
242
- });
243
- child.stderr.on('data', (chunk) => {
244
- stderr += chunk.toString();
245
- });
246
- child.on('error', (error) => {
247
- if (settled) {
248
- return;
249
- }
250
- settled = true;
251
- clearTimeout(timer);
252
- reject(new Error(`review_recorder_unavailable:${recorderPath}:${error.message}`));
253
- });
254
- child.on('close', (code) => {
255
- if (settled) {
256
- return;
257
- }
258
- settled = true;
259
- clearTimeout(timer);
260
- if (code === 0) {
261
- resolve(stdout);
262
- return;
263
- }
264
- const detail = (stderr || stdout || `exit ${code}`).trim();
265
- reject(new Error(`review_recorder_failed:${detail}`));
266
- });
267
- });
268
- }
269
- async function listReviewCaptureWindows(options) {
270
- const output = await runRecorder(buildReviewListWindowsRecorderArgs(options));
271
- process.stdout.write(output);
272
- }
273
- async function captureReviewScreenshot(options) {
274
- const { args, metadataPath, outputPath } = buildReviewScreenshotRecorderArgs(options);
275
- await node_fs_1.promises.mkdir(node_path_1.default.dirname(outputPath), { recursive: true });
276
- await node_fs_1.promises.mkdir(node_path_1.default.dirname(metadataPath), { recursive: true });
277
- await runRecorder(args);
278
- await assertNonBlankPngFile(outputPath);
279
- (0, output_1.printSuccess)(`Review screenshot written to ${outputPath}`);
280
- return { metadataPath, outputPath };
281
- }
282
171
  async function validateGameReviewResult(input) {
283
172
  const normalizedState = String(input.reviewState || '').trim().toUpperCase();
284
173
  if (!TERMINAL_REVIEW_STATES.has(normalizedState)) {
@@ -0,0 +1,17 @@
1
+ import { type InstrumentEvidenceCapture, type InstrumentEvidenceManifest } from '@playdrop/types';
2
+ export declare const INSTRUMENT_EVIDENCE_MANIFEST_FILE = "instrument-evidence.json";
3
+ export declare function createWorkerInstrumentScreenshotCollector(workspaceDir: string): {
4
+ push: (chunk: string) => number;
5
+ finish: () => number;
6
+ };
7
+ export declare function saveInstrumentEvidenceCapture(input: {
8
+ workspaceDir: string;
9
+ evidenceDir: string;
10
+ groupId: string;
11
+ name: string;
12
+ screenshotId: string;
13
+ }): Promise<InstrumentEvidenceCapture>;
14
+ export declare function readAndValidateInstrumentEvidenceManifest(input: {
15
+ workspaceDir: string;
16
+ evidenceDir: string;
17
+ }): InstrumentEvidenceManifest;
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.INSTRUMENT_EVIDENCE_MANIFEST_FILE = void 0;
7
+ exports.createWorkerInstrumentScreenshotCollector = createWorkerInstrumentScreenshotCollector;
8
+ exports.saveInstrumentEvidenceCapture = saveInstrumentEvidenceCapture;
9
+ exports.readAndValidateInstrumentEvidenceManifest = readAndValidateInstrumentEvidenceManifest;
10
+ const types_1 = require("@playdrop/types");
11
+ const node_crypto_1 = require("node:crypto");
12
+ const node_fs_1 = require("node:fs");
13
+ const node_path_1 = __importDefault(require("node:path"));
14
+ const sharp_1 = __importDefault(require("sharp"));
15
+ const SCREENSHOT_STORE_DIR = '.playdrop-instrument-screenshots';
16
+ const SCREENSHOT_INDEX_FILE = 'index.json';
17
+ exports.INSTRUMENT_EVIDENCE_MANIFEST_FILE = 'instrument-evidence.json';
18
+ const GROUP_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/;
19
+ const SHA256_PATTERN = /^[a-f0-9]{64}$/;
20
+ function sha256(buffer) {
21
+ return (0, node_crypto_1.createHash)('sha256').update(buffer).digest('hex');
22
+ }
23
+ function isPathInside(parent, child) {
24
+ const relative = node_path_1.default.relative(parent, child);
25
+ return relative === '' || (!!relative && !relative.startsWith('..') && !node_path_1.default.isAbsolute(relative));
26
+ }
27
+ function writeJsonAtomic(filePath, value) {
28
+ (0, node_fs_1.mkdirSync)(node_path_1.default.dirname(filePath), { recursive: true });
29
+ const tempPath = `${filePath}.${process.pid}.tmp`;
30
+ (0, node_fs_1.writeFileSync)(tempPath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
31
+ (0, node_fs_1.renameSync)(tempPath, filePath);
32
+ }
33
+ function readJsonRecord(filePath, code) {
34
+ let parsed;
35
+ try {
36
+ parsed = JSON.parse((0, node_fs_1.readFileSync)(filePath, 'utf8'));
37
+ }
38
+ catch (error) {
39
+ throw new Error(`${code}:${error instanceof Error ? error.message : String(error)}`);
40
+ }
41
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
42
+ throw new Error(code);
43
+ }
44
+ return parsed;
45
+ }
46
+ function screenshotStorePath(workspaceDir) {
47
+ return node_path_1.default.join(workspaceDir, SCREENSHOT_STORE_DIR);
48
+ }
49
+ function screenshotIndexPath(workspaceDir) {
50
+ return node_path_1.default.join(screenshotStorePath(workspaceDir), SCREENSHOT_INDEX_FILE);
51
+ }
52
+ function readScreenshotIndex(workspaceDir) {
53
+ const filePath = screenshotIndexPath(workspaceDir);
54
+ if (!(0, node_fs_1.existsSync)(filePath)) {
55
+ return { version: 1, screenshots: {} };
56
+ }
57
+ const parsed = readJsonRecord(filePath, 'invalid_instrument_screenshot_index');
58
+ const screenshots = parsed['screenshots'];
59
+ if (parsed['version'] !== 1 || !screenshots || typeof screenshots !== 'object' || Array.isArray(screenshots)) {
60
+ throw new Error('invalid_instrument_screenshot_index');
61
+ }
62
+ return parsed;
63
+ }
64
+ function extensionForMediaType(mediaType) {
65
+ return mediaType === 'image/jpeg' ? 'jpg' : 'png';
66
+ }
67
+ function createWorkerInstrumentScreenshotCollector(workspaceDir) {
68
+ const resolvedWorkspace = node_path_1.default.resolve(workspaceDir);
69
+ const parser = new types_1.ClaudeChromeScreenshotStreamParser((screenshot) => {
70
+ const buffer = Buffer.from(screenshot.dataBase64, 'base64');
71
+ if (buffer.length <= 0) {
72
+ throw new Error(`invalid_claude_screenshot_payload:${screenshot.screenshotId}`);
73
+ }
74
+ const storeDir = screenshotStorePath(resolvedWorkspace);
75
+ (0, node_fs_1.mkdirSync)(storeDir, { recursive: true });
76
+ const fileName = `${screenshot.screenshotId}.${extensionForMediaType(screenshot.mediaType)}`;
77
+ const filePath = node_path_1.default.join(storeDir, fileName);
78
+ (0, node_fs_1.writeFileSync)(filePath, buffer);
79
+ const index = readScreenshotIndex(resolvedWorkspace);
80
+ index.screenshots[screenshot.screenshotId] = {
81
+ screenshotId: screenshot.screenshotId,
82
+ tabId: screenshot.tabId,
83
+ toolUseId: screenshot.toolUseId,
84
+ mediaType: screenshot.mediaType,
85
+ fileName,
86
+ sha256: sha256(buffer),
87
+ capturedAt: new Date().toISOString(),
88
+ width: screenshot.width,
89
+ height: screenshot.height,
90
+ };
91
+ writeJsonAtomic(screenshotIndexPath(resolvedWorkspace), index);
92
+ });
93
+ return {
94
+ push: (chunk) => parser.push(chunk),
95
+ finish: () => parser.finish(),
96
+ };
97
+ }
98
+ function normalizeEvidenceName(value) {
99
+ const normalized = value.trim();
100
+ if (!types_1.INSTRUMENT_EVIDENCE_NAMES.includes(normalized)) {
101
+ throw new Error(`invalid_instrument_evidence_name:${value}`);
102
+ }
103
+ return normalized;
104
+ }
105
+ function normalizeGroupId(value) {
106
+ const normalized = value.trim().toLowerCase();
107
+ if (!GROUP_ID_PATTERN.test(normalized)) {
108
+ throw new Error(`invalid_instrument_evidence_group:${value}`);
109
+ }
110
+ return normalized;
111
+ }
112
+ function resolveEvidenceDir(workspaceDir, evidenceDir) {
113
+ const resolvedWorkspace = node_path_1.default.resolve(workspaceDir);
114
+ const resolvedEvidenceDir = node_path_1.default.resolve(evidenceDir);
115
+ if (!isPathInside(resolvedWorkspace, resolvedEvidenceDir)) {
116
+ throw new Error('instrument_evidence_dir_outside_workspace');
117
+ }
118
+ return resolvedEvidenceDir;
119
+ }
120
+ function readManifestIfPresent(evidenceDir) {
121
+ const filePath = node_path_1.default.join(evidenceDir, exports.INSTRUMENT_EVIDENCE_MANIFEST_FILE);
122
+ if (!(0, node_fs_1.existsSync)(filePath)) {
123
+ return null;
124
+ }
125
+ return readJsonRecord(filePath, 'invalid_instrument_evidence_manifest');
126
+ }
127
+ async function saveInstrumentEvidenceCapture(input) {
128
+ const evidenceName = normalizeEvidenceName(input.name);
129
+ const groupId = normalizeGroupId(input.groupId);
130
+ const screenshotId = input.screenshotId.trim();
131
+ const index = readScreenshotIndex(input.workspaceDir);
132
+ const screenshot = index.screenshots[screenshotId];
133
+ if (!screenshot) {
134
+ throw new Error(`instrument_screenshot_not_found:${screenshotId}`);
135
+ }
136
+ const evidenceDir = resolveEvidenceDir(input.workspaceDir, input.evidenceDir);
137
+ (0, node_fs_1.mkdirSync)(evidenceDir, { recursive: true });
138
+ const existing = readManifestIfPresent(evidenceDir);
139
+ if (existing && existing.groupId !== groupId) {
140
+ throw new Error(`instrument_evidence_group_mismatch:${existing.groupId}:${groupId}`);
141
+ }
142
+ if (existing && existing.controlledTabId !== screenshot.tabId) {
143
+ throw new Error(`instrument_evidence_tab_mismatch:${existing.controlledTabId}:${screenshot.tabId}`);
144
+ }
145
+ const fileName = `${evidenceName}.png`;
146
+ const sourcePath = node_path_1.default.join(screenshotStorePath(input.workspaceDir), screenshot.fileName);
147
+ const outputPath = node_path_1.default.join(evidenceDir, fileName);
148
+ const sourceBuffer = (0, node_fs_1.readFileSync)(sourcePath);
149
+ const outputBuffer = await (0, sharp_1.default)(sourceBuffer).png().toBuffer();
150
+ (0, node_fs_1.writeFileSync)(outputPath, outputBuffer);
151
+ const outputHash = sha256(outputBuffer);
152
+ const capture = {
153
+ name: evidenceName,
154
+ screenshotId,
155
+ tabId: screenshot.tabId,
156
+ toolUseId: screenshot.toolUseId,
157
+ mediaType: screenshot.mediaType,
158
+ fileName,
159
+ sourceSha256: screenshot.sha256,
160
+ sha256: outputHash,
161
+ capturedAt: screenshot.capturedAt,
162
+ };
163
+ const manifest = existing ?? {
164
+ version: 1,
165
+ groupId,
166
+ controlledTabId: screenshot.tabId,
167
+ captures: {},
168
+ };
169
+ manifest.captures[evidenceName] = capture;
170
+ writeJsonAtomic(node_path_1.default.join(evidenceDir, exports.INSTRUMENT_EVIDENCE_MANIFEST_FILE), manifest);
171
+ return capture;
172
+ }
173
+ function normalizeCapture(value, name) {
174
+ const capture = value && typeof value === 'object' && !Array.isArray(value)
175
+ ? value
176
+ : null;
177
+ const screenshotId = typeof capture?.['screenshotId'] === 'string' ? capture['screenshotId'].trim() : '';
178
+ const tabId = Number(capture?.['tabId']);
179
+ const toolUseId = typeof capture?.['toolUseId'] === 'string' ? capture['toolUseId'].trim() : '';
180
+ const mediaType = capture?.['mediaType'];
181
+ const fileName = typeof capture?.['fileName'] === 'string' ? capture['fileName'].trim() : '';
182
+ const hash = typeof capture?.['sha256'] === 'string' ? capture['sha256'].trim().toLowerCase() : '';
183
+ const sourceHash = typeof capture?.['sourceSha256'] === 'string' ? capture['sourceSha256'].trim().toLowerCase() : '';
184
+ const capturedAt = typeof capture?.['capturedAt'] === 'string' ? capture['capturedAt'].trim() : '';
185
+ if (capture?.['name'] !== name
186
+ || !screenshotId
187
+ || !Number.isInteger(tabId)
188
+ || tabId <= 0
189
+ || !toolUseId
190
+ || (mediaType !== 'image/jpeg' && mediaType !== 'image/png')
191
+ || node_path_1.default.basename(fileName) !== fileName
192
+ || !fileName
193
+ || !SHA256_PATTERN.test(sourceHash)
194
+ || !SHA256_PATTERN.test(hash)
195
+ || !capturedAt) {
196
+ throw new Error(`invalid_instrument_evidence_capture:${name}`);
197
+ }
198
+ return {
199
+ name,
200
+ screenshotId,
201
+ tabId,
202
+ toolUseId,
203
+ mediaType,
204
+ fileName,
205
+ sourceSha256: sourceHash,
206
+ sha256: hash,
207
+ capturedAt,
208
+ };
209
+ }
210
+ function readAndValidateInstrumentEvidenceManifest(input) {
211
+ const evidenceDir = resolveEvidenceDir(input.workspaceDir, input.evidenceDir);
212
+ const parsed = readManifestIfPresent(evidenceDir);
213
+ if (!parsed || parsed.version !== 1) {
214
+ throw new Error('missing_instrument_evidence_manifest');
215
+ }
216
+ const groupId = normalizeGroupId(parsed.groupId);
217
+ const controlledTabId = Number(parsed.controlledTabId);
218
+ if (!Number.isInteger(controlledTabId) || controlledTabId <= 0) {
219
+ throw new Error('invalid_instrument_evidence_tab');
220
+ }
221
+ const captures = {};
222
+ const screenshotIds = new Set();
223
+ for (const name of types_1.INSTRUMENT_EVIDENCE_NAMES) {
224
+ const capture = normalizeCapture(parsed.captures?.[name], name);
225
+ if (capture.tabId !== controlledTabId) {
226
+ throw new Error(`instrument_evidence_tab_mismatch:${controlledTabId}:${capture.tabId}`);
227
+ }
228
+ const filePath = node_path_1.default.join(evidenceDir, capture.fileName);
229
+ if (!(0, node_fs_1.existsSync)(filePath) || sha256((0, node_fs_1.readFileSync)(filePath)) !== capture.sha256) {
230
+ throw new Error(`instrument_evidence_file_hash_mismatch:${name}`);
231
+ }
232
+ if (screenshotIds.has(capture.screenshotId)) {
233
+ throw new Error(`duplicate_instrument_evidence_screenshot:${capture.screenshotId}`);
234
+ }
235
+ screenshotIds.add(capture.screenshotId);
236
+ captures[name] = capture;
237
+ }
238
+ return { version: 1, groupId, controlledTabId, captures };
239
+ }
@@ -50,6 +50,7 @@ export declare function runLoggedProcess(input: {
50
50
  maxOutputChars: number;
51
51
  transcriptFlushIntervalMs?: number;
52
52
  onTranscriptChunks?: (chunks: LoggedProcessTranscriptChunk[]) => Promise<void> | void;
53
+ onStdoutText?: (text: string) => boolean | void;
53
54
  onChild?: (controls: {
54
55
  terminate: () => void;
55
56
  }) => void;
@@ -377,6 +377,17 @@ async function runLoggedProcess(input) {
377
377
  if (input.onTranscriptChunks) {
378
378
  pendingTranscriptChunks.push({ stream, content: text });
379
379
  }
380
+ if (stream === 'stdout' && input.onStdoutText) {
381
+ try {
382
+ const flushNow = input.onStdoutText(text) === true;
383
+ if (flushNow && input.onTranscriptChunks) {
384
+ queueTranscriptFlush().catch(handleTranscriptFlushError);
385
+ }
386
+ }
387
+ catch (error) {
388
+ handleTranscriptFlushError(error);
389
+ }
390
+ }
380
391
  combined = tailText(combined + text, input.maxOutputChars);
381
392
  };
382
393
  const timeout = setTimeout(() => {
@@ -91,6 +91,16 @@ type TaskFailOptions = {
91
91
  env?: string;
92
92
  message?: string;
93
93
  };
94
+ type TaskEvidenceOptions = {
95
+ evidenceDir?: string;
96
+ group?: string;
97
+ name?: string;
98
+ screenshotId?: string;
99
+ };
100
+ type TaskInstrumentErrorOptions = {
101
+ env?: string;
102
+ reason?: string;
103
+ };
94
104
  type TaskSubmitReviewOptions = {
95
105
  env?: string;
96
106
  state?: string;
@@ -101,6 +111,7 @@ type TaskSubmitReviewOptions = {
101
111
  };
102
112
  type TaskSubmitEvalOptions = {
103
113
  env?: string;
114
+ evidenceDir?: string[];
104
115
  resultFile?: string;
105
116
  };
106
117
  export type WorkerHealthAlertInput = {
@@ -251,17 +262,31 @@ export declare function assertLaunchAgentPlistCompatible(input: {
251
262
  }): void;
252
263
  export declare function showWorkerStatus(options?: WorkerStatusOptions): Promise<WorkerStatusPayload>;
253
264
  export declare function setupWorker(options?: WorkerSetupOptions): Promise<void>;
265
+ export declare function instrumentTaskChromeTitleMatchers(assignment: WorkerAgentTaskAssignmentResponse): string[];
266
+ type InstrumentBrowserHygieneRunner = (command: string, args: string[]) => Promise<{
267
+ stdout: string;
268
+ stderr: string;
269
+ }>;
270
+ export declare function enforceInstrumentBrowserHygiene(assignment: WorkerAgentTaskAssignmentResponse, options?: {
271
+ platform?: NodeJS.Platform;
272
+ runner?: InstrumentBrowserHygieneRunner;
273
+ }): Promise<{
274
+ remainingTaskTabs: number;
275
+ blankWindows: number;
276
+ }>;
254
277
  export declare function startWorker(options?: WorkerStartOptions): Promise<void>;
255
278
  export declare function reportTask(options: TaskReportOptions): Promise<void>;
256
279
  export declare function reportCatalogueTask(options: TaskCatalogueReportOptions): Promise<void>;
257
280
  export declare function uploadTask(options?: TaskUploadOptions): Promise<void>;
258
281
  export declare function claimSlugTask(options: TaskClaimSlugOptions): Promise<void>;
259
282
  export declare function completeTask(options: TaskCompleteOptions): Promise<void>;
260
- export declare function readReviewEvidenceFiles(evidenceDir: string | undefined): Array<{
283
+ export declare function readReviewEvidenceFiles(evidenceDir: string | undefined, workspaceDir?: string): Array<{
261
284
  name: string;
262
285
  contentType: string;
263
286
  contentBase64: string;
264
287
  }>;
265
288
  export declare function submitReviewTask(options: TaskSubmitReviewOptions): Promise<void>;
266
289
  export declare function submitEvalTask(options: TaskSubmitEvalOptions): Promise<void>;
290
+ export declare function saveTaskEvidence(options: TaskEvidenceOptions): Promise<void>;
291
+ export declare function instrumentErrorTask(options: TaskInstrumentErrorOptions): Promise<void>;
267
292
  export declare function failTask(options: TaskFailOptions): Promise<void>;