@basemachina/agentic-browser-cli 0.3.0

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.
@@ -0,0 +1,3186 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ computeCacheRate,
4
+ computeTotalRealInput,
5
+ getModelId,
6
+ getModelProvider
7
+ } from "./chunk-XAEHXRUC.js";
8
+ import {
9
+ cancel,
10
+ getLocale,
11
+ log,
12
+ t
13
+ } from "./chunk-VNQYQSMI.js";
14
+
15
+ // src/cli/debug-logger.ts
16
+ import { appendFile, writeFile } from "fs/promises";
17
+ import * as p from "@clack/prompts";
18
+ var FileDebugLogger = class {
19
+ constructor(filePath) {
20
+ this.filePath = filePath;
21
+ this.buffer = [];
22
+ this.writing = false;
23
+ this.pending = Promise.resolve();
24
+ this.initialized = false;
25
+ }
26
+ log(entry) {
27
+ const line = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry });
28
+ this.buffer.push(line);
29
+ this.scheduleWrite();
30
+ }
31
+ async flush() {
32
+ await this.pending;
33
+ if (this.buffer.length > 0) {
34
+ await this.writeBuffer();
35
+ }
36
+ }
37
+ scheduleWrite() {
38
+ if (this.writing) return;
39
+ this.pending = this.writeBuffer();
40
+ }
41
+ async writeBuffer() {
42
+ if (this.buffer.length === 0) return;
43
+ this.writing = true;
44
+ const lines = this.buffer.splice(0, this.buffer.length);
45
+ const content = lines.join("\n") + "\n";
46
+ try {
47
+ if (!this.initialized) {
48
+ await writeFile(this.filePath, content, "utf-8");
49
+ this.initialized = true;
50
+ } else {
51
+ await appendFile(this.filePath, content, "utf-8");
52
+ }
53
+ } catch {
54
+ }
55
+ this.writing = false;
56
+ if (this.buffer.length > 0) {
57
+ this.pending = this.writeBuffer();
58
+ }
59
+ }
60
+ };
61
+ var ConsoleDebugLogger = class {
62
+ log(entry) {
63
+ const stepPrefix = entry.step !== void 0 ? `step#${entry.step}` : "";
64
+ const prefix = `[debug] [${entry.event}]${stepPrefix ? ` ${stepPrefix}` : ""}`;
65
+ switch (entry.event) {
66
+ case "extract":
67
+ case "extract_result":
68
+ this.logExtract(prefix, entry.data);
69
+ break;
70
+ case "memory_append":
71
+ this.logMemoryAppend(prefix, entry.data);
72
+ break;
73
+ case "memory_aggregate":
74
+ this.logMemoryAggregate(prefix, entry.data);
75
+ break;
76
+ case "export":
77
+ this.logExport(prefix, entry.data);
78
+ break;
79
+ case "action":
80
+ this.logAction(prefix, entry.data);
81
+ break;
82
+ default:
83
+ this.logGeneric(prefix, entry.data);
84
+ break;
85
+ }
86
+ }
87
+ async flush() {
88
+ }
89
+ logExtract(prefix, data) {
90
+ if (data.script) {
91
+ p.log.info(`${prefix} script: "${truncate(String(data.script), 120)}"`);
92
+ }
93
+ if (data.extractPrompt) {
94
+ p.log.info(`${prefix} extractPrompt: "${truncate(String(data.extractPrompt), 120)}"`);
95
+ }
96
+ if (data.resultType !== void 0) {
97
+ const typeInfo = data.isArray ? `${data.resultType} (array), length: ${data.arrayLength}` : String(data.resultType);
98
+ p.log.info(`${prefix} result typeof: "${typeInfo}"`);
99
+ }
100
+ if (data.resultPreview !== void 0) {
101
+ p.log.info(`${prefix} result preview: ${truncate(String(data.resultPreview), 200)}`);
102
+ }
103
+ if (data.storedLength !== void 0) {
104
+ const note = data.stringifyApplied ? " \u2190 JSON.stringify applied" : "";
105
+ p.log.info(`${prefix} stored as string (length: ${data.storedLength})${note}`);
106
+ }
107
+ }
108
+ logMemoryAppend(prefix, data) {
109
+ if (data.collection) {
110
+ p.log.info(`${prefix} collection: "${data.collection}"`);
111
+ }
112
+ if (data.rawValuePreview !== void 0) {
113
+ p.log.info(`${prefix} rawValue preview (200 chars): ${truncate(String(data.rawValuePreview), 200)}`);
114
+ }
115
+ if (data.doubleStringifyDetected) {
116
+ p.log.warn(`${prefix} double-stringify detected! Unquoting before parse.`);
117
+ }
118
+ if (data.parseResult !== void 0) {
119
+ const result = data.parseResult;
120
+ if (result === "array") {
121
+ p.log.info(
122
+ `${prefix} JSON.parse OK \u2192 Array(${data.arrayLength}), element[0] keys: ${data.firstElementKeys ?? "N/A"}`
123
+ );
124
+ } else if (result === "object") {
125
+ p.log.info(`${prefix} JSON.parse OK \u2192 Object, keys: ${data.objectKeys ?? "N/A"}`);
126
+ } else if (result === "fallback") {
127
+ p.log.warn(`${prefix} JSON.parse FAILED \u2192 storing as {value: "..."}`);
128
+ }
129
+ }
130
+ if (data.count !== void 0 && data.parseResult === void 0) {
131
+ p.log.info(`${prefix} stored ${data.count} items`);
132
+ }
133
+ }
134
+ logMemoryAggregate(prefix, data) {
135
+ p.log.info(
136
+ `${prefix} ${data.operation}(${data.collection}.${data.field}) = "${data.result}" \u2192 {{${data.outputVariable}}}`
137
+ );
138
+ }
139
+ logExport(prefix, data) {
140
+ p.log.info(`${prefix} collection: "${data.collection}", items: ${data.itemCount}, format: ${data.format}`);
141
+ if (data.sampleKeys) {
142
+ p.log.info(`${prefix} sample keys: ${data.sampleKeys}`);
143
+ }
144
+ if (data.path) {
145
+ p.log.info(`${prefix} \u2192 ${data.path}`);
146
+ }
147
+ }
148
+ logAction(prefix, data) {
149
+ p.log.info(
150
+ `${prefix} type=${data.type} ref=${data.ref ?? "N/A"} value="${truncate(String(data.value ?? ""), 80)}" status=${data.status}`
151
+ );
152
+ }
153
+ logGeneric(prefix, data) {
154
+ const preview = JSON.stringify(data);
155
+ p.log.info(`${prefix} ${truncate(preview, 200)}`);
156
+ }
157
+ };
158
+ var CompositeDebugLogger = class {
159
+ constructor(loggers) {
160
+ this.loggers = loggers;
161
+ }
162
+ log(entry) {
163
+ for (const logger of this.loggers) {
164
+ logger.log(entry);
165
+ }
166
+ }
167
+ async flush() {
168
+ await Promise.all(this.loggers.map((l) => l.flush()));
169
+ }
170
+ };
171
+ var noopLogger = {
172
+ log() {
173
+ },
174
+ async flush() {
175
+ }
176
+ };
177
+ function truncate(str, maxLen) {
178
+ if (str.length <= maxLen) return str;
179
+ return `${str.slice(0, maxLen)}...`;
180
+ }
181
+ function createDebugLogger(opts) {
182
+ if (!opts) return noopLogger;
183
+ const loggers = [];
184
+ if (opts.filePath) {
185
+ loggers.push(new FileDebugLogger(opts.filePath));
186
+ }
187
+ if (opts.console) {
188
+ loggers.push(new ConsoleDebugLogger());
189
+ }
190
+ if (loggers.length === 0) return noopLogger;
191
+ if (loggers.length === 1) return loggers[0];
192
+ return new CompositeDebugLogger(loggers);
193
+ }
194
+
195
+ // src/context/data-store.ts
196
+ import { writeFile as writeFile2 } from "fs/promises";
197
+ var InMemoryDataStore = class {
198
+ constructor() {
199
+ this.collections = /* @__PURE__ */ new Map();
200
+ }
201
+ append(collection, item) {
202
+ const items = this.collections.get(collection) ?? [];
203
+ items.push(item);
204
+ this.collections.set(collection, items);
205
+ }
206
+ extend(collection, newItems) {
207
+ const items = this.collections.get(collection) ?? [];
208
+ items.push(...newItems);
209
+ this.collections.set(collection, items);
210
+ }
211
+ getAll(collection) {
212
+ return this.collections.get(collection) ?? [];
213
+ }
214
+ count(collection) {
215
+ return (this.collections.get(collection) ?? []).length;
216
+ }
217
+ clear(collection) {
218
+ this.collections.delete(collection);
219
+ }
220
+ listCollections() {
221
+ return Array.from(this.collections.keys());
222
+ }
223
+ toJSON() {
224
+ const result = {};
225
+ for (const [key, items] of this.collections) {
226
+ result[key] = items;
227
+ }
228
+ return result;
229
+ }
230
+ aggregate(query) {
231
+ const items = this.getAll(query.collection);
232
+ if (items.length === 0) return "0";
233
+ const values = items.map((item) => item[query.field]);
234
+ switch (query.operation) {
235
+ case "sum": {
236
+ const total = values.reduce(
237
+ (acc, v) => acc + (Number(v) || 0),
238
+ 0
239
+ );
240
+ return String(total);
241
+ }
242
+ case "count":
243
+ return String(values.length);
244
+ case "concat":
245
+ return values.map(String).join(", ");
246
+ case "min": {
247
+ const nums = values.map(Number).filter((n) => !Number.isNaN(n));
248
+ return nums.length > 0 ? String(Math.min(...nums)) : "0";
249
+ }
250
+ case "max": {
251
+ const nums = values.map(Number).filter((n) => !Number.isNaN(n));
252
+ return nums.length > 0 ? String(Math.max(...nums)) : "0";
253
+ }
254
+ case "avg": {
255
+ const nums = values.map(Number).filter((n) => !Number.isNaN(n));
256
+ if (nums.length === 0) return "0";
257
+ const sum = nums.reduce((a, b) => a + b, 0);
258
+ return String(sum / nums.length);
259
+ }
260
+ case "unique_count": {
261
+ const unique = new Set(values.map(String));
262
+ return String(unique.size);
263
+ }
264
+ }
265
+ }
266
+ /**
267
+ * コレクションごとのメモリ圧迫状況を返す。
268
+ * low: <1000, medium: 1000-5000, high: >5000
269
+ */
270
+ getPressureStatus() {
271
+ const result = {};
272
+ for (const [name, items] of this.collections) {
273
+ const count = items.length;
274
+ const pressure = count >= 5e3 ? "high" : count >= 1e3 ? "medium" : "low";
275
+ result[name] = { count, pressure };
276
+ }
277
+ return result;
278
+ }
279
+ /** コレクションを CSV 文字列として出力 */
280
+ toCSV(collection) {
281
+ const items = this.getAll(collection);
282
+ if (items.length === 0) return "";
283
+ const keys = /* @__PURE__ */ new Set();
284
+ for (const item of items) {
285
+ for (const key of Object.keys(item)) {
286
+ keys.add(key);
287
+ }
288
+ }
289
+ const headers = Array.from(keys);
290
+ const escapeCSV = (val) => {
291
+ const str = val === null || val === void 0 ? "" : String(val);
292
+ if (str.includes(",") || str.includes('"') || str.includes("\n")) {
293
+ return `"${str.replace(/"/g, '""')}"`;
294
+ }
295
+ return str;
296
+ };
297
+ const rows = items.map(
298
+ (item) => headers.map((h) => escapeCSV(item[h])).join(",")
299
+ );
300
+ return [headers.join(","), ...rows].join("\n");
301
+ }
302
+ /** コレクションをファイルに書き出し */
303
+ async writeToFile(collection, path, format) {
304
+ const content = format === "csv" ? this.toCSV(collection) : JSON.stringify(this.getAll(collection), null, 2);
305
+ await writeFile2(path, content, "utf-8");
306
+ }
307
+ };
308
+
309
+ // src/context/download-manager.ts
310
+ import { readFile, writeFile as writeFile3, mkdir, stat } from "fs/promises";
311
+ import { join } from "path";
312
+ import { randomUUID } from "crypto";
313
+ var LocalStorage = class {
314
+ constructor(baseDir) {
315
+ this.baseDir = baseDir;
316
+ }
317
+ async write(filename, content) {
318
+ await mkdir(this.baseDir, { recursive: true });
319
+ const filePath = join(this.baseDir, filename);
320
+ await writeFile3(filePath, content);
321
+ return filePath;
322
+ }
323
+ async read(filename) {
324
+ const filePath = join(this.baseDir, filename);
325
+ return readFile(filePath);
326
+ }
327
+ async exists(filename) {
328
+ try {
329
+ await stat(join(this.baseDir, filename));
330
+ return true;
331
+ } catch {
332
+ return false;
333
+ }
334
+ }
335
+ };
336
+ var DownloadManager = class {
337
+ constructor(outputDir) {
338
+ this.downloads = [];
339
+ this.storage = new LocalStorage(outputDir);
340
+ }
341
+ addDownload(record) {
342
+ const download = {
343
+ ...record,
344
+ id: randomUUID()
345
+ };
346
+ this.downloads.push(download);
347
+ return download;
348
+ }
349
+ getDownloads() {
350
+ return [...this.downloads];
351
+ }
352
+ getDownloadsByStep(stepOrdinal) {
353
+ return this.downloads.filter((d) => d.stepOrdinal === stepOrdinal);
354
+ }
355
+ /**
356
+ * 複数のダウンロード済みCSVファイルを結合する。
357
+ * 1つ目のファイルのヘッダを使用し、2つ目以降はヘッダ行をスキップ。
358
+ */
359
+ async mergeCSVFiles(downloadIds, outputFilename) {
360
+ const targets = downloadIds.map((id) => this.downloads.find((d) => d.id === id)).filter((d) => d !== void 0);
361
+ if (targets.length === 0) {
362
+ throw new Error("No downloads found for the given IDs");
363
+ }
364
+ const chunks = [];
365
+ let headerLine = null;
366
+ for (let i = 0; i < targets.length; i++) {
367
+ const content = await readFile(targets[i].path, "utf-8");
368
+ const lines = content.split("\n").filter((l) => l.trim().length > 0);
369
+ if (lines.length === 0) continue;
370
+ if (i === 0) {
371
+ headerLine = lines[0];
372
+ chunks.push(content.trimEnd());
373
+ } else {
374
+ const dataLines = lines.slice(1);
375
+ if (dataLines.length > 0) {
376
+ chunks.push(dataLines.join("\n"));
377
+ }
378
+ }
379
+ }
380
+ const merged = chunks.join("\n") + "\n";
381
+ const outputPath = await this.storage.write(outputFilename, merged);
382
+ return outputPath;
383
+ }
384
+ /** 全ダウンロードの ID 一覧 */
385
+ getAllDownloadIds() {
386
+ return this.downloads.map((d) => d.id);
387
+ }
388
+ /** ダウンロードのファイルパス一覧 */
389
+ getAllPaths() {
390
+ return this.downloads.map((d) => d.path);
391
+ }
392
+ };
393
+
394
+ // src/cli/format.ts
395
+ function formatDuration(ms) {
396
+ return ms >= 1e3 ? `${(ms / 1e3).toFixed(1)}s` : `${Math.round(ms)}ms`;
397
+ }
398
+
399
+ // src/cli/options-serializer.ts
400
+ function serializeGeneratorOptions(config) {
401
+ const opts = [];
402
+ opts.push({ name: "--url", value: config.url });
403
+ opts.push({ name: "--goal", value: config.goal });
404
+ opts.push({ name: "--output", value: config.output });
405
+ opts.push({ name: "--headless", value: String(config.headless) });
406
+ opts.push({ name: "--max-iterations", value: String(config.maxIterations) });
407
+ opts.push({ name: "--step-delay", value: `${config.stepDelay}ms` });
408
+ opts.push({ name: "--stall-check-interval", value: String(config.stallCheckInterval) });
409
+ opts.push({ name: "--history-window", value: String(config.historyWindow) });
410
+ opts.push({ name: "--max-failures", value: String(config.maxConsecutiveFailures) });
411
+ opts.push({ name: "--snapshot-filter", value: String(config.snapshotFilter) });
412
+ if (config.screenshotDir) {
413
+ opts.push({ name: "--screenshots", value: config.screenshotDir });
414
+ }
415
+ if (config.contextMarkdown) {
416
+ opts.push({ name: "--context", value: `loaded (${config.contextMarkdown.split("\n").length} lines)` });
417
+ }
418
+ if (config.secrets && Object.keys(config.secrets.values).length > 0) {
419
+ opts.push({ name: "--secrets", value: `${Object.keys(config.secrets.values).length} keys` });
420
+ }
421
+ if (config.debugLogPath) {
422
+ opts.push({ name: "--debug-log", value: config.debugLogPath });
423
+ }
424
+ if (config.debugConsole) {
425
+ opts.push({ name: "--debug", value: "true" });
426
+ }
427
+ if (config.stealth) {
428
+ opts.push({ name: "--stealth", value: "true" });
429
+ }
430
+ if (config.proxy) {
431
+ opts.push({ name: "--proxy", value: config.proxy });
432
+ }
433
+ if (config.videoDir) {
434
+ opts.push({ name: "--video", value: config.videoDir });
435
+ }
436
+ serializeModelConfig(opts, config.aiModelConfig);
437
+ return opts;
438
+ }
439
+ function serializeExecutorOptions(config) {
440
+ const opts = [];
441
+ opts.push({ name: "--instruction", value: config.instructionPath });
442
+ opts.push({ name: "--headless", value: String(config.headless) });
443
+ if (config.stepDelay !== void 0) {
444
+ opts.push({ name: "--step-delay", value: `${config.stepDelay}ms` });
445
+ }
446
+ if (config.screenshotDir) {
447
+ opts.push({ name: "--screenshots", value: config.screenshotDir });
448
+ }
449
+ if (config.contextMarkdown) {
450
+ opts.push({ name: "--context", value: `loaded (${config.contextMarkdown.split("\n").length} lines)` });
451
+ }
452
+ if (config.secrets && Object.keys(config.secrets.values).length > 0) {
453
+ opts.push({ name: "--secrets", value: `${Object.keys(config.secrets.values).length} keys` });
454
+ }
455
+ if (config.skipConfirmation) {
456
+ opts.push({ name: "--skip-confirmation", value: "true" });
457
+ }
458
+ if (config.dataFilePath) {
459
+ opts.push({ name: "--data", value: config.dataFilePath });
460
+ }
461
+ if (config.reuseSession === false) {
462
+ opts.push({ name: "--no-reuse-session", value: "true" });
463
+ }
464
+ if (config.debugLogPath) {
465
+ opts.push({ name: "--debug-log", value: config.debugLogPath });
466
+ }
467
+ if (config.debugConsole) {
468
+ opts.push({ name: "--debug", value: "true" });
469
+ }
470
+ if (config.approvalMode && config.approvalMode !== "cli") {
471
+ opts.push({ name: "--approval-mode", value: config.approvalMode });
472
+ }
473
+ if (config.notifyMode) {
474
+ opts.push({ name: "--notify", value: config.notifyMode });
475
+ }
476
+ if (config.enableSelectorCache) {
477
+ opts.push({ name: "--enable-selector-cache", value: "true" });
478
+ }
479
+ if (config.enableAgentFallback) {
480
+ opts.push({ name: "--enable-agent-fallback", value: "true" });
481
+ }
482
+ if (config.enableVisionFallback) {
483
+ opts.push({ name: "--enable-vision-fallback", value: "true" });
484
+ }
485
+ if (config.outputDir) {
486
+ opts.push({ name: "--output-dir", value: config.outputDir });
487
+ }
488
+ if (config.mergeDownloads) {
489
+ opts.push({ name: "--merge-downloads", value: "true" });
490
+ }
491
+ if (config.maintenance) {
492
+ opts.push({ name: "--maintenance", value: "true" });
493
+ }
494
+ if (config.maxRetries !== void 0) {
495
+ opts.push({ name: "--max-retries", value: String(config.maxRetries) });
496
+ }
497
+ if (config.retryWarningThreshold !== void 0) {
498
+ opts.push({ name: "--retry-warning-threshold", value: String(config.retryWarningThreshold) });
499
+ }
500
+ if (config.forceReport) {
501
+ opts.push({ name: "--report", value: "true" });
502
+ }
503
+ if (config.videoDir) {
504
+ opts.push({ name: "--video", value: config.videoDir });
505
+ }
506
+ if (config.callbackPort !== void 0) {
507
+ opts.push({ name: "--callback-port", value: String(config.callbackPort) });
508
+ }
509
+ if (config.approvalTimeoutMs !== void 0) {
510
+ opts.push({ name: "--approval-timeout", value: `${config.approvalTimeoutMs}ms` });
511
+ }
512
+ if (config.stealth) {
513
+ opts.push({ name: "--stealth", value: "true" });
514
+ }
515
+ if (config.proxy) {
516
+ opts.push({ name: "--proxy", value: config.proxy });
517
+ }
518
+ serializeModelConfig(opts, config.aiModelConfig);
519
+ return opts;
520
+ }
521
+ var MODEL_OVERRIDE_FLAGS = {
522
+ selector: "--model-selector",
523
+ extraction: "--model-extraction",
524
+ exploration: "--model-exploration",
525
+ review: "--model-review",
526
+ fallback: "--model-fallback",
527
+ vision: "--model-vision"
528
+ };
529
+ function serializeModelConfig(opts, aiModelConfig) {
530
+ if (!aiModelConfig) return;
531
+ opts.push({ name: "--model", value: aiModelConfig.modelId });
532
+ opts.push({ name: "--model-provider", value: aiModelConfig.provider });
533
+ if (aiModelConfig.baseURL) {
534
+ opts.push({ name: "--model-base-url", value: aiModelConfig.baseURL });
535
+ }
536
+ if (aiModelConfig.modelOverrides) {
537
+ for (const [purpose, modelId] of Object.entries(aiModelConfig.modelOverrides)) {
538
+ const flag = MODEL_OVERRIDE_FLAGS[purpose];
539
+ if (flag && modelId) {
540
+ opts.push({ name: flag, value: modelId });
541
+ }
542
+ }
543
+ }
544
+ }
545
+ function renderOptionsMarkdown(options) {
546
+ if (options.length === 0) return [];
547
+ const lines = [];
548
+ for (const opt of options) {
549
+ lines.push(`- \`${opt.name}\`: ${opt.value}`);
550
+ }
551
+ return lines;
552
+ }
553
+
554
+ // src/cli/secrets-loader.ts
555
+ import { readFile as readFile2 } from "fs/promises";
556
+ async function loadSecrets(path) {
557
+ if (!path) {
558
+ return { values: {}, keys: /* @__PURE__ */ new Set() };
559
+ }
560
+ const raw = await readFile2(path, "utf-8").catch(() => {
561
+ cancel(`secrets \u30D5\u30A1\u30A4\u30EB\u304C\u8AAD\u307F\u8FBC\u3081\u307E\u305B\u3093: ${path}`);
562
+ process.exit(1);
563
+ });
564
+ let parsed;
565
+ try {
566
+ parsed = JSON.parse(raw);
567
+ } catch {
568
+ cancel(`secrets \u30D5\u30A1\u30A4\u30EB\u306E JSON \u30D1\u30FC\u30B9\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${path}`);
569
+ process.exit(1);
570
+ }
571
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
572
+ cancel('secrets \u30D5\u30A1\u30A4\u30EB\u306F\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u5F62\u5F0F {"key": "value"} \u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059');
573
+ process.exit(1);
574
+ }
575
+ const values = {};
576
+ for (const [key, val] of Object.entries(parsed)) {
577
+ if (typeof val !== "string") {
578
+ cancel(`secrets \u306E\u5024\u306F\u6587\u5B57\u5217\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059 (key: "${key}")`);
579
+ process.exit(1);
580
+ }
581
+ values[key] = val;
582
+ }
583
+ return {
584
+ values,
585
+ keys: new Set(Object.keys(values))
586
+ };
587
+ }
588
+
589
+ // src/logger.ts
590
+ var NoopLogger = class {
591
+ step(_message) {
592
+ }
593
+ success(_message) {
594
+ }
595
+ error(_message) {
596
+ }
597
+ warn(_message) {
598
+ }
599
+ info(_message) {
600
+ }
601
+ debug(_message) {
602
+ }
603
+ };
604
+ var NoopSpinner = class {
605
+ start(_message) {
606
+ }
607
+ stop(_message) {
608
+ }
609
+ };
610
+
611
+ // src/browser/browser-client.ts
612
+ import { mkdir as mkdir2, readFile as readFile3, unlink } from "fs/promises";
613
+ import { tmpdir } from "os";
614
+ import { join as join2 } from "path";
615
+ import { spawn, execSync } from "child_process";
616
+
617
+ // src/browser/agent-browser-imports.ts
618
+ import { BrowserManager } from "agent-browser/dist/browser.js";
619
+ import { executeCommand } from "agent-browser/dist/actions.js";
620
+ import { resetRefs, getSnapshotStats } from "agent-browser/dist/snapshot.js";
621
+
622
+ // src/browser/stealth.ts
623
+ function webdriverPatch() {
624
+ return `
625
+ Object.defineProperty(navigator, 'webdriver', {
626
+ get: () => undefined,
627
+ });
628
+ `;
629
+ }
630
+ function chromePatch() {
631
+ return `
632
+ if (!window.chrome) {
633
+ Object.defineProperty(window, 'chrome', {
634
+ writable: true,
635
+ enumerable: true,
636
+ configurable: true,
637
+ value: {},
638
+ });
639
+ }
640
+ if (!window.chrome.runtime) {
641
+ window.chrome.runtime = {
642
+ connect: function() { return { onMessage: { addListener: function() {} }, postMessage: function() {}, onDisconnect: { addListener: function() {} } }; },
643
+ sendMessage: function(a, b, c) { if (typeof c === 'function') c(); },
644
+ };
645
+ }
646
+ `;
647
+ }
648
+ function pluginsPatch() {
649
+ return `
650
+ Object.defineProperty(navigator, 'plugins', {
651
+ get: () => {
652
+ const plugins = [
653
+ { name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },
654
+ { name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai', description: '' },
655
+ { name: 'Native Client', filename: 'internal-nacl-plugin', description: '' },
656
+ ];
657
+ plugins.refresh = () => {};
658
+ return plugins;
659
+ },
660
+ });
661
+ Object.defineProperty(navigator, 'mimeTypes', {
662
+ get: () => {
663
+ const mimeTypes = [
664
+ { type: 'application/pdf', suffixes: 'pdf', description: 'Portable Document Format' },
665
+ { type: 'application/x-google-chrome-pdf', suffixes: 'pdf', description: 'Portable Document Format' },
666
+ ];
667
+ mimeTypes.refresh = () => {};
668
+ return mimeTypes;
669
+ },
670
+ });
671
+ `;
672
+ }
673
+ function languagesPatch() {
674
+ return `
675
+ Object.defineProperty(navigator, 'languages', {
676
+ get: () => ['en-US', 'en'],
677
+ });
678
+ `;
679
+ }
680
+ function permissionsPatch() {
681
+ return `
682
+ const originalQuery = navigator.permissions.query.bind(navigator.permissions);
683
+ navigator.permissions.query = (parameters) => {
684
+ if (parameters.name === 'notifications') {
685
+ return Promise.resolve({ state: Notification.permission });
686
+ }
687
+ return originalQuery(parameters);
688
+ };
689
+ `;
690
+ }
691
+ function webglPatch() {
692
+ return `
693
+ const getParameter = WebGLRenderingContext.prototype.getParameter;
694
+ WebGLRenderingContext.prototype.getParameter = function(parameter) {
695
+ // UNMASKED_VENDOR_WEBGL
696
+ if (parameter === 37445) return 'Google Inc. (Intel)';
697
+ // UNMASKED_RENDERER_WEBGL
698
+ if (parameter === 37446) return 'ANGLE (Intel, Intel(R) UHD Graphics, OpenGL 4.1)';
699
+ return getParameter.call(this, parameter);
700
+ };
701
+ if (typeof WebGL2RenderingContext !== 'undefined') {
702
+ const getParameter2 = WebGL2RenderingContext.prototype.getParameter;
703
+ WebGL2RenderingContext.prototype.getParameter = function(parameter) {
704
+ if (parameter === 37445) return 'Google Inc. (Intel)';
705
+ if (parameter === 37446) return 'ANGLE (Intel, Intel(R) UHD Graphics, OpenGL 4.1)';
706
+ return getParameter2.call(this, parameter);
707
+ };
708
+ }
709
+ `;
710
+ }
711
+ function hardwareConcurrencyPatch() {
712
+ return `
713
+ Object.defineProperty(navigator, 'hardwareConcurrency', {
714
+ get: () => 4,
715
+ });
716
+ `;
717
+ }
718
+ function iframePatch() {
719
+ return `
720
+ try {
721
+ if (window.self !== window.top) return;
722
+ const originalAttachShadow = Element.prototype.attachShadow;
723
+ if (originalAttachShadow) {
724
+ Element.prototype.attachShadow = function() {
725
+ return originalAttachShadow.call(this, ...arguments);
726
+ };
727
+ }
728
+ } catch {}
729
+ `;
730
+ }
731
+ function userAgentPatch() {
732
+ return `
733
+ (function() {
734
+ var ua = navigator.userAgent || '';
735
+ if (ua.indexOf('HeadlessChrome') !== -1) {
736
+ var cleanUA = ua.replace('HeadlessChrome', 'Chrome');
737
+ Object.defineProperty(navigator, 'userAgent', {
738
+ get: () => cleanUA,
739
+ });
740
+ }
741
+ var av = navigator.appVersion || '';
742
+ if (av.indexOf('HeadlessChrome') !== -1) {
743
+ var cleanAV = av.replace('HeadlessChrome', 'Chrome');
744
+ Object.defineProperty(navigator, 'appVersion', {
745
+ get: () => cleanAV,
746
+ });
747
+ }
748
+ })();
749
+ `;
750
+ }
751
+ function getStealthScripts() {
752
+ return [
753
+ webdriverPatch(),
754
+ chromePatch(),
755
+ pluginsPatch(),
756
+ languagesPatch(),
757
+ permissionsPatch(),
758
+ webglPatch(),
759
+ hardwareConcurrencyPatch(),
760
+ iframePatch(),
761
+ userAgentPatch()
762
+ ];
763
+ }
764
+ async function applyStealthScripts(page) {
765
+ const context = page.context();
766
+ const scripts = getStealthScripts();
767
+ for (const script of scripts) {
768
+ await context.addInitScript(script);
769
+ }
770
+ }
771
+
772
+ // src/browser/browser-client.ts
773
+ function parseProxyUrl(proxyUrl) {
774
+ try {
775
+ const url = new URL(proxyUrl);
776
+ const server = `${url.protocol}//${url.hostname}${url.port ? `:${url.port}` : ""}`;
777
+ const username = url.username || void 0;
778
+ const password = url.password || void 0;
779
+ return { server, username, password };
780
+ } catch {
781
+ return { server: proxyUrl };
782
+ }
783
+ }
784
+ var commandId = 0;
785
+ function nextId() {
786
+ return String(++commandId);
787
+ }
788
+ var SCREENCAST_FPS = 5;
789
+ var FRAME_PUMP_INTERVAL_MS = Math.round(1e3 / SCREENCAST_FPS);
790
+ var SCREENCAST_RECOVERY_INTERVAL_MS = 500;
791
+ var AgentBrowser = class {
792
+ constructor(logger) {
793
+ this.browser = new BrowserManager();
794
+ // CDP Screencast + ffmpeg 録画用プロパティ
795
+ this.ffmpegProcess = null;
796
+ this.latestFrame = null;
797
+ this.framePumpInterval = null;
798
+ this.screencastRecoveryInterval = null;
799
+ this.isScreencastRecording = false;
800
+ this.screencastOutputPath = null;
801
+ this.logger = logger ?? new NoopLogger();
802
+ }
803
+ /**
804
+ * executeCommand のラッパー。失敗時はthrow。
805
+ */
806
+ async exec(command) {
807
+ const response = await executeCommand(command, this.browser);
808
+ if (!response.success) {
809
+ throw new Error(response.error);
810
+ }
811
+ return response;
812
+ }
813
+ async open(url, options) {
814
+ if (!this.browser.isLaunched()) {
815
+ const launchCommand = {
816
+ id: nextId(),
817
+ action: "launch",
818
+ headless: options?.headless ?? true,
819
+ viewport: { width: 1280, height: 720 },
820
+ colorScheme: options?.colorScheme,
821
+ downloadPath: options?.downloadPath,
822
+ proxy: options?.proxy ? parseProxyUrl(options.proxy) : void 0
823
+ };
824
+ await this.browser.launch(launchCommand);
825
+ if (options?.stealth) {
826
+ await applyStealthScripts(this.browser.getPage());
827
+ this.logger.debug("[stealth] patches applied");
828
+ }
829
+ }
830
+ await this.exec({ id: nextId(), action: "navigate", url });
831
+ }
832
+ async snapshot(options) {
833
+ const result = await this.browser.getSnapshot({ compact: true, cursor: true, ...options });
834
+ return result.tree;
835
+ }
836
+ async snapshotWithRefs(options) {
837
+ const result = await this.browser.getSnapshot({ compact: true, cursor: true, ...options });
838
+ return { tree: result.tree, refs: result.refs };
839
+ }
840
+ async click(ref) {
841
+ await this.exec({ id: nextId(), action: "click", selector: ref });
842
+ }
843
+ async fill(ref, value) {
844
+ await this.exec({ id: nextId(), action: "fill", selector: ref, value });
845
+ }
846
+ async type(ref, text) {
847
+ await this.exec({
848
+ id: nextId(),
849
+ action: "type",
850
+ selector: ref,
851
+ text
852
+ });
853
+ }
854
+ async select(ref, value) {
855
+ await this.exec({
856
+ id: nextId(),
857
+ action: "select",
858
+ selector: ref,
859
+ values: value
860
+ });
861
+ }
862
+ async check(ref) {
863
+ await this.exec({ id: nextId(), action: "check", selector: ref });
864
+ }
865
+ async uncheck(ref) {
866
+ await this.exec({ id: nextId(), action: "uncheck", selector: ref });
867
+ }
868
+ async navigate(url) {
869
+ await this.exec({ id: nextId(), action: "navigate", url });
870
+ }
871
+ async wait(ms) {
872
+ await this.exec({ id: nextId(), action: "wait", timeout: ms });
873
+ }
874
+ async scroll(direction, amount = 500, options) {
875
+ await this.exec({
876
+ id: nextId(),
877
+ action: "scroll",
878
+ direction,
879
+ amount,
880
+ selector: options?.selector
881
+ });
882
+ }
883
+ async screenshot(path, options) {
884
+ const response = await this.exec({
885
+ id: nextId(),
886
+ action: "screenshot",
887
+ path,
888
+ ...options
889
+ });
890
+ const data = response.data;
891
+ return data?.annotations ?? [];
892
+ }
893
+ /**
894
+ * アノテーション付きスクリーンショットを撮影し、画像バッファとアノテーション配列を返す。
895
+ * Vision Fallback 用: AI にスクリーンショットを渡して視覚的にセレクタを解決する。
896
+ */
897
+ async screenshotWithAnnotations() {
898
+ const tempPath = join2(tmpdir(), `vision-fallback-${Date.now()}.png`);
899
+ const annotations = await this.screenshot(tempPath, { annotate: true });
900
+ const imageBuffer = await readFile3(tempPath);
901
+ unlink(tempPath).catch(() => {
902
+ });
903
+ return { imageBuffer, annotations };
904
+ }
905
+ async download(selector, savePath) {
906
+ await this.exec({
907
+ id: nextId(),
908
+ action: "download",
909
+ selector,
910
+ path: savePath
911
+ });
912
+ return savePath;
913
+ }
914
+ async waitForDownload(savePath, timeout) {
915
+ await this.exec({
916
+ id: nextId(),
917
+ action: "waitfordownload",
918
+ path: savePath,
919
+ timeout: timeout ?? 3e4
920
+ });
921
+ }
922
+ async evaluate(script) {
923
+ const response = await this.exec({
924
+ id: nextId(),
925
+ action: "evaluate",
926
+ script
927
+ });
928
+ const data = response.data;
929
+ return data?.result ?? data;
930
+ }
931
+ async getText(selector) {
932
+ const response = await this.exec({
933
+ id: nextId(),
934
+ action: "gettext",
935
+ selector
936
+ });
937
+ const data = response.data;
938
+ return String(data ?? "");
939
+ }
940
+ async getInnerText(selector) {
941
+ const response = await this.exec({
942
+ id: nextId(),
943
+ action: "innertext",
944
+ selector
945
+ });
946
+ const data = response.data;
947
+ return String(data ?? "");
948
+ }
949
+ async url() {
950
+ return this.browser.getPage().url();
951
+ }
952
+ /** アクション前のページ数を返す */
953
+ async pageCount() {
954
+ return this.browser.getPages().length;
955
+ }
956
+ /**
957
+ * 新しいタブが開かれた場合、最新のタブに切り替える。
958
+ * @returns 切り替えが発生したかどうか
959
+ */
960
+ async switchToLatestTab(pageCountBefore) {
961
+ const pages = this.browser.getPages();
962
+ if (pages.length > pageCountBefore) {
963
+ const latestIndex = pages.length - 1;
964
+ await this.browser.switchTo(latestIndex);
965
+ return true;
966
+ }
967
+ return false;
968
+ }
969
+ /**
970
+ * DOM が安定するまで待機(SPA遷移後のレンダリング完了検出用)。
971
+ * Phase 1: networkidle を最大3秒待つ
972
+ * Phase 2: スナップショットをアダプティブ間隔でポーリングし、
973
+ * 2回連続で同一なら安定と判定。
974
+ * 初期100ms → 200ms → 300ms と段階的に延長(高速ページで早期検出)
975
+ */
976
+ async waitForDOMStability(timeoutMs = 5e3) {
977
+ const page = this.browser.getPage();
978
+ try {
979
+ await page.waitForLoadState("networkidle", { timeout: Math.min(timeoutMs, 3e3) });
980
+ } catch {
981
+ }
982
+ const pollEnd = Date.now() + timeoutMs;
983
+ let prevSnapshot = "";
984
+ let stableCount = 0;
985
+ let pollCount = 0;
986
+ while (Date.now() < pollEnd) {
987
+ const current = await this.snapshot();
988
+ if (current === prevSnapshot) {
989
+ stableCount++;
990
+ if (stableCount >= 2) {
991
+ this.logger.debug("[dom-stability] snapshot stable");
992
+ return;
993
+ }
994
+ } else {
995
+ stableCount = 0;
996
+ }
997
+ prevSnapshot = current;
998
+ pollCount++;
999
+ const interval = pollCount <= 1 ? 100 : pollCount <= 2 ? 200 : 300;
1000
+ await new Promise((r) => setTimeout(r, interval));
1001
+ }
1002
+ this.logger.debug("[dom-stability] timeout, proceeding");
1003
+ }
1004
+ /**
1005
+ * スナップショットが変化するまでポーリングで待機。
1006
+ * SPA遷移がまだ始まっていないケースに対応。
1007
+ * waitForDOMStability(変化が止まるのを待つ)とは逆で、変化が始まるのを待つ。
1008
+ */
1009
+ async waitForSnapshotChange(baselineSnapshot, timeoutMs = 1e4, pollIntervalMs = 500) {
1010
+ const deadline = Date.now() + timeoutMs;
1011
+ while (Date.now() < deadline) {
1012
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
1013
+ const current = await this.snapshot();
1014
+ if (current !== baselineSnapshot) {
1015
+ this.logger.debug("[snapshot-change] transition detected");
1016
+ await this.waitForDOMStability();
1017
+ return { changed: true, snapshot: await this.snapshot() };
1018
+ }
1019
+ }
1020
+ this.logger.debug("[snapshot-change] timeout, no change");
1021
+ return { changed: false, snapshot: baselineSnapshot };
1022
+ }
1023
+ /**
1024
+ * アクション後のナビゲーション/タブ切替を待機。
1025
+ * Stage 1: 新タブが開いていたら切り替え + ロード待機
1026
+ * Stage 2: 同一タブ内のナビゲーションなら URL 変化を待機
1027
+ * Stage 3: URL変化なしの場合、スナップショット変化でSPA遷移を検知し安定化待機
1028
+ */
1029
+ async waitForPossibleNavigation(urlBefore, pageCountBefore, timeout = 3e3) {
1030
+ const pollEnd = Date.now() + 500;
1031
+ let switched = false;
1032
+ while (Date.now() < pollEnd) {
1033
+ const currentCount = this.browser.getPages().length;
1034
+ this.logger.debug(`[nav] polling tabs: before=${pageCountBefore} now=${currentCount}`);
1035
+ if (currentCount > pageCountBefore) {
1036
+ switched = true;
1037
+ break;
1038
+ }
1039
+ await new Promise((r) => setTimeout(r, 100));
1040
+ }
1041
+ if (switched) {
1042
+ const pages = this.browser.getPages();
1043
+ const latestIndex = pages.length - 1;
1044
+ this.logger.debug(`[nav] new tab detected, switching to index ${latestIndex}`);
1045
+ await this.browser.switchTo(latestIndex);
1046
+ const page2 = this.browser.getPage();
1047
+ this.logger.debug(`[nav] switched, url=${page2.url()}`);
1048
+ try {
1049
+ await page2.waitForLoadState("load", { timeout });
1050
+ } catch {
1051
+ }
1052
+ this.logger.debug(`[nav] load complete, url=${page2.url()}`);
1053
+ return;
1054
+ }
1055
+ this.logger.debug(`[nav] no new tab, checking same-tab navigation (urlBefore=${urlBefore})`);
1056
+ const page = this.browser.getPage();
1057
+ let urlChanged = false;
1058
+ try {
1059
+ await page.waitForURL(
1060
+ (url) => url.toString() !== urlBefore,
1061
+ { timeout, waitUntil: "load" }
1062
+ );
1063
+ urlChanged = true;
1064
+ this.logger.debug(`[nav] URL changed to ${page.url()}`);
1065
+ await this.waitForDOMStability();
1066
+ } catch {
1067
+ this.logger.debug(`[nav] no URL change detected`);
1068
+ }
1069
+ if (!urlChanged) {
1070
+ this.logger.debug("[nav] checking SPA transition via snapshot diff");
1071
+ const snapshotBefore = await this.snapshot();
1072
+ const spaPollEnd = Date.now() + 5e3;
1073
+ const stageStart = Date.now();
1074
+ let spaDetected = false;
1075
+ let stableCount = 0;
1076
+ while (Date.now() < spaPollEnd) {
1077
+ await new Promise((r) => setTimeout(r, 300));
1078
+ const current = await this.snapshot();
1079
+ if (current !== snapshotBefore) {
1080
+ spaDetected = true;
1081
+ this.logger.debug("[nav] SPA transition detected (snapshot changed)");
1082
+ break;
1083
+ }
1084
+ stableCount++;
1085
+ if (Date.now() - stageStart >= 1e3 && stableCount >= 3) {
1086
+ this.logger.debug("[nav] snapshot stable, no SPA transition");
1087
+ break;
1088
+ }
1089
+ }
1090
+ if (spaDetected) {
1091
+ await this.waitForDOMStability();
1092
+ }
1093
+ }
1094
+ }
1095
+ /**
1096
+ * CDP Screencast + ffmpeg でビューポート全体を録画開始する。
1097
+ * タブ切り替えを含む単一動画を生成する。
1098
+ * @param outputDir 動画ファイル保存先ディレクトリ
1099
+ * @param _url 未使用(後方互換性のため残す)
1100
+ */
1101
+ async startRecording(outputDir, _url) {
1102
+ if (this.isScreencastRecording) {
1103
+ return;
1104
+ }
1105
+ try {
1106
+ execSync("which ffmpeg", { stdio: "ignore" });
1107
+ } catch {
1108
+ this.logger.warn(
1109
+ "ffmpeg \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002--video \u3092\u4F7F\u7528\u3059\u308B\u306B\u306F ffmpeg \u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u3066\u304F\u3060\u3055\u3044"
1110
+ );
1111
+ return;
1112
+ }
1113
+ await mkdir2(outputDir, { recursive: true });
1114
+ const now = /* @__PURE__ */ new Date();
1115
+ const pad = (n) => String(n).padStart(2, "0");
1116
+ const ts = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}T${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
1117
+ const outputPath = join2(outputDir, `recording-${ts}.webm`);
1118
+ this.screencastOutputPath = outputPath;
1119
+ this.ffmpegProcess = spawn("ffmpeg", [
1120
+ "-f",
1121
+ "image2pipe",
1122
+ "-framerate",
1123
+ String(SCREENCAST_FPS),
1124
+ "-vcodec",
1125
+ "mjpeg",
1126
+ "-i",
1127
+ "pipe:0",
1128
+ "-c:v",
1129
+ "libvpx",
1130
+ "-b:v",
1131
+ "1M",
1132
+ "-auto-alt-ref",
1133
+ "0",
1134
+ "-y",
1135
+ outputPath
1136
+ ], { stdio: ["pipe", "ignore", "ignore"] });
1137
+ this.ffmpegProcess.on("error", (err) => {
1138
+ this.logger.warn(`ffmpeg \u30D7\u30ED\u30BB\u30B9\u30A8\u30E9\u30FC: ${err.message}`);
1139
+ });
1140
+ this.latestFrame = null;
1141
+ const frameCallback = (frame) => {
1142
+ this.latestFrame = Buffer.from(frame.data, "base64");
1143
+ };
1144
+ try {
1145
+ await this.browser.startScreencast(frameCallback, {
1146
+ format: "jpeg",
1147
+ quality: 80
1148
+ });
1149
+ } catch (err) {
1150
+ this.logger.warn(
1151
+ `screencast \u958B\u59CB\u306B\u5931\u6557: ${err instanceof Error ? err.message : String(err)}`
1152
+ );
1153
+ this.cleanupFfmpeg();
1154
+ return;
1155
+ }
1156
+ this.isScreencastRecording = true;
1157
+ this.framePumpInterval = setInterval(() => {
1158
+ if (this.latestFrame && this.ffmpegProcess?.stdin?.writable) {
1159
+ try {
1160
+ this.ffmpegProcess.stdin.write(this.latestFrame);
1161
+ } catch {
1162
+ }
1163
+ }
1164
+ }, FRAME_PUMP_INTERVAL_MS);
1165
+ this.screencastRecoveryInterval = setInterval(async () => {
1166
+ if (!this.isScreencastRecording) return;
1167
+ try {
1168
+ if (!this.browser.isScreencasting()) {
1169
+ this.logger.debug("[screencast] \u56DE\u5FA9: screencast \u3092\u518D\u958B");
1170
+ await this.browser.startScreencast(frameCallback, {
1171
+ format: "jpeg",
1172
+ quality: 80
1173
+ });
1174
+ }
1175
+ } catch {
1176
+ }
1177
+ }, SCREENCAST_RECOVERY_INTERVAL_MS);
1178
+ }
1179
+ /**
1180
+ * ffmpeg プロセスをクリーンアップする内部ヘルパー。
1181
+ */
1182
+ cleanupFfmpeg() {
1183
+ if (this.ffmpegProcess) {
1184
+ try {
1185
+ this.ffmpegProcess.stdin?.end();
1186
+ this.ffmpegProcess.kill();
1187
+ } catch {
1188
+ }
1189
+ this.ffmpegProcess = null;
1190
+ }
1191
+ this.screencastOutputPath = null;
1192
+ }
1193
+ /**
1194
+ * 録画を停止し、動画ファイルパスを返す。
1195
+ * @returns paths: 保存された動画ファイルパス一覧, frames: 0(CDP screencast ではフレーム数不明)
1196
+ */
1197
+ async stopRecording() {
1198
+ if (!this.isScreencastRecording) {
1199
+ return { paths: [], frames: 0, error: "No recording in progress" };
1200
+ }
1201
+ if (this.framePumpInterval) {
1202
+ clearInterval(this.framePumpInterval);
1203
+ this.framePumpInterval = null;
1204
+ }
1205
+ if (this.screencastRecoveryInterval) {
1206
+ clearInterval(this.screencastRecoveryInterval);
1207
+ this.screencastRecoveryInterval = null;
1208
+ }
1209
+ try {
1210
+ await this.browser.stopScreencast();
1211
+ } catch {
1212
+ }
1213
+ const outputPath = this.screencastOutputPath;
1214
+ this.isScreencastRecording = false;
1215
+ this.latestFrame = null;
1216
+ if (this.ffmpegProcess) {
1217
+ const ffmpeg = this.ffmpegProcess;
1218
+ this.ffmpegProcess = null;
1219
+ await new Promise((resolve) => {
1220
+ const timeout = setTimeout(() => {
1221
+ ffmpeg.kill("SIGKILL");
1222
+ resolve();
1223
+ }, 1e4);
1224
+ ffmpeg.on("close", () => {
1225
+ clearTimeout(timeout);
1226
+ resolve();
1227
+ });
1228
+ try {
1229
+ ffmpeg.stdin?.end();
1230
+ } catch {
1231
+ clearTimeout(timeout);
1232
+ resolve();
1233
+ }
1234
+ });
1235
+ }
1236
+ this.screencastOutputPath = null;
1237
+ if (outputPath) {
1238
+ return { paths: [outputPath], frames: 0 };
1239
+ }
1240
+ return { paths: [], frames: 0 };
1241
+ }
1242
+ /**
1243
+ * 現在録画中かどうかを返す。
1244
+ */
1245
+ isRecording() {
1246
+ return this.isScreencastRecording;
1247
+ }
1248
+ async close() {
1249
+ if (this.isScreencastRecording) {
1250
+ try {
1251
+ await this.stopRecording();
1252
+ } catch {
1253
+ }
1254
+ }
1255
+ this.cleanupFfmpeg();
1256
+ await this.browser.close();
1257
+ }
1258
+ /**
1259
+ * ExplorationAction を適切なコマンドにディスパッチ
1260
+ */
1261
+ async executeStep(step) {
1262
+ const start = Date.now();
1263
+ try {
1264
+ let output = "";
1265
+ switch (step.action) {
1266
+ case "click":
1267
+ await this.click(step.selector);
1268
+ break;
1269
+ case "fill":
1270
+ await this.fill(step.selector, step.value || "");
1271
+ break;
1272
+ case "type":
1273
+ await this.type(step.selector, step.value || "");
1274
+ break;
1275
+ case "select":
1276
+ await this.select(step.selector, step.value || "");
1277
+ break;
1278
+ case "check":
1279
+ await this.check(step.selector);
1280
+ break;
1281
+ case "uncheck":
1282
+ await this.uncheck(step.selector);
1283
+ break;
1284
+ case "navigate":
1285
+ await this.navigate(step.value);
1286
+ break;
1287
+ case "wait":
1288
+ await this.wait(Number(step.value) || 1e3);
1289
+ break;
1290
+ case "scroll":
1291
+ await this.scroll(step.value || "down");
1292
+ break;
1293
+ case "extract": {
1294
+ const script = step.value || step.script || "";
1295
+ if (script) {
1296
+ const result = await this.evaluate(script);
1297
+ output = typeof result === "string" ? result : JSON.stringify(result);
1298
+ }
1299
+ break;
1300
+ }
1301
+ case "download": {
1302
+ const downloadPath = step.downloadPath || `/tmp/download-${Date.now()}.bin`;
1303
+ await this.download(step.selector, downloadPath);
1304
+ output = downloadPath;
1305
+ break;
1306
+ }
1307
+ default:
1308
+ output = `Unknown action: ${step.action}`;
1309
+ }
1310
+ return {
1311
+ success: true,
1312
+ output,
1313
+ durationMs: Date.now() - start
1314
+ };
1315
+ } catch (error) {
1316
+ return {
1317
+ success: false,
1318
+ output: "",
1319
+ error: error instanceof Error ? error.message : String(error),
1320
+ durationMs: Date.now() - start
1321
+ };
1322
+ }
1323
+ }
1324
+ };
1325
+
1326
+ // src/browser/snapshot-parser.ts
1327
+ function parseSnapshotLine(line) {
1328
+ const refMatch = line.match(/\[ref=(e\d+)\]/);
1329
+ if (!refMatch) return null;
1330
+ const ref = refMatch[1];
1331
+ const refIndex = line.indexOf(refMatch[0]);
1332
+ const beforeRef = line.slice(0, refIndex).trim();
1333
+ const afterRef = line.slice(refIndex + refMatch[0].length).trim();
1334
+ const cleanBefore = beforeRef.replace(/^-\s+/, "");
1335
+ let role;
1336
+ let name;
1337
+ const roleNameMatch = cleanBefore.match(/^(\S+)\s+"([^"]*)"$/);
1338
+ const roleOnlyMatch = cleanBefore.match(/^(\S+)$/);
1339
+ if (roleNameMatch) {
1340
+ role = roleNameMatch[1];
1341
+ name = roleNameMatch[2];
1342
+ } else if (roleOnlyMatch && !roleOnlyMatch[1].startsWith("[")) {
1343
+ role = roleOnlyMatch[1];
1344
+ name = "";
1345
+ } else {
1346
+ return null;
1347
+ }
1348
+ const attrSource = afterRef.replace(/:$/, "").trim();
1349
+ const attributes = {};
1350
+ const attrRegex = /\[(\w+)(?:=([^\]]*))?\]/g;
1351
+ let attrMatch;
1352
+ while ((attrMatch = attrRegex.exec(attrSource)) !== null) {
1353
+ const key = attrMatch[1];
1354
+ if (key === "ref") continue;
1355
+ const value = attrMatch[2] ?? "true";
1356
+ attributes[key] = value;
1357
+ }
1358
+ return { ref, role, name, attributes };
1359
+ }
1360
+ function findElementInSnapshot(snapshot, ref) {
1361
+ const lines = snapshot.split("\n");
1362
+ for (const line of lines) {
1363
+ if (!line.includes(`[ref=${ref}]`)) continue;
1364
+ const parsed = parseSnapshotLine(line);
1365
+ if (parsed && parsed.ref === ref) return parsed;
1366
+ }
1367
+ return null;
1368
+ }
1369
+ function parseAllElements(snapshot) {
1370
+ const lines = snapshot.split("\n");
1371
+ const elements = [];
1372
+ for (const line of lines) {
1373
+ const parsed = parseSnapshotLine(line);
1374
+ if (parsed) elements.push(parsed);
1375
+ }
1376
+ return elements;
1377
+ }
1378
+ function countElements(snapshot) {
1379
+ return parseAllElements(snapshot).length;
1380
+ }
1381
+
1382
+ // src/harness/error-classifier.ts
1383
+ function classifyFailure(ctx) {
1384
+ if (ctx.actionExecuted) {
1385
+ return "action_failed";
1386
+ }
1387
+ if (ctx.cachedRefUsed && !ctx.selectorResolved) {
1388
+ return "element_stale";
1389
+ }
1390
+ if (!ctx.selectorResolved && ctx.retryDetails && ctx.retryDetails.length > 0) {
1391
+ const anySnapshotChange = ctx.retryDetails.some((d) => d.snapshotChanged);
1392
+ if (anySnapshotChange) {
1393
+ return "page_structure_changed";
1394
+ }
1395
+ return "element_not_found";
1396
+ }
1397
+ if (!ctx.selectorResolved) {
1398
+ return "element_not_found";
1399
+ }
1400
+ const lowerError = ctx.error.toLowerCase();
1401
+ if (lowerError.includes("timeout") || lowerError.includes("navigation")) {
1402
+ return "navigation_timeout";
1403
+ }
1404
+ return "unknown";
1405
+ }
1406
+ function getRecoveryHint(category) {
1407
+ const keyMap = {
1408
+ element_not_found: "errors.elementNotFound",
1409
+ element_stale: "errors.elementStale",
1410
+ page_structure_changed: "errors.pageStructureChanged",
1411
+ navigation_timeout: "errors.navigationTimeout",
1412
+ action_failed: "errors.actionFailed",
1413
+ unknown: "errors.unknownError"
1414
+ };
1415
+ return t(keyMap[category]);
1416
+ }
1417
+
1418
+ // src/context/memory-operations.ts
1419
+ function parseAndAppendToMemory(rawValue, ctx) {
1420
+ const { dataStore, debugLogger, phase, step, collection } = ctx;
1421
+ debugLogger.log({
1422
+ phase,
1423
+ event: "memory_append",
1424
+ step,
1425
+ data: {
1426
+ collection,
1427
+ rawValuePreview: rawValue.slice(0, 200),
1428
+ rawValueLength: rawValue.length
1429
+ }
1430
+ });
1431
+ try {
1432
+ let dataToParse = rawValue;
1433
+ if (dataToParse.startsWith('"') && dataToParse.endsWith('"')) {
1434
+ try {
1435
+ const unquoted = JSON.parse(dataToParse);
1436
+ if (typeof unquoted === "string") {
1437
+ debugLogger.log({
1438
+ phase,
1439
+ event: "memory_append",
1440
+ step,
1441
+ data: { doubleStringifyDetected: true }
1442
+ });
1443
+ dataToParse = unquoted;
1444
+ }
1445
+ } catch {
1446
+ }
1447
+ }
1448
+ const parsed = JSON.parse(dataToParse);
1449
+ if (Array.isArray(parsed)) {
1450
+ const items = parsed.map((item2, i) => {
1451
+ if (typeof item2 === "object" && item2 !== null && !Array.isArray(item2)) {
1452
+ return item2;
1453
+ }
1454
+ return { index: i, value: item2 };
1455
+ });
1456
+ const firstKeys = items.length > 0 ? JSON.stringify(Object.keys(items[0])) : "N/A";
1457
+ debugLogger.log({
1458
+ phase,
1459
+ event: "memory_append",
1460
+ step,
1461
+ data: {
1462
+ parseResult: "array",
1463
+ arrayLength: items.length,
1464
+ firstElementKeys: firstKeys,
1465
+ collection,
1466
+ count: items.length
1467
+ }
1468
+ });
1469
+ dataStore.extend(collection, items);
1470
+ log.success(`Memory: appended ${items.length} items to "${collection}"`);
1471
+ return { appended: items.length };
1472
+ }
1473
+ const item = typeof parsed === "object" && parsed !== null ? parsed : { value: parsed };
1474
+ debugLogger.log({
1475
+ phase,
1476
+ event: "memory_append",
1477
+ step,
1478
+ data: {
1479
+ parseResult: "object",
1480
+ objectKeys: JSON.stringify(Object.keys(item)),
1481
+ collection
1482
+ }
1483
+ });
1484
+ dataStore.append(collection, item);
1485
+ log.success(`Memory: appended 1 item to "${collection}"`);
1486
+ return { appended: 1 };
1487
+ } catch {
1488
+ debugLogger.log({
1489
+ phase,
1490
+ event: "memory_append",
1491
+ step,
1492
+ data: {
1493
+ parseResult: "fallback",
1494
+ collection,
1495
+ rawValuePreview: rawValue.slice(0, 200)
1496
+ }
1497
+ });
1498
+ log.warn(
1499
+ `Memory: could not parse as JSON, storing as {value: "..."} \u2014 extract script should return an array of objects`
1500
+ );
1501
+ dataStore.append(collection, { value: rawValue });
1502
+ return { appended: 1 };
1503
+ }
1504
+ }
1505
+
1506
+ // src/harness/sleep.ts
1507
+ function sleep(ms) {
1508
+ return new Promise((resolve) => setTimeout(resolve, ms));
1509
+ }
1510
+
1511
+ // src/i18n/prompts/en/exploration.ts
1512
+ var COMMAND_SCHEMA = `### Basic Operations
1513
+ - \`click\`: Click element by selector
1514
+ - \`fill\`: Fill value in selector (clears existing value)
1515
+ - \`type\`: Type value into selector character by character
1516
+ - \`select\`: Select value from dropdown by selector
1517
+ - \`check\` / \`uncheck\`: Toggle checkbox by selector
1518
+
1519
+ ### Navigation
1520
+ - \`navigate\`: Navigate to URL in value (only URLs explicitly stated in goal/context; use click for page links)
1521
+ - \`wait\`: Wait for value ms
1522
+ - \`scroll\`: value: "up"|"down"|"left"|"right"
1523
+
1524
+ ### Data Extraction
1525
+ - \`extract\`: Extract data via script (JS) or extractPrompt (AI). Result in extractedData
1526
+ - **script must return a flat array of objects. No JSON.stringify() needed**
1527
+ - When extracting tables, dynamically get column names from header row
1528
+ - \`download\`: Download by clicking selector (specify save path with downloadPath)
1529
+ - \`export\`: Export memory data to file (exportCollection, exportFormat: csv|json, exportPath)
1530
+
1531
+ ### Memory Operations
1532
+ - \`memory_append\`: Accumulate data in memoryCollection (value or previous extract result)
1533
+ - **extract and memory_append must be in the same actions array**
1534
+ - \`memory_aggregate\`: aggregation: {collection, field, operation: sum|count|concat|min|max|avg|unique_count, outputVariable}
1535
+
1536
+ **Use only the actions listed above.**`;
1537
+ function getAgentInstructions(goal, contextMarkdown, secrets) {
1538
+ const contextSection = contextMarkdown ? `
1539
+ ## Supplementary Information
1540
+ ${contextMarkdown}
1541
+ ` : "";
1542
+ let secretsSection = "";
1543
+ if (secrets && Object.keys(secrets.values).length > 0) {
1544
+ const entries = Object.entries(secrets.values).map(([key, value]) => `- ${key}: ${value}`).join("\n");
1545
+ secretsSection = `
1546
+ ## Input Values (secrets)
1547
+ Use the following values when filling forms:
1548
+ ${entries}
1549
+
1550
+ **Important**: When using these values, set the corresponding \`variableName\` and choose the appropriate \`inputCategory\` (\`credential\` or \`user_data\`).
1551
+ `;
1552
+ }
1553
+ return `You are an AI explorer that navigates web pages to achieve goals.
1554
+ Use the browser tool to operate the browser and achieve the goal.
1555
+
1556
+ ## How to use the browser tool
1557
+
1558
+ ### Getting a Snapshot
1559
+ Call the browser tool with \`actions: []\` (empty array) to get the current page's snapshot (accessibility tree) and URL.
1560
+
1561
+ ### Executing Actions
1562
+ Pass operations as an array in the browser tool's \`actions\`. Keep to 1-3 actions per call.
1563
+
1564
+ ### Selector Format
1565
+ Use \`@eN\` format corresponding to \`[ref=eN]\` in the snapshot (e.g., \`@e1\`, \`@e10\`)
1566
+
1567
+ ### Available Commands
1568
+ ${COMMAND_SCHEMA}
1569
+
1570
+ ### Input Category (inputCategory)
1571
+ When entering values with fill/type, always include inputCategory:
1572
+ - \`credential\`: Authentication info (username, email, password, etc.)
1573
+ - \`user_data\`: User-specific data (name, address, phone number, etc.)
1574
+ - \`fixed\`: Fixed values (test values, constants, etc.)
1575
+ - \`navigation\`: URLs, search keywords, etc.
1576
+
1577
+ ### Variable Name (variableName)
1578
+ When entering values with fill/type, assign a short English camelCase variable name to \`variableName\`.
1579
+ Example: \`email\`, \`password\`, \`searchQuery\`, \`companyName\`, \`firstName\`
1580
+ Always use the same name for the same concept (e.g., if email input appears in 2 places, use \`email\` for both).
1581
+
1582
+ ### Data Capture (suggestedCaptures)
1583
+ When important data appears on the page as a result of operations (IDs, order numbers, URL changes, amounts, etc.),
1584
+ record them with \`suggestedCaptures\`. Prioritize values needed in subsequent steps.
1585
+ Available strategies:
1586
+ - \`snapshot\`: Regex match on snapshot (pattern required)
1587
+ - \`url\`: Regex match on URL (pattern required)
1588
+ - \`ai\`: AI extraction with natural language (prompt required)
1589
+ - \`expression\`: Template from existing variables (expression required)
1590
+ - \`evaluate\`: Execute JavaScript on page and capture result (expression contains JS code)
1591
+
1592
+ Example:
1593
+ \`\`\`json
1594
+ {
1595
+ "suggestedCaptures": [
1596
+ { "name": "orderId", "strategy": "snapshot", "pattern": "Order[:\uFF1A]\\\\s*([A-Z]+-\\\\d+)", "description": "Order confirmation number" },
1597
+ { "name": "resourceId", "strategy": "url", "pattern": "/orders/(\\\\d+)" }
1598
+ ]
1599
+ }
1600
+ \`\`\`
1601
+
1602
+ ## Critical Rules
1603
+ - **Only interact with elements visible in the snapshot.** Never guess about elements or URLs that may not exist
1604
+ - Navigate pages by clicking links/buttons on screen. Do not guess/construct URLs with \`navigate\` (except URLs explicitly stated in goal or context)
1605
+ - Only use \`@eN\` refs that exist as \`[ref=eN]\` in the most recent snapshot
1606
+ - If context (supplementary info) is provided, prioritize the procedures, paths, and hints described there
1607
+ - If the snapshot shows \`[... N items omitted]\`, the target element may be in the omitted section. Try \`scroll\` to move the view and re-fetch
1608
+
1609
+ ## Exploration Strategy
1610
+ 1. First check page state with \`actions: []\`
1611
+ 2. **Carefully read** the snapshot and identify links, buttons, and form elements related to the goal
1612
+ 3. Execute operations using the identified \`@eN\` refs
1613
+ 4. Check results and decide next operation (observe \u2192 decide \u2192 act cycle)
1614
+ 5. If target element is not found, \`scroll\` to move the view and check the snapshot again
1615
+ 6. Use \`select\` command for SELECT elements, not \`click\`
1616
+ 7. Use \`check\` / \`uncheck\` commands for checkboxes
1617
+ 8. Use \`click\` command for radio buttons \u2014 just click the desired option directly
1618
+ 9. Never use \`extract\` to investigate form elements. If radio buttons, checkboxes, or dropdowns are visible in the snapshot, interact with them directly using the appropriate command
1619
+
1620
+ ## Form Input Efficiency
1621
+ When filling large forms, strictly follow these rules:
1622
+ 1. **Enter each field only once** \u2014 Do not re-operate on fields that already have values
1623
+ 2. **Check filled fields in snapshot** \u2014 After fill/type, the field value appears in the snapshot. Skip fields showing values
1624
+ 3. **Progress systematically top to bottom** \u2014 Process fieldsets/sections in order. Don't go back to previous sections
1625
+ 4. **Track filled field count** \u2014 Reference operation history to know what you've already entered. Duplicate operations on the same selector are forbidden
1626
+ 5. **Submit after all fields are filled** \u2014 Once all required fields are complete, promptly click the submit button
1627
+ 6. **Check filledFields** \u2014 The browser tool result includes filledFields listing filled fields. Never re-enter fields in this list
1628
+
1629
+ ## Form Completion Verification
1630
+ Before returning your final report when filling forms:
1631
+ 1. Scan the ENTIRE current snapshot for remaining unfilled fields \u2014 check every fieldset/section
1632
+ 2. Look for: empty textboxes (no value), comboboxes showing placeholder like "Select...", unchecked checkboxes
1633
+ 3. The LAST section of a form is commonly missed \u2014 always verify the bottom of the form
1634
+ 4. After all fields are filled, click the submit/register button
1635
+ 5. Verify the success confirmation appears before declaring goalAchieved: true
1636
+ Do NOT declare goalAchieved without first confirming all form sections are complete and the form is submitted.
1637
+
1638
+ ## Data Collection Pattern (Pagination Traversal)
1639
+ Procedure: extract \u2192 memory_append \u2192 click "Next" \u2192 repeat \u2192 memory_aggregate for aggregation
1640
+
1641
+ Example (table extract \u2192 accumulate \u2192 next page \u2192 aggregate \u2192 CSV export):
1642
+ \`\`\`json
1643
+ [
1644
+ {"action":"extract","description":"Extract table","script":"(()=>{const h=[...document.querySelectorAll('table thead th')].map(t=>t.textContent.trim());return[...document.querySelectorAll('table tbody tr')].map(r=>{const c=r.querySelectorAll('td');return Object.fromEntries([...c].map((c,i)=>[h[i]||'col'+i,c.textContent.trim()]));});})()"},
1645
+ {"action":"memory_append","description":"Accumulate","memoryCollection":"data"},
1646
+ {"action":"click","description":"Next page","selector":"@e15"}
1647
+ ]
1648
+ \`\`\`
1649
+ Aggregate: \`[{"action":"memory_aggregate","description":"Sum total","aggregation":{"collection":"data","field":"amount","operation":"sum","outputVariable":"total"}}]\`
1650
+ CSV export: \`[{"action":"export","description":"CSV export","exportCollection":"data","exportFormat":"csv","exportPath":"/tmp/data.csv"}]\`
1651
+
1652
+ Note: No JSON.stringify() needed in script. Each row is a flat object. Check collection item count via memoryStatus.
1653
+
1654
+ **Important**: When paginating, you MUST continue until the LAST page. Check the page indicator (e.g., "Page X of Y") and keep clicking "Next" until you reach the final page. Do NOT stop after a few pages \u2014 process every single page. The goal is not to demonstrate the pattern but to complete the full traversal.
1655
+
1656
+ ## Using filledFields
1657
+ If the browser tool result includes \`filledFields\`, this is a list of already-filled form fields.
1658
+ Do not re-enter fields in this list. Only operate on unfilled fields.
1659
+
1660
+ ## Diff Snapshots
1661
+ After text input (fill/type), if page structure hasn't changed, you'll receive a compact snapshot listing all refs.
1662
+ - \`Unfilled:\` shows elements you still need to interact with
1663
+ - \`Filled:\` shows elements already filled
1664
+ - All refs from the compact snapshot are valid and usable
1665
+ - If you need full page context, call the browser tool with \`actions: []\`
1666
+
1667
+ ## Responding to nudgeMessage
1668
+ If the browser tool result includes \`nudgeMessage\`, it contains important guidance:
1669
+ - **Pagination progress**: Shows current page and remaining pages. You MUST continue until ALL pages are processed.
1670
+ - **Loop warning**: Indicates you are repeating the same actions without progress. Try a different approach.
1671
+ - **Form hints**: Lists remaining unfilled form fields.
1672
+ Always follow the instructions in the message.
1673
+
1674
+ ## When Goal is Achieved
1675
+ When you determine the goal has been achieved \u2014 for form-filling tasks, first verify all fields are filled and the form is submitted \u2014 stop calling the browser tool and return a final report in this JSON format:
1676
+ \`\`\`json
1677
+ { "goalAchieved": true, "summary": "Summary of what was accomplished" }
1678
+ \`\`\`
1679
+
1680
+ If you determine the goal cannot be achieved:
1681
+ \`\`\`json
1682
+ { "goalAchieved": false, "summary": "Summary of the situation and reasons" }
1683
+ \`\`\`
1684
+
1685
+ ---
1686
+ The following is task-specific information.
1687
+
1688
+ ## Goal
1689
+ ${goal}
1690
+ ${contextSection}${secretsSection}
1691
+ Respond in English.`;
1692
+ }
1693
+ function getInitialUserMessage() {
1694
+ return "Call the browser tool with actions:[] (empty array) to check the current page state. Then begin exploring toward the goal.";
1695
+ }
1696
+
1697
+ // src/i18n/prompts/ja/exploration.ts
1698
+ var COMMAND_SCHEMA2 = `### \u57FA\u672C\u64CD\u4F5C
1699
+ - \`click\`: selector \u3067\u8981\u7D20\u30AF\u30EA\u30C3\u30AF
1700
+ - \`fill\`: selector \u306B value \u3092\u5165\u529B\uFF08\u65E2\u5B58\u5024\u30AF\u30EA\u30A2\uFF09
1701
+ - \`type\`: selector \u306B value \u30921\u6587\u5B57\u305A\u3064\u5165\u529B
1702
+ - \`select\`: selector \u306E\u30C9\u30ED\u30C3\u30D7\u30C0\u30A6\u30F3\u3067 value \u3092\u9078\u629E
1703
+ - \`check\` / \`uncheck\`: selector \u306E\u30C1\u30A7\u30C3\u30AF\u30DC\u30C3\u30AF\u30B9\u64CD\u4F5C
1704
+
1705
+ ### \u30CA\u30D3\u30B2\u30FC\u30B7\u30E7\u30F3
1706
+ - \`navigate\`: value \u306EURL\u306B\u79FB\u52D5\uFF08\u30B4\u30FC\u30EB/\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3067\u660E\u793A\u3055\u308C\u305FURL\u306E\u307F\u3002\u30DA\u30FC\u30B8\u5185\u30EA\u30F3\u30AF\u306F click\uFF09
1707
+ - \`wait\`: value ms\u5F85\u6A5F
1708
+ - \`scroll\`: value: "up"|"down"|"left"|"right"
1709
+
1710
+ ### \u30C7\u30FC\u30BF\u62BD\u51FA
1711
+ - \`extract\`: script(JS) \u307E\u305F\u306F extractPrompt(AI) \u3067\u30C7\u30FC\u30BF\u62BD\u51FA\u3002\u7D50\u679C\u306F extractedData
1712
+ - **script \u306F\u30D5\u30E9\u30C3\u30C8\u306A\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u914D\u5217\u3092\u8FD4\u3059\u3053\u3068\u3002JSON.stringify()\u4E0D\u8981**
1713
+ - \u30C6\u30FC\u30D6\u30EB\u62BD\u51FA\u6642\u306F\u30D8\u30C3\u30C0\u30FC\u884C\u304B\u3089\u30AB\u30E9\u30E0\u540D\u3092\u52D5\u7684\u53D6\u5F97
1714
+ - \`download\`: selector \u30AF\u30EA\u30C3\u30AF\u3067\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\uFF08downloadPath \u3067\u4FDD\u5B58\u5148\u6307\u5B9A\u53EF\uFF09
1715
+ - \`export\`: \u30E1\u30E2\u30EA\u30C7\u30FC\u30BF\u3092\u30D5\u30A1\u30A4\u30EB\u51FA\u529B\uFF08exportCollection, exportFormat: csv|json, exportPath\uFF09
1716
+
1717
+ ### \u30E1\u30E2\u30EA\u64CD\u4F5C
1718
+ - \`memory_append\`: memoryCollection \u306B\u30C7\u30FC\u30BF\u84C4\u7A4D\uFF08value \u307E\u305F\u306F\u76F4\u524D extract \u7D50\u679C\uFF09
1719
+ - **extract \u3068 memory_append \u306F\u5FC5\u305A\u540C\u3058 actions \u914D\u5217\u306B\u542B\u3081\u308B\u3053\u3068**
1720
+ - \`memory_aggregate\`: aggregation: {collection, field, operation: sum|count|concat|min|max|avg|unique_count, outputVariable}
1721
+
1722
+ **\u4E0A\u8A18\u306E\u30A2\u30AF\u30B7\u30E7\u30F3\u306E\u307F\u4F7F\u7528\u3002**`;
1723
+ function getAgentInstructions2(goal, contextMarkdown, secrets) {
1724
+ const contextSection = contextMarkdown ? `
1725
+ ## \u88DC\u8DB3\u60C5\u5831
1726
+ ${contextMarkdown}
1727
+ ` : "";
1728
+ let secretsSection = "";
1729
+ if (secrets && Object.keys(secrets.values).length > 0) {
1730
+ const entries = Object.entries(secrets.values).map(([key, value]) => `- ${key}: ${value}`).join("\n");
1731
+ secretsSection = `
1732
+ ## \u5165\u529B\u5024\uFF08secrets\uFF09
1733
+ \u30D5\u30A9\u30FC\u30E0\u5165\u529B\u6642\u306B\u4EE5\u4E0B\u306E\u5024\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044:
1734
+ ${entries}
1735
+
1736
+ **\u91CD\u8981**: \u3053\u308C\u3089\u306E\u5024\u3092\u4F7F\u7528\u3059\u308B\u969B\u306F\u3001\u5BFE\u5FDC\u3059\u308B \`variableName\` \u3092\u8A2D\u5B9A\u3057\u3001\`inputCategory\` \u306F \`credential\` \u307E\u305F\u306F \`user_data\` \u3092\u9069\u5207\u306B\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002
1737
+ `;
1738
+ }
1739
+ return `\u3042\u306A\u305F\u306FWeb\u30DA\u30FC\u30B8\u3092\u63A2\u7D22\u3057\u3066\u30B4\u30FC\u30EB\u3092\u9054\u6210\u3059\u308BAI\u30A8\u30AF\u30B9\u30D7\u30ED\u30FC\u30E9\u30FC\u3067\u3059\u3002
1740
+ browser\u30C4\u30FC\u30EB\u3092\u4F7F\u3063\u3066\u30D6\u30E9\u30A6\u30B6\u3092\u64CD\u4F5C\u3057\u3001\u30B4\u30FC\u30EB\u3092\u9054\u6210\u3057\u3066\u304F\u3060\u3055\u3044\u3002
1741
+
1742
+ ## browser\u30C4\u30FC\u30EB\u306E\u4F7F\u3044\u65B9
1743
+
1744
+ ### \u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u53D6\u5F97
1745
+ browser\u30C4\u30FC\u30EB\u3092 \`actions: []\`\uFF08\u7A7A\u914D\u5217\uFF09\u3067\u547C\u3076\u3068\u3001\u73FE\u5728\u306E\u30DA\u30FC\u30B8\u306E\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\uFF08\u30A2\u30AF\u30BB\u30B7\u30D3\u30EA\u30C6\u30A3\u30C4\u30EA\u30FC\uFF09\u3068URL\u304C\u8FD4\u308A\u307E\u3059\u3002
1746
+
1747
+ ### \u30A2\u30AF\u30B7\u30E7\u30F3\u5B9F\u884C
1748
+ browser\u30C4\u30FC\u30EB\u306E \`actions\` \u306B\u64CD\u4F5C\u3092\u914D\u5217\u3067\u6E21\u3059\u3068\u9806\u6B21\u5B9F\u884C\u3055\u308C\u307E\u3059\u30021\u56DE\u306E\u547C\u3073\u51FA\u3057\u30671\u301C3\u30A2\u30AF\u30B7\u30E7\u30F3\u7A0B\u5EA6\u306B\u6291\u3048\u3066\u304F\u3060\u3055\u3044\u3002
1749
+
1750
+ ### \u30BB\u30EC\u30AF\u30BF\u5F62\u5F0F
1751
+ \u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u306E \`[ref=eN]\` \u306B\u5BFE\u5FDC\u3059\u308B \`@eN\` \u5F62\u5F0F\u3067\u6307\u5B9A\uFF08\u4F8B: \`@e1\`, \`@e10\`\uFF09
1752
+
1753
+ ### \u5229\u7528\u53EF\u80FD\u306A\u30B3\u30DE\u30F3\u30C9
1754
+ ${COMMAND_SCHEMA2}
1755
+
1756
+ ### \u5165\u529B\u5024\u306E\u30AB\u30C6\u30B4\u30EA\uFF08inputCategory\uFF09
1757
+ fill/type \u3067\u5024\u3092\u5165\u529B\u3059\u308B\u5834\u5408\u3001\u5FC5\u305A inputCategory \u3092\u4ED8\u4E0E:
1758
+ - \`credential\`: \u30E6\u30FC\u30B6\u30FC\u540D\u3001\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3001\u30D1\u30B9\u30EF\u30FC\u30C9\u7B49\u306E\u8A8D\u8A3C\u60C5\u5831
1759
+ - \`user_data\`: \u30E6\u30FC\u30B6\u30FC\u56FA\u6709\u306E\u30C7\u30FC\u30BF\uFF08\u540D\u524D\u3001\u4F4F\u6240\u3001\u96FB\u8A71\u756A\u53F7\u7B49\uFF09
1760
+ - \`fixed\`: \u56FA\u5B9A\u5024\uFF08\u30C6\u30B9\u30C8\u5024\u3001\u5B9A\u6570\u7B49\uFF09
1761
+ - \`navigation\`: URL\u3001\u691C\u7D22\u30AD\u30FC\u30EF\u30FC\u30C9\u7B49
1762
+
1763
+ ### \u5909\u6570\u540D\uFF08variableName\uFF09
1764
+ fill/type \u3067\u5024\u3092\u5165\u529B\u3059\u308B\u5834\u5408\u3001\u77ED\u3044\u82F1\u8A9E camelCase \u306E\u5909\u6570\u540D\u3092 \`variableName\` \u306B\u4ED8\u4E0E\u3057\u3066\u304F\u3060\u3055\u3044\u3002
1765
+ \u4F8B: \`email\`, \`password\`, \`searchQuery\`, \`companyName\`, \`firstName\`
1766
+ \u540C\u3058\u6982\u5FF5\u306E\u5024\u306B\u306F\u5FC5\u305A\u540C\u3058\u540D\u524D\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4F8B: \u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u5165\u529B\u304C2\u7B87\u6240\u3042\u308C\u3070\u4E21\u65B9 \`email\`\uFF09\u3002
1767
+
1768
+ ### \u30C7\u30FC\u30BF\u30AD\u30E3\u30D7\u30C1\u30E3\uFF08suggestedCaptures\uFF09
1769
+ \u64CD\u4F5C\u7D50\u679C\u3068\u3057\u3066\u91CD\u8981\u306A\u30C7\u30FC\u30BF\uFF08ID\u3001\u6CE8\u6587\u756A\u53F7\u3001URL\u5909\u5316\u3001\u91D1\u984D\u7B49\uFF09\u304C\u30DA\u30FC\u30B8\u306B\u73FE\u308C\u305F\u5834\u5408\u3001
1770
+ \`suggestedCaptures\` \u3067\u8A18\u9332\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u5F8C\u7D9A\u30B9\u30C6\u30C3\u30D7\u3067\u53C2\u7167\u304C\u5FC5\u8981\u306B\u306A\u308B\u5024\u3092\u512A\u5148\u3002
1771
+ \u5229\u7528\u53EF\u80FD\u306A strategy:
1772
+ - \`snapshot\`: \u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u306B\u6B63\u898F\u8868\u73FE\u30DE\u30C3\u30C1\uFF08pattern \u5FC5\u9808\uFF09
1773
+ - \`url\`: URL\u306B\u6B63\u898F\u8868\u73FE\u30DE\u30C3\u30C1\uFF08pattern \u5FC5\u9808\uFF09
1774
+ - \`ai\`: AI\u306B\u81EA\u7136\u8A00\u8A9E\u3067\u62BD\u51FA\u3055\u305B\u308B\uFF08prompt \u5FC5\u9808\uFF09
1775
+ - \`expression\`: \u65E2\u5B58\u5909\u6570\u304B\u3089\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u7D44\u307F\u7ACB\u3066\uFF08expression \u5FC5\u9808\uFF09
1776
+ - \`evaluate\`: \u30DA\u30FC\u30B8\u5185\u3067JavaScript\u3092\u5B9F\u884C\u3057\u3066\u7D50\u679C\u3092\u30AD\u30E3\u30D7\u30C1\u30E3\uFF08expression \u306BJS\u30B3\u30FC\u30C9\u3092\u6307\u5B9A\uFF09
1777
+
1778
+ \u4F8B:
1779
+ \`\`\`json
1780
+ {
1781
+ "suggestedCaptures": [
1782
+ { "name": "orderId", "strategy": "snapshot", "pattern": "\u6CE8\u6587\u756A\u53F7[:\uFF1A]\\\\s*([A-Z]+-\\\\d+)", "description": "\u6CE8\u6587\u78BA\u8A8D\u756A\u53F7" },
1783
+ { "name": "resourceId", "strategy": "url", "pattern": "/orders/(\\\\d+)" }
1784
+ ]
1785
+ }
1786
+ \`\`\`
1787
+
1788
+ ## \u6700\u91CD\u8981\u30EB\u30FC\u30EB
1789
+ - **\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u306B\u898B\u3048\u3066\u3044\u308B\u8981\u7D20\u3060\u3051\u3092\u64CD\u4F5C\u3059\u308B\u3053\u3068\u3002** \u5B58\u5728\u3059\u308B\u304B\u5206\u304B\u3089\u306A\u3044\u8981\u7D20\u3084URL\u3092\u63A8\u6E2C\u3057\u3066\u306F\u3044\u3051\u306A\u3044
1790
+ - \u30DA\u30FC\u30B8\u9077\u79FB\u306F\u753B\u9762\u4E0A\u306E\u30EA\u30F3\u30AF\u3084\u30DC\u30BF\u30F3\u3092 \`click\` \u3067\u884C\u3046\u3002\`navigate\` \u3067URL\u3092\u63A8\u6E2C\u30FB\u7D44\u307F\u7ACB\u3066\u308B\u3053\u3068\u306F\u7981\u6B62\uFF08\u30B4\u30FC\u30EB\u3084\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3067\u660E\u793A\u3055\u308C\u305FURL\u306F\u9664\u304F\uFF09
1791
+ - \u64CD\u4F5C\u5BFE\u8C61\u306E \`@eN\` \u306F\u3001\u76F4\u524D\u306E\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u306B \`[ref=eN]\` \u3068\u3057\u3066\u5B58\u5728\u3059\u308B\u3082\u306E\u3060\u3051\u3092\u4F7F\u7528\u3059\u308B
1792
+ - \u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\uFF08\u88DC\u8DB3\u60C5\u5831\uFF09\u304C\u63D0\u4F9B\u3055\u308C\u3066\u3044\u308B\u5834\u5408\u3001\u305D\u3053\u306B\u8A18\u8F09\u3055\u308C\u305F\u624B\u9806\u30FB\u5C0E\u7DDA\u30FB\u30D2\u30F3\u30C8\u3092\u512A\u5148\u7684\u306B\u53C2\u7167\u3059\u308B
1793
+ - \u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u306B \`[... N\u4EF6\u7701\u7565]\` \u3068\u3042\u308B\u5834\u5408\u3001\u76EE\u7684\u306E\u8981\u7D20\u304C\u7701\u7565\u90E8\u5206\u306B\u3042\u308B\u53EF\u80FD\u6027\u304C\u3042\u308B\u3002\`scroll\` \u3067\u753B\u9762\u3092\u52D5\u304B\u3057\u3066\u518D\u53D6\u5F97\u3092\u8A66\u307F\u308B\u3053\u3068
1794
+
1795
+ ## \u63A2\u7D22\u6226\u7565
1796
+ 1. \u307E\u305A \`actions: []\` \u3067\u30DA\u30FC\u30B8\u72B6\u614B\u3092\u78BA\u8A8D
1797
+ 2. \u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u3092**\u6CE8\u610F\u6DF1\u304F\u8AAD\u307F**\u3001\u30B4\u30FC\u30EB\u306B\u95A2\u9023\u3059\u308B\u30EA\u30F3\u30AF\u30FB\u30DC\u30BF\u30F3\u30FB\u30D5\u30A9\u30FC\u30E0\u8981\u7D20\u3092\u7279\u5B9A
1798
+ 3. \u7279\u5B9A\u3057\u305F\u8981\u7D20\u306E \`@eN\` \u3092\u4F7F\u3063\u3066\u64CD\u4F5C\u3092\u5B9F\u884C
1799
+ 4. \u7D50\u679C\u3092\u78BA\u8A8D\u3057\u3001\u6B21\u306E\u64CD\u4F5C\u3092\u6C7A\u5B9A\uFF08\u89B3\u5BDF\u2192\u5224\u65AD\u2192\u884C\u52D5\u306E\u7E70\u308A\u8FD4\u3057\uFF09
1800
+ 5. \u76EE\u7684\u306E\u8981\u7D20\u304C\u898B\u3064\u304B\u3089\u306A\u3044\u5834\u5408\u306F \`scroll\` \u3067\u753B\u9762\u3092\u79FB\u52D5\u3057\u3001\u518D\u5EA6\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u3092\u78BA\u8A8D\u3059\u308B
1801
+ 6. SELECT\u8981\u7D20\u306B\u306F \`click\` \u3067\u306F\u306A\u304F \`select\` \u30B3\u30DE\u30F3\u30C9\u3092\u4F7F\u7528
1802
+ 7. \u30C1\u30A7\u30C3\u30AF\u30DC\u30C3\u30AF\u30B9\u306B\u306F \`check\` / \`uncheck\` \u30B3\u30DE\u30F3\u30C9\u3092\u4F7F\u7528
1803
+ 8. \u30E9\u30B8\u30AA\u30DC\u30BF\u30F3\u306B\u306F \`click\` \u30B3\u30DE\u30F3\u30C9\u3092\u4F7F\u7528 \u2014 \u9078\u629E\u3057\u305F\u3044\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u76F4\u63A5\u30AF\u30EA\u30C3\u30AF\u3059\u308B
1804
+ 9. \u30D5\u30A9\u30FC\u30E0\u8981\u7D20\u306E\u8ABF\u67FB\u306B \`extract\` \u3092\u4F7F\u308F\u306A\u3044\u3053\u3068\u3002\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u306B\u30E9\u30B8\u30AA\u30DC\u30BF\u30F3\u30FB\u30C1\u30A7\u30C3\u30AF\u30DC\u30C3\u30AF\u30B9\u30FB\u30C9\u30ED\u30C3\u30D7\u30C0\u30A6\u30F3\u304C\u898B\u3048\u3066\u3044\u308C\u3070\u3001\u9069\u5207\u306A\u30B3\u30DE\u30F3\u30C9\u3067\u76F4\u63A5\u64CD\u4F5C\u3059\u308B
1805
+
1806
+ ## \u30D5\u30A9\u30FC\u30E0\u5165\u529B\u306E\u52B9\u7387\u5316
1807
+ \u5927\u304D\u306A\u30D5\u30A9\u30FC\u30E0\u3092\u5165\u529B\u3059\u308B\u969B\u306F\u3001\u4EE5\u4E0B\u306E\u30EB\u30FC\u30EB\u3092\u53B3\u5B88\u3057\u3066\u304F\u3060\u3055\u3044:
1808
+ 1. **\u5404\u30D5\u30A3\u30FC\u30EB\u30C9\u306F1\u56DE\u3060\u3051\u5165\u529B\u3059\u308B** \u2014 \u65E2\u306B\u5024\u304C\u5165\u529B\u3055\u308C\u305F\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u518D\u5EA6\u64CD\u4F5C\u3057\u306A\u3044\u3053\u3068
1809
+ 2. **\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u3067\u5165\u529B\u6E08\u307F\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u78BA\u8A8D\u3059\u308B** \u2014 fill/type \u5F8C\u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u306F\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u306B\u5165\u529B\u5024\u304C\u8868\u793A\u3055\u308C\u308B\u3002\u5024\u304C\u8868\u793A\u3055\u308C\u3066\u3044\u308B\u30D5\u30A3\u30FC\u30EB\u30C9\u306F\u30B9\u30AD\u30C3\u30D7\u3059\u308B
1810
+ 3. **\u4E0A\u304B\u3089\u4E0B\u3078\u4F53\u7CFB\u7684\u306B\u9032\u3080** \u2014 \u30D5\u30A9\u30FC\u30E0\u5185\u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u30BB\u30C3\u30C8/\u30BB\u30AF\u30B7\u30E7\u30F3\u3092\u4E0A\u304B\u3089\u9806\u306B\u51E6\u7406\u3059\u308B\u3002\u524D\u306E\u30BB\u30AF\u30B7\u30E7\u30F3\u306B\u623B\u3089\u306A\u3044
1811
+ 4. **\u5165\u529B\u6E08\u307F\u30D5\u30A3\u30FC\u30EB\u30C9\u6570\u3092\u610F\u8B58\u3059\u308B** \u2014 \u64CD\u4F5C\u5C65\u6B74\u3092\u53C2\u7167\u3057\u3001\u65E2\u306B\u4F55\u3092\u5165\u529B\u3057\u305F\u304B\u3092\u628A\u63E1\u3059\u308B\u3002\u540C\u3058\u30BB\u30EC\u30AF\u30BF\u3078\u306E\u91CD\u8907\u64CD\u4F5C\u306F\u7981\u6B62
1812
+ 5. **\u5168\u30D5\u30A3\u30FC\u30EB\u30C9\u5165\u529B\u5F8C\u306B\u9001\u4FE1\u3059\u308B** \u2014 \u30D5\u30A9\u30FC\u30E0\u5185\u306E\u5168\u5FC5\u9808\u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u57CB\u307E\u3063\u305F\u3089\u3001\u901F\u3084\u304B\u306B\u9001\u4FE1\u30DC\u30BF\u30F3\u3092\u30AF\u30EA\u30C3\u30AF\u3059\u308B
1813
+ 6. **filledFields \u3092\u78BA\u8A8D\u3059\u308B** \u2014 browser\u30C4\u30FC\u30EB\u306E\u7D50\u679C\u306B\u542B\u307E\u308C\u308B filledFields \u306F\u5165\u529B\u6E08\u307F\u30D5\u30A3\u30FC\u30EB\u30C9\u306E\u4E00\u89A7\u3002\u3053\u306E\u4E00\u89A7\u306B\u3042\u308B\u30D5\u30A3\u30FC\u30EB\u30C9\u306F\u7D76\u5BFE\u306B\u518D\u5165\u529B\u3057\u306A\u3044\u3053\u3068
1814
+
1815
+ ## \u30D5\u30A9\u30FC\u30E0\u5B8C\u4E86\u306E\u691C\u8A3C
1816
+ \u30D5\u30A9\u30FC\u30E0\u5165\u529B\u306E\u30B4\u30FC\u30EB\u3067\u6700\u7D42\u5831\u544A\u3092\u8FD4\u3059\u524D\u306B:
1817
+ 1. \u73FE\u5728\u306E\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u5168\u4F53\u3092\u30B9\u30AD\u30E3\u30F3\u3057\u3001\u672A\u5165\u529B\u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u306A\u3044\u304B\u78BA\u8A8D \u2014 \u5168\u30D5\u30A3\u30FC\u30EB\u30C9\u30BB\u30C3\u30C8/\u30BB\u30AF\u30B7\u30E7\u30F3\u3092\u30C1\u30A7\u30C3\u30AF
1818
+ 2. \u4EE5\u4E0B\u3092\u63A2\u3059: \u5024\u306E\u306A\u3044\u30C6\u30AD\u30B9\u30C8\u30DC\u30C3\u30AF\u30B9\u3001\u300CSelect...\u300D\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u8868\u793A\u306E\u30C9\u30ED\u30C3\u30D7\u30C0\u30A6\u30F3\u3001\u672A\u30C1\u30A7\u30C3\u30AF\u306E\u30C1\u30A7\u30C3\u30AF\u30DC\u30C3\u30AF\u30B9
1819
+ 3. \u30D5\u30A9\u30FC\u30E0\u306E\u6700\u5F8C\u306E\u30BB\u30AF\u30B7\u30E7\u30F3\u306F\u898B\u843D\u3068\u3057\u3084\u3059\u3044 \u2014 \u30D5\u30A9\u30FC\u30E0\u6700\u4E0B\u90E8\u3092\u5FC5\u305A\u78BA\u8A8D
1820
+ 4. \u5168\u30D5\u30A3\u30FC\u30EB\u30C9\u5165\u529B\u5F8C\u3001\u9001\u4FE1/\u767B\u9332\u30DC\u30BF\u30F3\u3092\u30AF\u30EA\u30C3\u30AF
1821
+ 5. goalAchieved: true \u3092\u5BA3\u8A00\u3059\u308B\u524D\u306B\u6210\u529F\u78BA\u8A8D\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u8868\u793A\u3092\u78BA\u8A8D
1822
+ \u5168\u30BB\u30AF\u30B7\u30E7\u30F3\u5B8C\u4E86\u30FB\u30D5\u30A9\u30FC\u30E0\u9001\u4FE1\u3092\u78BA\u8A8D\u3059\u308B\u307E\u3067 goalAchieved \u3092\u5BA3\u8A00\u3057\u306A\u3044\u3053\u3068\u3002
1823
+
1824
+ ## \u30C7\u30FC\u30BF\u53CE\u96C6\u30D1\u30BF\u30FC\u30F3\uFF08\u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u8D70\u67FB\uFF09
1825
+ \u624B\u9806: extract \u2192 memory_append \u2192 click\u300C\u6B21\u3078\u300D\u2192 \u7E70\u308A\u8FD4\u3057 \u2192 memory_aggregate \u3067\u96C6\u8A08
1826
+
1827
+ \u4F8B\uFF08\u30C6\u30FC\u30D6\u30EB\u62BD\u51FA \u2192 \u84C4\u7A4D \u2192 \u6B21\u30DA\u30FC\u30B8 \u2192 \u96C6\u8A08 \u2192 CSV\u51FA\u529B\uFF09:
1828
+ \`\`\`json
1829
+ [
1830
+ {"action":"extract","description":"\u30C6\u30FC\u30D6\u30EB\u62BD\u51FA","script":"(()=>{const h=[...document.querySelectorAll('table thead th')].map(t=>t.textContent.trim());return[...document.querySelectorAll('table tbody tr')].map(r=>{const c=r.querySelectorAll('td');return Object.fromEntries([...c].map((c,i)=>[h[i]||'col'+i,c.textContent.trim()]));});})()"},
1831
+ {"action":"memory_append","description":"\u84C4\u7A4D","memoryCollection":"data"},
1832
+ {"action":"click","description":"\u6B21\u30DA\u30FC\u30B8","selector":"@e15"}
1833
+ ]
1834
+ \`\`\`
1835
+ \u96C6\u8A08: \`[{"action":"memory_aggregate","description":"\u5408\u8A08","aggregation":{"collection":"data","field":"amount","operation":"sum","outputVariable":"total"}}]\`
1836
+ CSV\u51FA\u529B: \`[{"action":"export","description":"CSV\u51FA\u529B","exportCollection":"data","exportFormat":"csv","exportPath":"/tmp/data.csv"}]\`
1837
+
1838
+ \u6CE8\u610F: script \u3067 JSON.stringify() \u4E0D\u8981\u3002\u5404\u884C\u306F\u30D5\u30E9\u30C3\u30C8\u306A\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3002memoryStatus \u3067\u30B3\u30EC\u30AF\u30B7\u30E7\u30F3\u306E\u30A2\u30A4\u30C6\u30E0\u6570\u3092\u78BA\u8A8D\u53EF\u80FD\u3002
1839
+
1840
+ **\u91CD\u8981**: \u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u8D70\u67FB\u6642\u306F\u6700\u5F8C\u306E\u30DA\u30FC\u30B8\u307E\u3067\u5FC5\u305A\u7D99\u7D9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30DA\u30FC\u30B8\u8868\u793A\uFF08\u4F8B: "Page X of Y"\uFF09\u3092\u78BA\u8A8D\u3057\u3001\u6700\u7D42\u30DA\u30FC\u30B8\u306B\u5230\u9054\u3059\u308B\u307E\u3067\u300C\u6B21\u3078\u300D\u3092\u30AF\u30EA\u30C3\u30AF\u3057\u7D9A\u3051\u3066\u304F\u3060\u3055\u3044\u3002\u6570\u30DA\u30FC\u30B8\u3067\u6B62\u3081\u305A\u3001\u5168\u30DA\u30FC\u30B8\u3092\u51E6\u7406\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30D1\u30BF\u30FC\u30F3\u3092\u793A\u3059\u3053\u3068\u304C\u76EE\u7684\u3067\u306F\u306A\u304F\u3001\u5168\u30C7\u30FC\u30BF\u306E\u8D70\u67FB\u3092\u5B8C\u4E86\u3059\u308B\u3053\u3068\u304C\u76EE\u7684\u3067\u3059\u3002
1841
+
1842
+ ## filledFields \u306E\u6D3B\u7528
1843
+ browser\u30C4\u30FC\u30EB\u306E\u7D50\u679C\u306B \`filledFields\` \u304C\u542B\u307E\u308C\u3066\u3044\u308B\u5834\u5408\u3001\u3053\u308C\u306F\u65E2\u306B\u5165\u529B\u6E08\u307F\u306E\u30D5\u30A9\u30FC\u30E0\u30D5\u30A3\u30FC\u30EB\u30C9\u306E\u4E00\u89A7\u3067\u3059\u3002
1844
+ \u30EA\u30B9\u30C8\u306B\u542B\u307E\u308C\u308B\u30D5\u30A3\u30FC\u30EB\u30C9\u306B\u306F\u518D\u5EA6\u5165\u529B\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002\u672A\u5165\u529B\u306E\u30D5\u30A3\u30FC\u30EB\u30C9\u306E\u307F\u3092\u64CD\u4F5C\u3057\u3066\u304F\u3060\u3055\u3044\u3002
1845
+
1846
+ ## \u5DEE\u5206\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8
1847
+ \u30C6\u30AD\u30B9\u30C8\u5165\u529B\uFF08fill/type\uFF09\u5F8C\u306B\u30DA\u30FC\u30B8\u69CB\u9020\u304C\u5909\u308F\u3063\u3066\u3044\u306A\u3044\u5834\u5408\u3001\u5168ref\u3092\u4E00\u89A7\u306B\u3057\u305F\u30B3\u30F3\u30D1\u30AF\u30C8\u306A\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u304C\u8FD4\u3055\u308C\u307E\u3059\u3002
1848
+ - \`Unfilled:\` \u306F\u307E\u3060\u64CD\u4F5C\u304C\u5FC5\u8981\u306A\u8981\u7D20\u3092\u793A\u3057\u307E\u3059
1849
+ - \`Filled:\` \u306F\u5165\u529B\u6E08\u307F\u306E\u8981\u7D20\u3092\u793A\u3057\u307E\u3059
1850
+ - \u30B3\u30F3\u30D1\u30AF\u30C8\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u5185\u306E\u5168ref\u306F\u6709\u52B9\u3067\u4F7F\u7528\u53EF\u80FD\u3067\u3059
1851
+ - \u5B8C\u5168\u306A\u30DA\u30FC\u30B8\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u304C\u5FC5\u8981\u306A\u5834\u5408\u306F \`actions: []\` \u3067\u30D6\u30E9\u30A6\u30B6\u30C4\u30FC\u30EB\u3092\u547C\u3073\u51FA\u3057\u3066\u304F\u3060\u3055\u3044
1852
+
1853
+ ## nudgeMessage \u3078\u306E\u5BFE\u5FDC
1854
+ browser\u30C4\u30FC\u30EB\u306E\u7D50\u679C\u306B \`nudgeMessage\` \u304C\u542B\u307E\u308C\u3066\u3044\u308B\u5834\u5408\u3001\u91CD\u8981\u306A\u30AC\u30A4\u30C0\u30F3\u30B9\u304C\u8A18\u8F09\u3055\u308C\u3066\u3044\u307E\u3059:
1855
+ - **\u30DA\u30FC\u30B8\u30CD\u30FC\u30B7\u30E7\u30F3\u9032\u6357**: \u73FE\u5728\u306E\u30DA\u30FC\u30B8\u3068\u6B8B\u308A\u30DA\u30FC\u30B8\u6570\u3092\u793A\u3057\u307E\u3059\u3002\u5168\u30DA\u30FC\u30B8\u306E\u51E6\u7406\u304C\u5B8C\u4E86\u3059\u308B\u307E\u3067\u7D9A\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002
1856
+ - **\u30EB\u30FC\u30D7\u8B66\u544A**: \u540C\u3058\u64CD\u4F5C\u3092\u7E70\u308A\u8FD4\u3057\u3066\u3044\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002\u5225\u306E\u30A2\u30D7\u30ED\u30FC\u30C1\u3092\u8A66\u3057\u3066\u304F\u3060\u3055\u3044\u3002
1857
+ - **\u30D5\u30A9\u30FC\u30E0\u30D2\u30F3\u30C8**: \u672A\u5165\u529B\u306E\u30D5\u30A9\u30FC\u30E0\u30D5\u30A3\u30FC\u30EB\u30C9\u4E00\u89A7\u3067\u3059\u3002
1858
+ \u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u6307\u793A\u306B\u5FC5\u305A\u5F93\u3063\u3066\u304F\u3060\u3055\u3044\u3002
1859
+
1860
+ ## \u30B4\u30FC\u30EB\u9054\u6210\u6642
1861
+ \u30B4\u30FC\u30EB\u304C\u9054\u6210\u3055\u308C\u305F\u3068\u5224\u65AD\u3057\u305F\u3089 \u2014 \u30D5\u30A9\u30FC\u30E0\u5165\u529B\u30BF\u30B9\u30AF\u306E\u5834\u5408\u3001\u5168\u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u5165\u529B\u6E08\u307F\u3067\u30D5\u30A9\u30FC\u30E0\u304C\u9001\u4FE1\u6E08\u307F\u3067\u3042\u308B\u3053\u3068\u3092\u5148\u306B\u78BA\u8A8D\u3059\u308B\u3053\u3068 \u2014 browser\u30C4\u30FC\u30EB\u306E\u547C\u3073\u51FA\u3057\u3092\u505C\u6B62\u3057\u3001\u4EE5\u4E0B\u306EJSON\u5F62\u5F0F\u3067\u6700\u7D42\u5831\u544A\u3092\u8FD4\u3057\u3066\u304F\u3060\u3055\u3044:
1862
+ \`\`\`json
1863
+ { "goalAchieved": true, "summary": "\u9054\u6210\u5185\u5BB9\u306E\u8981\u7D04" }
1864
+ \`\`\`
1865
+
1866
+ \u30B4\u30FC\u30EB\u304C\u9054\u6210\u3067\u304D\u306A\u3044\u3068\u5224\u65AD\u3057\u305F\u5834\u5408\u3082\u540C\u69D8\u306B:
1867
+ \`\`\`json
1868
+ { "goalAchieved": false, "summary": "\u72B6\u6CC1\u306E\u8981\u7D04\u3068\u7406\u7531" }
1869
+ \`\`\`
1870
+
1871
+ ---
1872
+ \u4EE5\u4E0B\u306F\u3053\u306E\u30BF\u30B9\u30AF\u56FA\u6709\u306E\u60C5\u5831\u3067\u3059\u3002
1873
+
1874
+ ## \u30B4\u30FC\u30EB
1875
+ ${goal}
1876
+ ${contextSection}${secretsSection}
1877
+ \u65E5\u672C\u8A9E\u3067\u5FDC\u7B54\u3057\u3066\u304F\u3060\u3055\u3044\u3002`;
1878
+ }
1879
+ function getInitialUserMessage2() {
1880
+ return "browser\u30C4\u30FC\u30EB\u3092actions:[]\uFF08\u7A7A\u914D\u5217\uFF09\u3067\u547C\u3073\u51FA\u3057\u3001\u73FE\u5728\u306E\u30DA\u30FC\u30B8\u72B6\u614B\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u305D\u306E\u5F8C\u3001\u30B4\u30FC\u30EB\u9054\u6210\u306B\u5411\u3051\u3066\u63A2\u7D22\u3092\u958B\u59CB\u3057\u3066\u304F\u3060\u3055\u3044\u3002";
1881
+ }
1882
+
1883
+ // src/i18n/prompts/en/review.ts
1884
+ function getReviewSystemPrompt() {
1885
+ return `You are an expert reviewer of web browser automation procedures.
1886
+
1887
+ ## Task
1888
+ Analyze the recorded steps and determine the following:
1889
+
1890
+ 1. **Remove unnecessary steps**: Identify and exclude trial-and-error during exploration, dead ends, unnecessary navigation, and duplicate operations
1891
+ 2. **Risk level assessment**: Assess the risk of each step
1892
+ - \`low\`: Low side-effect operations like input, scrolling, navigation
1893
+ - \`medium\`: Login, form submission, settings changes, etc.
1894
+ - \`high\`: Irreversible operations like deletion, payment, data modification
1895
+ 3. **Approval requirement**: Whether user approval is needed before execution
1896
+
1897
+ ## Capture Candidate Identification
1898
+ If you detect important data used in subsequent steps (IDs, order numbers, URL changes, amounts, etc.),
1899
+ include them as suggestedCaptures in each step's response.
1900
+ Available strategies: snapshot (regex), url (URL regex), ai (AI extraction), expression (template)
1901
+
1902
+ ## Notes
1903
+ - Failed steps (success=false) should generally be excluded
1904
+ - Keep only the shortest path steps needed to achieve the goal
1905
+ - riskLevel is used in the Chrome extension approval flow (medium/high requires approval)
1906
+ - Respond in English`;
1907
+ }
1908
+ function createReviewUserPrompt(goal, recordedSteps, goalAchieved, interventions) {
1909
+ const stepsList = recordedSteps.map(
1910
+ (s) => `${s.ordinal}. [${s.success ? "OK" : "FAIL"}] ${s.action.description} (action: ${s.action.action}, url: ${s.url})${s.error ? ` Error: ${s.error}` : ""}`
1911
+ ).join("\n");
1912
+ const interventionsSection = interventions && interventions.length > 0 ? `
1913
+ ## User Interventions During Exploration
1914
+ The user modified the exploration direction at the following points.
1915
+ Steps before the intervention likely contain trial-and-error, while steps after are more accurate.
1916
+
1917
+ ${interventions.map(
1918
+ (iv, i) => `${i + 1}. Around step #${iv.stepIndex}: "${iv.userInstruction}" (URL: ${iv.url})`
1919
+ ).join("\n")}
1920
+
1921
+ **Review note**: Actively exclude unnecessary steps before intervention points.
1922
+ ` : "";
1923
+ return `## Goal
1924
+ ${goal}
1925
+
1926
+ ## Goal Achievement Status
1927
+ ${goalAchieved ? "Achieved" : "Not achieved"}
1928
+ ${interventionsSection}
1929
+ ## All Recorded Steps
1930
+ ${stepsList}`;
1931
+ }
1932
+
1933
+ // src/i18n/prompts/ja/review.ts
1934
+ function getReviewSystemPrompt2() {
1935
+ return `\u3042\u306A\u305F\u306FWeb\u30D6\u30E9\u30A6\u30B6\u81EA\u52D5\u64CD\u4F5C\u306E\u624B\u9806\u66F8\u3092\u30EC\u30D3\u30E5\u30FC\u3059\u308B\u30A8\u30AD\u30B9\u30D1\u30FC\u30C8\u3067\u3059\u3002
1936
+
1937
+ ## \u30BF\u30B9\u30AF
1938
+ \u8A18\u9332\u3055\u308C\u305F\u30B9\u30C6\u30C3\u30D7\u3092\u5206\u6790\u3057\u3001\u4EE5\u4E0B\u3092\u5224\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044:
1939
+
1940
+ 1. **\u4E0D\u8981\u30B9\u30C6\u30C3\u30D7\u306E\u9664\u5916**: \u63A2\u7D22\u4E2D\u306E\u8A66\u884C\u932F\u8AA4\u3001\u884C\u304D\u6B62\u307E\u308A\u3001\u4E0D\u8981\u306A\u30CA\u30D3\u30B2\u30FC\u30B7\u30E7\u30F3\u3001\u91CD\u8907\u64CD\u4F5C\u3092\u7279\u5B9A\u3057\u9664\u5916
1941
+ 2. **\u30EA\u30B9\u30AF\u30EC\u30D9\u30EB\u5224\u5B9A**: \u5404\u30B9\u30C6\u30C3\u30D7\u306E\u30EA\u30B9\u30AF\u3092\u5224\u5B9A
1942
+ - \`low\`: \u5165\u529B\u3001\u30B9\u30AF\u30ED\u30FC\u30EB\u3001\u30CA\u30D3\u30B2\u30FC\u30B7\u30E7\u30F3\u306A\u3069\u526F\u4F5C\u7528\u306E\u5C11\u306A\u3044\u64CD\u4F5C
1943
+ - \`medium\`: \u30ED\u30B0\u30A4\u30F3\u3001\u30D5\u30A9\u30FC\u30E0\u9001\u4FE1\u3001\u8A2D\u5B9A\u5909\u66F4\u306A\u3069
1944
+ - \`high\`: \u524A\u9664\u3001\u6C7A\u6E08\u3001\u30C7\u30FC\u30BF\u5909\u66F4\u306A\u3069\u4E0D\u53EF\u9006\u306A\u64CD\u4F5C
1945
+ 3. **\u627F\u8A8D\u8981\u5426\u5224\u5B9A**: \u5B9F\u884C\u524D\u306B\u30E6\u30FC\u30B6\u30FC\u627F\u8A8D\u304C\u5FC5\u8981\u304B\u3069\u3046\u304B
1946
+
1947
+ ## \u30AD\u30E3\u30D7\u30C1\u30E3\u5019\u88DC\u306E\u8B58\u5225
1948
+ \u64CD\u4F5C\u7D50\u679C\u3068\u3057\u3066\u5F8C\u7D9A\u30B9\u30C6\u30C3\u30D7\u3067\u4F7F\u7528\u3055\u308C\u308B\u91CD\u8981\u306A\u30C7\u30FC\u30BF\uFF08ID\u3001\u6CE8\u6587\u756A\u53F7\u3001URL\u5909\u5316\u3001\u91D1\u984D\u306A\u3069\uFF09\u3092\u691C\u51FA\u3057\u305F\u5834\u5408\u3001
1949
+ suggestedCaptures \u3068\u3057\u3066\u30EC\u30B9\u30DD\u30F3\u30B9\u306E\u5404\u30B9\u30C6\u30C3\u30D7\u306B\u542B\u3081\u3066\u304F\u3060\u3055\u3044\u3002
1950
+ \u5229\u7528\u53EF\u80FD\u306A strategy: snapshot\uFF08\u6B63\u898F\u8868\u73FE\uFF09, url\uFF08URL\u6B63\u898F\u8868\u73FE\uFF09, ai\uFF08AI\u62BD\u51FA\uFF09, expression\uFF08\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\uFF09
1951
+
1952
+ ## \u6CE8\u610F\u4E8B\u9805
1953
+ - \u5931\u6557\u30B9\u30C6\u30C3\u30D7\uFF08success=false\uFF09\u306F\u57FA\u672C\u7684\u306B\u9664\u5916\u3057\u3066\u304F\u3060\u3055\u3044
1954
+ - \u30B4\u30FC\u30EB\u9054\u6210\u306B\u5FC5\u8981\u306A\u6700\u77ED\u7D4C\u8DEF\u306E\u30B9\u30C6\u30C3\u30D7\u306E\u307F\u6B8B\u3057\u3066\u304F\u3060\u3055\u3044
1955
+ - riskLevel \u306F Chrome\u62E1\u5F35\u306E\u627F\u8A8D\u30D5\u30ED\u30FC\u3067\u4F7F\u7528\u3055\u308C\u307E\u3059\uFF08medium/high \u306F\u627F\u8A8D\u304C\u5FC5\u8981\uFF09
1956
+ - \u65E5\u672C\u8A9E\u3067\u5FDC\u7B54\u3057\u3066\u304F\u3060\u3055\u3044`;
1957
+ }
1958
+ function createReviewUserPrompt2(goal, recordedSteps, goalAchieved, interventions) {
1959
+ const stepsList = recordedSteps.map(
1960
+ (s) => `${s.ordinal}. [${s.success ? "\u6210\u529F" : "\u5931\u6557"}] ${s.action.description} (action: ${s.action.action}, url: ${s.url})${s.error ? ` \u30A8\u30E9\u30FC: ${s.error}` : ""}`
1961
+ ).join("\n");
1962
+ const interventionsSection = interventions && interventions.length > 0 ? `
1963
+ ## \u63A2\u7D22\u4E2D\u306E\u30E6\u30FC\u30B6\u30FC\u4ECB\u5165
1964
+ \u4EE5\u4E0B\u306E\u30BF\u30A4\u30DF\u30F3\u30B0\u3067\u30E6\u30FC\u30B6\u30FC\u304C\u63A2\u7D22\u65B9\u91DD\u3092\u4FEE\u6B63\u3057\u307E\u3057\u305F\u3002
1965
+ \u4ECB\u5165\u524D\u306E\u30B9\u30C6\u30C3\u30D7\u306B\u306F\u63A2\u7D22\u306E\u8A66\u884C\u932F\u8AA4\u304C\u542B\u307E\u308C\u3066\u3044\u308B\u53EF\u80FD\u6027\u304C\u9AD8\u304F\u3001\u4ECB\u5165\u5F8C\u306E\u30B9\u30C6\u30C3\u30D7\u304C\u3088\u308A\u6B63\u78BA\u306A\u624B\u9806\u3067\u3059\u3002
1966
+
1967
+ ${interventions.map(
1968
+ (iv, i) => `${i + 1}. \u30B9\u30C6\u30C3\u30D7#${iv.stepIndex}\u4ED8\u8FD1: "${iv.userInstruction}" (URL: ${iv.url})`
1969
+ ).join("\n")}
1970
+
1971
+ **\u30EC\u30D3\u30E5\u30FC\u6642\u306E\u6CE8\u610F**: \u4ECB\u5165\u30DD\u30A4\u30F3\u30C8\u3088\u308A\u524D\u306E\u4E0D\u8981\u306A\u30B9\u30C6\u30C3\u30D7\u306F\u7A4D\u6975\u7684\u306B\u9664\u5916\u3057\u3066\u304F\u3060\u3055\u3044\u3002
1972
+ ` : "";
1973
+ return `## \u30B4\u30FC\u30EB
1974
+ ${goal}
1975
+
1976
+ ## \u30B4\u30FC\u30EB\u9054\u6210\u72B6\u6CC1
1977
+ ${goalAchieved ? "\u9054\u6210\u6E08\u307F" : "\u672A\u9054\u6210"}
1978
+ ${interventionsSection}
1979
+ ## \u8A18\u9332\u3055\u308C\u305F\u5168\u30B9\u30C6\u30C3\u30D7
1980
+ ${stepsList}`;
1981
+ }
1982
+
1983
+ // src/i18n/prompts/en/selector.ts
1984
+ function buildSelectorMessages(snapshot, selector, stepDescription, url, contextMarkdown, retryContext, workingMemorySummary) {
1985
+ const system = [
1986
+ "You are a selector resolver for browser automation.",
1987
+ "Based on the step description, identify the target element in the accessibility snapshot.",
1988
+ "",
1989
+ "## Matching Guidelines",
1990
+ "1. Infer the type of target element (button, input field, link, etc.) from the step description",
1991
+ "2. Find the element in the snapshot that semantically best matches the step description",
1992
+ "3. Use surrounding text, labels, placeholders, and roles as clues",
1993
+ "4. Even without an exact match, return the ref of the most likely element",
1994
+ "5. Return an empty string only if the target element truly does not exist in the snapshot"
1995
+ ].join("\n");
1996
+ const parts = [
1997
+ "## Step Description (Most Important)",
1998
+ stepDescription,
1999
+ "",
2000
+ "## Selector Hints (Reference)",
2001
+ "The following is reference information for identifying the element. Even without an exact match, choose the element semantically closest to the step description.",
2002
+ JSON.stringify(pruneSelectorHints(selector)),
2003
+ "",
2004
+ "Notes:",
2005
+ '- If tagName is "unknown" or ":", there is no tag name information',
2006
+ "- ariaLabel is auto-generated from the step description and may not match the actual aria-label attribute",
2007
+ "- Prioritize semantic match with the step description over exact selector match",
2008
+ "",
2009
+ "## Current URL",
2010
+ url
2011
+ ];
2012
+ if (workingMemorySummary) {
2013
+ parts.push("", "## Execution Context", workingMemorySummary);
2014
+ }
2015
+ parts.push(
2016
+ "",
2017
+ "## Accessibility Snapshot",
2018
+ snapshot
2019
+ );
2020
+ const includeContext = contextMarkdown && !(retryContext && retryContext.attempt > 0);
2021
+ if (includeContext) {
2022
+ parts.push("", "## Supplementary Context", contextMarkdown);
2023
+ }
2024
+ if (retryContext && retryContext.attempt > 0) {
2025
+ parts.push(
2026
+ "",
2027
+ "## Recovery Context (Attempt " + (retryContext.attempt + 1) + ")"
2028
+ );
2029
+ if (retryContext.failureHints) {
2030
+ parts.push(`Pattern: ${retryContext.failureHints}`);
2031
+ }
2032
+ if (retryContext.failureHistory.length > 0) {
2033
+ const recent = retryContext.failureHistory.slice(-2);
2034
+ parts.push(`Previous: ${recent.join("; ")}`);
2035
+ }
2036
+ if (retryContext.previousAiResponse) {
2037
+ try {
2038
+ const parsed = JSON.parse(retryContext.previousAiResponse);
2039
+ if (parsed.reasoning) {
2040
+ parts.push(`Last reasoning: "${parsed.reasoning}"`);
2041
+ }
2042
+ } catch {
2043
+ parts.push(`Last response: ${retryContext.previousAiResponse.slice(0, 200)}`);
2044
+ }
2045
+ }
2046
+ if (!retryContext.snapshotChanged) {
2047
+ parts.push(
2048
+ "Snapshot unchanged \u2014 try partial matching, role-based matching, or inference from nearby elements."
2049
+ );
2050
+ }
2051
+ }
2052
+ return { system, userPrompt: parts.join("\n") };
2053
+ }
2054
+ function pruneSelectorHints(selector) {
2055
+ const pruned = {};
2056
+ for (const [key, value] of Object.entries(selector)) {
2057
+ if (value === null || value === void 0 || value === "" || value === "unknown" || value === ":") continue;
2058
+ pruned[key] = value;
2059
+ }
2060
+ return pruned;
2061
+ }
2062
+
2063
+ // src/i18n/prompts/ja/selector.ts
2064
+ function buildSelectorMessages2(snapshot, selector, stepDescription, url, contextMarkdown, retryContext, workingMemorySummary) {
2065
+ const system = [
2066
+ "\u3042\u306A\u305F\u306F\u30D6\u30E9\u30A6\u30B6\u81EA\u52D5\u64CD\u4F5C\u306E\u30BB\u30EC\u30AF\u30BF\u30EA\u30BE\u30EB\u30D0\u30FC\u3067\u3059\u3002",
2067
+ "\u30B9\u30C6\u30C3\u30D7\u306E\u8AAC\u660E\u306B\u57FA\u3065\u3044\u3066\u3001\u30A2\u30AF\u30BB\u30B7\u30D3\u30EA\u30C6\u30A3\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u5185\u306E\u64CD\u4F5C\u5BFE\u8C61\u8981\u7D20\u3092\u7279\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
2068
+ "",
2069
+ "## \u30DE\u30C3\u30C1\u30F3\u30B0\u306E\u6307\u91DD",
2070
+ "1. \u30B9\u30C6\u30C3\u30D7\u306E\u8AAC\u660E\u304B\u3089\u64CD\u4F5C\u5BFE\u8C61\u306E\u8981\u7D20\u306E\u7A2E\u985E\uFF08\u30DC\u30BF\u30F3\u3001\u5165\u529B\u30D5\u30A3\u30FC\u30EB\u30C9\u3001\u30EA\u30F3\u30AF\u7B49\uFF09\u3092\u63A8\u5B9A\u3059\u308B",
2071
+ "2. \u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u5185\u3067\u3001\u30B9\u30C6\u30C3\u30D7\u306E\u8AAC\u660E\u3068\u610F\u5473\u7684\u306B\u6700\u3082\u4E00\u81F4\u3059\u308B\u8981\u7D20\u3092\u63A2\u3059",
2072
+ "3. \u5468\u8FBA\u306E\u30C6\u30AD\u30B9\u30C8\u3001\u30E9\u30D9\u30EB\u3001\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u3001role \u3082\u624B\u304C\u304B\u308A\u306B\u3059\u308B",
2073
+ "4. \u5B8C\u5168\u4E00\u81F4\u304C\u306A\u304F\u3066\u3082\u6700\u3082\u53EF\u80FD\u6027\u306E\u9AD8\u3044\u8981\u7D20\u306E ref \u3092\u8FD4\u3059",
2074
+ "5. \u8A72\u5F53\u3059\u308B\u8981\u7D20\u304C\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u5185\u306B\u672C\u5F53\u306B\u5B58\u5728\u3057\u306A\u3044\u5834\u5408\u306E\u307F\u7A7A\u6587\u5B57\u3092\u8FD4\u3059"
2075
+ ].join("\n");
2076
+ const parts = [
2077
+ "## \u30B9\u30C6\u30C3\u30D7\u306E\u8AAC\u660E\uFF08\u6700\u91CD\u8981\uFF09",
2078
+ stepDescription,
2079
+ "",
2080
+ "## \u30BB\u30EC\u30AF\u30BF\u306E\u30D2\u30F3\u30C8\uFF08\u53C2\u8003\u60C5\u5831\uFF09",
2081
+ "\u4EE5\u4E0B\u306F\u8981\u7D20\u3092\u7279\u5B9A\u3059\u308B\u305F\u3081\u306E\u53C2\u8003\u60C5\u5831\u3067\u3059\u3002\u5B8C\u5168\u4E00\u81F4\u3057\u306A\u3044\u5834\u5408\u3067\u3082\u3001\u30B9\u30C6\u30C3\u30D7\u306E\u8AAC\u660E\u304B\u3089\u610F\u5473\u7684\u306B\u6700\u3082\u8FD1\u3044\u8981\u7D20\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\u3002",
2082
+ JSON.stringify(pruneSelectorHints2(selector)),
2083
+ "",
2084
+ "\u6CE8\u610F:",
2085
+ '- tagName \u304C "unknown" \u3084 ":" \u306E\u5834\u5408\u306F\u30BF\u30B0\u540D\u60C5\u5831\u304C\u3042\u308A\u307E\u305B\u3093',
2086
+ "- ariaLabel \u306F\u30B9\u30C6\u30C3\u30D7\u8AAC\u660E\u304B\u3089\u81EA\u52D5\u751F\u6210\u3055\u308C\u305F\u3082\u306E\u3067\u3042\u308A\u3001\u5B9F\u969B\u306E aria-label \u5C5E\u6027\u3068\u4E00\u81F4\u3057\u306A\u3044\u3053\u3068\u304C\u3042\u308A\u307E\u3059",
2087
+ "- \u30BB\u30EC\u30AF\u30BF\u306E\u5B8C\u5168\u4E00\u81F4\u3088\u308A\u3082\u30B9\u30C6\u30C3\u30D7\u306E\u8AAC\u660E\u3068\u306E\u610F\u5473\u7684\u4E00\u81F4\u3092\u512A\u5148\u3057\u3066\u304F\u3060\u3055\u3044",
2088
+ "",
2089
+ "## \u73FE\u5728\u306EURL",
2090
+ url
2091
+ ];
2092
+ if (workingMemorySummary) {
2093
+ parts.push("", "## \u5B9F\u884C\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8", workingMemorySummary);
2094
+ }
2095
+ parts.push(
2096
+ "",
2097
+ "## \u30A2\u30AF\u30BB\u30B7\u30D3\u30EA\u30C6\u30A3\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8",
2098
+ snapshot
2099
+ );
2100
+ const includeContext = contextMarkdown && !(retryContext && retryContext.attempt > 0);
2101
+ if (includeContext) {
2102
+ parts.push("", "## \u88DC\u8DB3\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8", contextMarkdown);
2103
+ }
2104
+ if (retryContext && retryContext.attempt > 0) {
2105
+ parts.push(
2106
+ "",
2107
+ "## \u56DE\u5FA9\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\uFF08\u8A66\u884C" + (retryContext.attempt + 1) + "\u56DE\u76EE\uFF09"
2108
+ );
2109
+ if (retryContext.failureHints) {
2110
+ parts.push(`\u30D1\u30BF\u30FC\u30F3: ${retryContext.failureHints}`);
2111
+ }
2112
+ if (retryContext.failureHistory.length > 0) {
2113
+ const recent = retryContext.failureHistory.slice(-2);
2114
+ parts.push(`\u76F4\u8FD1\u306E\u5931\u6557: ${recent.join("; ")}`);
2115
+ }
2116
+ if (retryContext.previousAiResponse) {
2117
+ try {
2118
+ const parsed = JSON.parse(retryContext.previousAiResponse);
2119
+ if (parsed.reasoning) {
2120
+ parts.push(`\u524D\u56DE\u306E\u63A8\u8AD6: "${parsed.reasoning}"`);
2121
+ }
2122
+ } catch {
2123
+ parts.push(`\u524D\u56DE\u306E\u5FDC\u7B54: ${retryContext.previousAiResponse.slice(0, 200)}`);
2124
+ }
2125
+ }
2126
+ if (!retryContext.snapshotChanged) {
2127
+ parts.push(
2128
+ "\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u672A\u5909\u5316 \u2014 \u90E8\u5206\u4E00\u81F4\u3001role\u30D9\u30FC\u30B9\u306E\u30DE\u30C3\u30C1\u30F3\u30B0\u3001\u8FD1\u63A5\u8981\u7D20\u304B\u3089\u306E\u63A8\u5B9A\u3092\u8A66\u307F\u3066\u304F\u3060\u3055\u3044\u3002"
2129
+ );
2130
+ }
2131
+ }
2132
+ return { system, userPrompt: parts.join("\n") };
2133
+ }
2134
+ function pruneSelectorHints2(selector) {
2135
+ const pruned = {};
2136
+ for (const [key, value] of Object.entries(selector)) {
2137
+ if (value === null || value === void 0 || value === "" || value === "unknown" || value === ":") continue;
2138
+ pruned[key] = value;
2139
+ }
2140
+ return pruned;
2141
+ }
2142
+
2143
+ // src/i18n/prompts/en/goal-agent.ts
2144
+ function buildFallbackMessages(snapshot, step, failureHistory, url, contextMarkdown) {
2145
+ const system = [
2146
+ "You are a problem-solving agent for browser automation.",
2147
+ "Normal selector resolution has failed at all phases. Analyze the entire page and find an alternative operation path.",
2148
+ "",
2149
+ "## Alternative Path Search Guidelines",
2150
+ "1. Check if the target element is hidden inside a hamburger menu, dropdown, or accordion",
2151
+ "2. Check if a modal or overlay is covering the target element",
2152
+ "3. Check if the page is in an unexpected state (not logged in, error state, loading)",
2153
+ "4. Check if the target element is above the current scroll position",
2154
+ "5. Check if a semantically equivalent alternative element exists (e.g., a button instead of a link)",
2155
+ "",
2156
+ "## Alternative Strategy Selection Criteria",
2157
+ "- direct_ref: Try a different ref directly (when a semantically equivalent element is found)",
2158
+ "- scroll_up_and_retry: When the target element might be above the current view",
2159
+ "- expand_collapsed: When a collapsed section/menu needs to be expanded (prerequisiteRef required)",
2160
+ "- dismiss_overlay: When a modal/overlay needs to be closed (prerequisiteRef required)",
2161
+ "- tab_navigation: When the element can be reached via Tab key focus navigation",
2162
+ "- not_found: When the target element truly does not exist on the page"
2163
+ ].join("\n");
2164
+ const parts = [
2165
+ "## Step Being Attempted",
2166
+ `Description: ${step.description}`,
2167
+ `Action: ${step.action.type}`,
2168
+ `Selector hints: ${JSON.stringify(step.action.selector, null, 2)}`,
2169
+ `URL: ${url}`,
2170
+ "",
2171
+ "## Failure History",
2172
+ ...failureHistory.map((f, i) => ` ${i + 1}. ${f}`),
2173
+ "",
2174
+ "## Current Page Snapshot (Filtered)",
2175
+ snapshot
2176
+ ];
2177
+ if (contextMarkdown) {
2178
+ parts.push("", "## Supplementary Context", contextMarkdown);
2179
+ }
2180
+ return { system, userPrompt: parts.join("\n") };
2181
+ }
2182
+
2183
+ // src/i18n/prompts/ja/goal-agent.ts
2184
+ function buildFallbackMessages2(snapshot, step, failureHistory, url, contextMarkdown) {
2185
+ const system = [
2186
+ "\u3042\u306A\u305F\u306F\u30D6\u30E9\u30A6\u30B6\u81EA\u52D5\u64CD\u4F5C\u306E\u554F\u984C\u89E3\u6C7A\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u3067\u3059\u3002",
2187
+ "\u901A\u5E38\u306E\u30BB\u30EC\u30AF\u30BF\u89E3\u6C7A\u304C\u5168\u30D5\u30A7\u30FC\u30BA\u5931\u6557\u3057\u307E\u3057\u305F\u3002\u30DA\u30FC\u30B8\u5168\u4F53\u3092\u5206\u6790\u3057\u3066\u4EE3\u66FF\u64CD\u4F5C\u30D1\u30B9\u3092\u898B\u3064\u3051\u3066\u304F\u3060\u3055\u3044\u3002",
2188
+ "",
2189
+ "## \u4EE3\u66FF\u30D1\u30B9\u63A2\u7D22\u306E\u6307\u91DD",
2190
+ "1. \u30CF\u30F3\u30D0\u30FC\u30AC\u30FC\u30E1\u30CB\u30E5\u30FC\u3001\u30C9\u30ED\u30C3\u30D7\u30C0\u30A6\u30F3\u3001\u30A2\u30B3\u30FC\u30C7\u30A3\u30AA\u30F3\u306E\u4E2D\u306B\u5BFE\u8C61\u8981\u7D20\u304C\u96A0\u308C\u3066\u3044\u306A\u3044\u304B\u78BA\u8A8D",
2191
+ "2. \u30E2\u30FC\u30C0\u30EB\u3084\u30AA\u30FC\u30D0\u30FC\u30EC\u30A4\u304C\u524D\u9762\u306B\u3042\u3063\u3066\u5BFE\u8C61\u8981\u7D20\u3092\u96A0\u3057\u3066\u3044\u306A\u3044\u304B\u78BA\u8A8D",
2192
+ "3. \u30DA\u30FC\u30B8\u304C\u60F3\u5B9A\u3068\u7570\u306A\u308B\u72B6\u614B\uFF08\u672A\u30ED\u30B0\u30A4\u30F3\u3001\u30A8\u30E9\u30FC\u72B6\u614B\u3001\u30ED\u30FC\u30C7\u30A3\u30F3\u30B0\u4E2D\uFF09\u3067\u306A\u3044\u304B\u78BA\u8A8D",
2193
+ "4. \u5BFE\u8C61\u8981\u7D20\u304C\u4E0A\u65B9\u306B\u3042\u3063\u3066\u30B9\u30AF\u30ED\u30FC\u30EB\u3067\u96A0\u308C\u3066\u3044\u306A\u3044\u304B\u78BA\u8A8D",
2194
+ "5. \u610F\u5473\u7684\u306B\u540C\u7B49\u306A\u5225\u306E\u8981\u7D20\uFF08\u30EA\u30F3\u30AF\u3067\u306F\u306A\u304F\u30DC\u30BF\u30F3\u3001\u7B49\uFF09\u304C\u5B58\u5728\u3057\u306A\u3044\u304B\u78BA\u8A8D",
2195
+ "",
2196
+ "## \u4EE3\u66FF\u6226\u7565\u306E\u9078\u629E\u57FA\u6E96",
2197
+ "- direct_ref: \u5225\u306E ref \u3092\u76F4\u63A5\u8A66\u3059\uFF08\u610F\u5473\u7684\u306B\u540C\u7B49\u306A\u8981\u7D20\u304C\u898B\u3064\u304B\u3063\u305F\u5834\u5408\uFF09",
2198
+ "- scroll_up_and_retry: \u5BFE\u8C61\u8981\u7D20\u304C\u4E0A\u65B9\u306B\u3042\u308B\u53EF\u80FD\u6027\u304C\u3042\u308B\u5834\u5408",
2199
+ "- expand_collapsed: \u6298\u308A\u7573\u307E\u308C\u305F\u30BB\u30AF\u30B7\u30E7\u30F3/\u30E1\u30CB\u30E5\u30FC\u3092\u5C55\u958B\u3059\u308B\u5FC5\u8981\u304C\u3042\u308B\u5834\u5408\uFF08prerequisiteRef \u5FC5\u9808\uFF09",
2200
+ "- dismiss_overlay: \u30E2\u30FC\u30C0\u30EB/\u30AA\u30FC\u30D0\u30FC\u30EC\u30A4\u3092\u9589\u3058\u308B\u5FC5\u8981\u304C\u3042\u308B\u5834\u5408\uFF08prerequisiteRef \u5FC5\u9808\uFF09",
2201
+ "- tab_navigation: Tab \u30AD\u30FC\u3067\u30D5\u30A9\u30FC\u30AB\u30B9\u79FB\u52D5\u3057\u3066\u5230\u9054\u3067\u304D\u308B\u5834\u5408",
2202
+ "- not_found: \u672C\u5F53\u306B\u30DA\u30FC\u30B8\u4E0A\u306B\u5BFE\u8C61\u8981\u7D20\u304C\u5B58\u5728\u3057\u306A\u3044\u5834\u5408"
2203
+ ].join("\n");
2204
+ const parts = [
2205
+ "## \u9054\u6210\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u305F\u30B9\u30C6\u30C3\u30D7",
2206
+ `\u8AAC\u660E: ${step.description}`,
2207
+ `\u30A2\u30AF\u30B7\u30E7\u30F3: ${step.action.type}`,
2208
+ `\u30BB\u30EC\u30AF\u30BF\u30D2\u30F3\u30C8: ${JSON.stringify(step.action.selector, null, 2)}`,
2209
+ `URL: ${url}`,
2210
+ "",
2211
+ "## \u3053\u308C\u307E\u3067\u306E\u5931\u6557\u5C65\u6B74",
2212
+ ...failureHistory.map((f, i) => ` ${i + 1}. ${f}`),
2213
+ "",
2214
+ "## \u73FE\u5728\u306E\u30DA\u30FC\u30B8\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\uFF08\u30D5\u30A3\u30EB\u30BF\u6E08\u307F\uFF09",
2215
+ snapshot
2216
+ ];
2217
+ if (contextMarkdown) {
2218
+ parts.push("", "## \u88DC\u8DB3\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8", contextMarkdown);
2219
+ }
2220
+ return { system, userPrompt: parts.join("\n") };
2221
+ }
2222
+
2223
+ // src/i18n/prompts/en/vision.ts
2224
+ function buildVisionMessages(annotations, selector, stepDescription, url, failureHistory) {
2225
+ const system = [
2226
+ "You are a visual selector resolver for browser automation.",
2227
+ "The text-based accessibility tree could not find the element.",
2228
+ "Analyze the screenshot visually to identify the target element.",
2229
+ "",
2230
+ "## How to Read the Screenshot",
2231
+ "- Each interactive element has a red border and number label",
2232
+ "- Number labels are displayed at the top-left of each element",
2233
+ "- Each number corresponds to the number field in the annotation list below",
2234
+ "",
2235
+ "## Matching Guidelines",
2236
+ "1. Infer the type of target element (button, input field, link, etc.) from the step description",
2237
+ "2. Find the element in the screenshot that visually best matches",
2238
+ "3. Use element text, position, and appearance as clues",
2239
+ "4. Also reference the role and name from the annotation list",
2240
+ "5. Target elements not in the A11y tree (canvas, Shadow DOM, iframe, etc.) are also in scope",
2241
+ "6. Return empty string if confidence is low (below 0.5)"
2242
+ ].join("\n");
2243
+ const annotationList = annotations.map((a) => {
2244
+ const nameStr = a.name ? ` "${a.name}"` : "";
2245
+ return ` #${a.number} ref=${a.ref} ${a.role}${nameStr} @(${a.box.x},${a.box.y})`;
2246
+ }).join("\n");
2247
+ const selectorHints = pruneSelectorHints3(selector);
2248
+ const parts = [
2249
+ "## Step Description (Most Important)",
2250
+ stepDescription,
2251
+ "",
2252
+ "## Selector Hints (Reference)",
2253
+ JSON.stringify(selectorHints),
2254
+ "",
2255
+ "## Current URL",
2256
+ url,
2257
+ "",
2258
+ "## Annotation List (Corresponds to Numbers on Screenshot)",
2259
+ annotationList
2260
+ ];
2261
+ if (failureHistory.length > 0) {
2262
+ const recent = failureHistory.slice(-3);
2263
+ parts.push(
2264
+ "",
2265
+ "## Text-based Resolution Failure History (Recent 3)",
2266
+ ...recent.map((f, i) => ` ${i + 1}. ${f}`)
2267
+ );
2268
+ }
2269
+ return { system, userTextContent: parts.join("\n") };
2270
+ }
2271
+ function pruneSelectorHints3(selector) {
2272
+ const pruned = {};
2273
+ for (const [key, value] of Object.entries(selector)) {
2274
+ if (value === null || value === void 0 || value === "" || value === "unknown" || value === ":") continue;
2275
+ pruned[key] = value;
2276
+ }
2277
+ return pruned;
2278
+ }
2279
+
2280
+ // src/i18n/prompts/ja/vision.ts
2281
+ function buildVisionMessages2(annotations, selector, stepDescription, url, failureHistory) {
2282
+ const system = [
2283
+ "\u3042\u306A\u305F\u306F\u30D6\u30E9\u30A6\u30B6\u81EA\u52D5\u64CD\u4F5C\u306E\u30D3\u30B8\u30E5\u30A2\u30EB\u30BB\u30EC\u30AF\u30BF\u30EA\u30BE\u30EB\u30D0\u30FC\u3067\u3059\u3002",
2284
+ "\u30C6\u30AD\u30B9\u30C8\u30D9\u30FC\u30B9\u306E\u30A2\u30AF\u30BB\u30B7\u30D3\u30EA\u30C6\u30A3\u30C4\u30EA\u30FC\u3067\u306F\u8981\u7D20\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002",
2285
+ "\u30B9\u30AF\u30EA\u30FC\u30F3\u30B7\u30E7\u30C3\u30C8\u3092\u8996\u899A\u7684\u306B\u5206\u6790\u3057\u3066\u3001\u64CD\u4F5C\u5BFE\u8C61\u306E\u8981\u7D20\u3092\u7279\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
2286
+ "",
2287
+ "## \u30B9\u30AF\u30EA\u30FC\u30F3\u30B7\u30E7\u30C3\u30C8\u306E\u898B\u65B9",
2288
+ "- \u5404\u30A4\u30F3\u30BF\u30E9\u30AF\u30C6\u30A3\u30D6\u8981\u7D20\u306B\u8D64\u3044\u67A0\u7DDA\u3068\u756A\u53F7\u30E9\u30D9\u30EB\u304C\u8868\u793A\u3055\u308C\u3066\u3044\u307E\u3059",
2289
+ "- \u756A\u53F7\u30E9\u30D9\u30EB\u306F\u5404\u8981\u7D20\u306E\u5DE6\u4E0A\u306B\u8868\u793A\u3055\u308C\u307E\u3059",
2290
+ "- \u5404\u756A\u53F7\u306F\u4E0B\u8A18\u306E\u30A2\u30CE\u30C6\u30FC\u30B7\u30E7\u30F3\u4E00\u89A7\u306E number \u30D5\u30A3\u30FC\u30EB\u30C9\u306B\u5BFE\u5FDC\u3057\u307E\u3059",
2291
+ "",
2292
+ "## \u30DE\u30C3\u30C1\u30F3\u30B0\u306E\u6307\u91DD",
2293
+ "1. \u30B9\u30C6\u30C3\u30D7\u306E\u8AAC\u660E\u304B\u3089\u64CD\u4F5C\u5BFE\u8C61\u306E\u8981\u7D20\u306E\u7A2E\u985E\uFF08\u30DC\u30BF\u30F3\u3001\u5165\u529B\u30D5\u30A3\u30FC\u30EB\u30C9\u3001\u30EA\u30F3\u30AF\u7B49\uFF09\u3092\u63A8\u5B9A",
2294
+ "2. \u30B9\u30AF\u30EA\u30FC\u30F3\u30B7\u30E7\u30C3\u30C8\u5185\u3067\u8996\u899A\u7684\u306B\u6700\u3082\u4E00\u81F4\u3059\u308B\u8981\u7D20\u3092\u63A2\u3059",
2295
+ "3. \u8981\u7D20\u306E\u30C6\u30AD\u30B9\u30C8\u3001\u4F4D\u7F6E\u3001\u5916\u89B3\u3092\u624B\u304C\u304B\u308A\u306B\u3059\u308B",
2296
+ "4. \u30A2\u30CE\u30C6\u30FC\u30B7\u30E7\u30F3\u4E00\u89A7\u306E role \u3068 name \u3082\u53C2\u8003\u306B\u3059\u308B",
2297
+ "5. A11y \u30C4\u30EA\u30FC\u306B\u8868\u308C\u306A\u3044\u8981\u7D20\uFF08canvas\u3001Shadow DOM\u3001iframe \u5185\u7B49\uFF09\u3082\u5BFE\u8C61",
2298
+ "6. \u78BA\u4FE1\u5EA6\u304C\u4F4E\u3044\u5834\u5408\uFF080.5\u672A\u6E80\uFF09\u306F\u7A7A\u6587\u5B57\u3092\u8FD4\u3059"
2299
+ ].join("\n");
2300
+ const annotationList = annotations.map((a) => {
2301
+ const nameStr = a.name ? ` "${a.name}"` : "";
2302
+ return ` #${a.number} ref=${a.ref} ${a.role}${nameStr} @(${a.box.x},${a.box.y})`;
2303
+ }).join("\n");
2304
+ const selectorHints = pruneSelectorHints4(selector);
2305
+ const parts = [
2306
+ "## \u30B9\u30C6\u30C3\u30D7\u306E\u8AAC\u660E\uFF08\u6700\u91CD\u8981\uFF09",
2307
+ stepDescription,
2308
+ "",
2309
+ "## \u30BB\u30EC\u30AF\u30BF\u306E\u30D2\u30F3\u30C8\uFF08\u53C2\u8003\u60C5\u5831\uFF09",
2310
+ JSON.stringify(selectorHints),
2311
+ "",
2312
+ "## \u73FE\u5728\u306EURL",
2313
+ url,
2314
+ "",
2315
+ "## \u30A2\u30CE\u30C6\u30FC\u30B7\u30E7\u30F3\u4E00\u89A7\uFF08\u30B9\u30AF\u30EA\u30FC\u30F3\u30B7\u30E7\u30C3\u30C8\u4E0A\u306E\u756A\u53F7\u3068\u5BFE\u5FDC\uFF09",
2316
+ annotationList
2317
+ ];
2318
+ if (failureHistory.length > 0) {
2319
+ const recent = failureHistory.slice(-3);
2320
+ parts.push(
2321
+ "",
2322
+ "## \u30C6\u30AD\u30B9\u30C8\u30D9\u30FC\u30B9\u89E3\u6C7A\u306E\u5931\u6557\u5C65\u6B74\uFF08\u76F4\u8FD13\u4EF6\uFF09",
2323
+ ...recent.map((f, i) => ` ${i + 1}. ${f}`)
2324
+ );
2325
+ }
2326
+ return { system, userTextContent: parts.join("\n") };
2327
+ }
2328
+ function pruneSelectorHints4(selector) {
2329
+ const pruned = {};
2330
+ for (const [key, value] of Object.entries(selector)) {
2331
+ if (value === null || value === void 0 || value === "" || value === "unknown" || value === ":") continue;
2332
+ pruned[key] = value;
2333
+ }
2334
+ return pruned;
2335
+ }
2336
+
2337
+ // src/i18n/prompts/en/maintenance.ts
2338
+ function getSuggestionSystemPrompt() {
2339
+ return [
2340
+ "You are an instruction debugger for browser automation.",
2341
+ "Analyze the failed steps from the debug execution and generate fix suggestions for the instruction and context.",
2342
+ "",
2343
+ "## Fix Suggestion Guidelines",
2344
+ "",
2345
+ "For each failed step, provide suggestions from these two perspectives:",
2346
+ "",
2347
+ "### 1. instructionFix (Instruction YAML Fix)",
2348
+ "- Selector changes (when element has moved/been renamed)",
2349
+ "- Step addition/removal (when the navigation flow has changed)",
2350
+ "- Action value updates (text or URL changes)",
2351
+ "- riskLevel or requiresConfirmation adjustments",
2352
+ `Example: "Step 3: Change selector ariaLabel from 'Submit' to 'Confirm and Submit'"`,
2353
+ "",
2354
+ "### 2. contextFix (Context Markdown Fix)",
2355
+ "- Document page structure changes (new modals, menu structure changes, etc.)",
2356
+ "- Add prerequisites (login state, permissions, environment conditions, etc.)",
2357
+ "- Add selector resolution hints (dynamic ID naming patterns, etc.)",
2358
+ `Example: "Add to context: 'Settings page has changed from tab UI to accordion UI'"`,
2359
+ "",
2360
+ "If context markdown is not provided, return empty string for contextFix.",
2361
+ "",
2362
+ "### severity Criteria",
2363
+ "- error: Selector completely not found, fundamental page structure change",
2364
+ "- warning: Succeeded with retry but unstable, element position or label slightly changed",
2365
+ "",
2366
+ "### failureCategory and suggestedStrategy",
2367
+ "If a failure category is provided, describe a specific fix strategy in suggestedStrategy:",
2368
+ "- element_not_found: Update selector ariaLabel/role/text to match current page structure",
2369
+ "- element_stale: Delete selector cache and re-execute",
2370
+ "- page_structure_changed: Document page structure changes in context.md and update all selectors",
2371
+ "- navigation_timeout: Add wait step or increase step-delay",
2372
+ "- action_failed: Consider changing action type (e.g., click \u2192 hover)",
2373
+ "- unknown: Analyze error details and propose specific remediation"
2374
+ ].join("\n");
2375
+ }
2376
+ function buildSuggestionUserPrompt(failedInfo, stepsInfo, instruction, contextMarkdown) {
2377
+ const parts = [
2378
+ "## Instruction Information",
2379
+ `Title: ${instruction.title}`,
2380
+ `Goal: ${instruction.metadata.goal}`,
2381
+ `Start URL: ${instruction.metadata.startUrl}`,
2382
+ "",
2383
+ "## Instruction Steps",
2384
+ stepsInfo.join("\n\n"),
2385
+ "",
2386
+ "## Failed Step Details",
2387
+ failedInfo.join("\n\n")
2388
+ ];
2389
+ if (contextMarkdown) {
2390
+ parts.push(
2391
+ "",
2392
+ "## Current Context (markdown)",
2393
+ contextMarkdown
2394
+ );
2395
+ } else {
2396
+ parts.push(
2397
+ "",
2398
+ "## Context",
2399
+ "Context markdown is not provided. Return empty string for contextFix."
2400
+ );
2401
+ }
2402
+ return parts.join("\n");
2403
+ }
2404
+
2405
+ // src/i18n/prompts/ja/maintenance.ts
2406
+ function getSuggestionSystemPrompt2() {
2407
+ return [
2408
+ "\u3042\u306A\u305F\u306F\u30D6\u30E9\u30A6\u30B6\u81EA\u52D5\u64CD\u4F5C\u306E\u6307\u793A\u66F8\u30C7\u30D0\u30C3\u30AC\u30FC\u3067\u3059\u3002",
2409
+ "\u30C7\u30D0\u30C3\u30B0\u5B9F\u884C\u3067\u5931\u6557\u3057\u305F\u30B9\u30C6\u30C3\u30D7\u3092\u5206\u6790\u3057\u3001\u6307\u793A\u66F8\u3068\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u306E\u4FEE\u6B63\u63D0\u6848\u3092\u751F\u6210\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
2410
+ "",
2411
+ "## \u4FEE\u6B63\u63D0\u6848\u306E\u6307\u91DD",
2412
+ "",
2413
+ "\u5404\u5931\u6557\u30B9\u30C6\u30C3\u30D7\u306B\u3064\u3044\u3066\u3001\u4EE5\u4E0B\u306E2\u3064\u306E\u89B3\u70B9\u3067\u4FEE\u6B63\u63D0\u6848\u3092\u51FA\u3057\u3066\u304F\u3060\u3055\u3044:",
2414
+ "",
2415
+ "### 1. instructionFix\uFF08\u6307\u793A\u66F8 YAML \u306E\u4FEE\u6B63\uFF09",
2416
+ "- \u30BB\u30EC\u30AF\u30BF\u306E\u5909\u66F4\uFF08\u8981\u7D20\u304C\u79FB\u52D5/\u30EA\u30CD\u30FC\u30E0\u3055\u308C\u305F\u5834\u5408\uFF09",
2417
+ "- \u30B9\u30C6\u30C3\u30D7\u306E\u8FFD\u52A0/\u524A\u9664\uFF08\u5C0E\u7DDA\u304C\u5909\u308F\u3063\u305F\u5834\u5408\uFF09",
2418
+ "- \u30A2\u30AF\u30B7\u30E7\u30F3\u5024\u306E\u66F4\u65B0\uFF08\u30C6\u30AD\u30B9\u30C8\u3084URL\u306E\u5909\u66F4\uFF09",
2419
+ "- riskLevel \u3084 requiresConfirmation \u306E\u8ABF\u6574",
2420
+ "\u4F8B: \u300CStep 3: selector \u306E ariaLabel \u3092 '\u9001\u4FE1' \u2192 '\u78BA\u8A8D\u3057\u3066\u9001\u4FE1' \u306B\u5909\u66F4\u300D",
2421
+ "",
2422
+ "### 2. contextFix\uFF08context markdown \u306E\u4FEE\u6B63\uFF09",
2423
+ "- \u30DA\u30FC\u30B8\u69CB\u9020\u5909\u66F4\u306E\u8A18\u8F09\uFF08\u65B0\u3057\u3044\u30E2\u30FC\u30C0\u30EB\u3001\u30E1\u30CB\u30E5\u30FC\u69CB\u9020\u306E\u5909\u66F4\u306A\u3069\uFF09",
2424
+ "- \u524D\u63D0\u6761\u4EF6\u306E\u8FFD\u52A0\uFF08\u30ED\u30B0\u30A4\u30F3\u72B6\u614B\u3001\u6A29\u9650\u3001\u74B0\u5883\u6761\u4EF6\u306A\u3069\uFF09",
2425
+ "- \u30BB\u30EC\u30AF\u30BF\u89E3\u6C7A\u306E\u30D2\u30F3\u30C8\u8FFD\u52A0\uFF08\u52D5\u7684ID\u306E\u547D\u540D\u898F\u5247\u306A\u3069\uFF09",
2426
+ "\u4F8B: \u300Ccontext \u306B'\u8A2D\u5B9A\u753B\u9762\u304C\u30BF\u30D6UI\u304B\u3089\u30A2\u30B3\u30FC\u30C7\u30A3\u30AA\u30F3UI\u306B\u5909\u66F4\u3055\u308C\u305F'\u65E8\u3092\u8FFD\u8A18\u300D",
2427
+ "",
2428
+ "context markdown \u304C\u672A\u63D0\u4F9B\u306E\u5834\u5408\u3001contextFix \u306F\u7A7A\u6587\u5B57\u3067\u8FD4\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
2429
+ "",
2430
+ "### severity \u306E\u5224\u5B9A\u57FA\u6E96",
2431
+ "- error: \u30BB\u30EC\u30AF\u30BF\u304C\u5B8C\u5168\u306B\u898B\u3064\u304B\u3089\u306A\u3044\u3001\u30DA\u30FC\u30B8\u69CB\u9020\u304C\u6839\u672C\u7684\u306B\u5909\u308F\u3063\u3066\u3044\u308B",
2432
+ "- warning: \u30EA\u30C8\u30E9\u30A4\u3067\u6210\u529F\u3057\u305F\u304C\u4E0D\u5B89\u5B9A\u3001\u8981\u7D20\u306E\u4F4D\u7F6E\u3084\u30E9\u30D9\u30EB\u304C\u5FAE\u5999\u306B\u5909\u308F\u3063\u3066\u3044\u308B",
2433
+ "",
2434
+ "### failureCategory \u3068 suggestedStrategy",
2435
+ "\u30A8\u30E9\u30FC\u5206\u985E\u30AB\u30C6\u30B4\u30EA\u304C\u63D0\u4F9B\u3055\u308C\u3066\u3044\u308B\u5834\u5408\u3001\u305D\u308C\u306B\u57FA\u3065\u3044\u3066\u5177\u4F53\u7684\u306A\u4FEE\u6B63\u6226\u7565\u3092 suggestedStrategy \u306B\u8A18\u8F09\u3057\u3066\u304F\u3060\u3055\u3044:",
2436
+ "- element_not_found: \u30BB\u30EC\u30AF\u30BF\u306E ariaLabel/role/text \u3092\u73FE\u5728\u306E\u30DA\u30FC\u30B8\u69CB\u9020\u306B\u5408\u308F\u305B\u3066\u66F4\u65B0",
2437
+ "- element_stale: \u30BB\u30EC\u30AF\u30BF\u30AD\u30E3\u30C3\u30B7\u30E5\u306E\u524A\u9664\u3068\u518D\u5B9F\u884C",
2438
+ "- page_structure_changed: context.md \u306B\u30DA\u30FC\u30B8\u69CB\u9020\u5909\u66F4\u3092\u8A18\u9332\u3057\u3001\u30BB\u30EC\u30AF\u30BF\u3092\u5168\u9762\u66F4\u65B0",
2439
+ "- navigation_timeout: wait \u30B9\u30C6\u30C3\u30D7\u306E\u8FFD\u52A0\u307E\u305F\u306F step-delay \u306E\u5897\u52A0",
2440
+ "- action_failed: \u30A2\u30AF\u30B7\u30E7\u30F3\u7A2E\u5225\u306E\u5909\u66F4\uFF08\u4F8B: click \u2192 hover\uFF09\u3092\u691C\u8A0E",
2441
+ "- unknown: \u30A8\u30E9\u30FC\u5185\u5BB9\u3092\u8A73\u7D30\u306B\u5206\u6790\u3057\u3001\u5177\u4F53\u7684\u306A\u5BFE\u51E6\u3092\u63D0\u6848"
2442
+ ].join("\n");
2443
+ }
2444
+ function buildSuggestionUserPrompt2(failedInfo, stepsInfo, instruction, contextMarkdown) {
2445
+ const parts = [
2446
+ "## \u6307\u793A\u66F8\u60C5\u5831",
2447
+ `\u30BF\u30A4\u30C8\u30EB: ${instruction.title}`,
2448
+ `\u76EE\u7684: ${instruction.metadata.goal}`,
2449
+ `\u958B\u59CBURL: ${instruction.metadata.startUrl}`,
2450
+ "",
2451
+ "## \u6307\u793A\u66F8\u306E\u30B9\u30C6\u30C3\u30D7\u4E00\u89A7",
2452
+ stepsInfo.join("\n\n"),
2453
+ "",
2454
+ "## \u5931\u6557\u30B9\u30C6\u30C3\u30D7\u306E\u8A73\u7D30",
2455
+ failedInfo.join("\n\n")
2456
+ ];
2457
+ if (contextMarkdown) {
2458
+ parts.push(
2459
+ "",
2460
+ "## \u73FE\u5728\u306E\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\uFF08markdown\uFF09",
2461
+ contextMarkdown
2462
+ );
2463
+ } else {
2464
+ parts.push(
2465
+ "",
2466
+ "## \u30B3\u30F3\u30C6\u30AD\u30B9\u30C8",
2467
+ "context markdown \u306F\u672A\u63D0\u4F9B\u3067\u3059\u3002contextFix \u306F\u7A7A\u6587\u5B57\u3067\u8FD4\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
2468
+ );
2469
+ }
2470
+ return parts.join("\n");
2471
+ }
2472
+
2473
+ // src/i18n/prompts/index.ts
2474
+ function getAgentInstructions3(goal, contextMarkdown, secrets) {
2475
+ return getLocale() === "ja" ? getAgentInstructions2(goal, contextMarkdown, secrets) : getAgentInstructions(goal, contextMarkdown, secrets);
2476
+ }
2477
+ function getInitialUserMessage3() {
2478
+ return getLocale() === "ja" ? getInitialUserMessage2() : getInitialUserMessage();
2479
+ }
2480
+ function getReviewSystemPrompt3() {
2481
+ return getLocale() === "ja" ? getReviewSystemPrompt2() : getReviewSystemPrompt();
2482
+ }
2483
+ function createReviewUserPrompt3(goal, recordedSteps, goalAchieved, interventions) {
2484
+ return getLocale() === "ja" ? createReviewUserPrompt2(goal, recordedSteps, goalAchieved, interventions) : createReviewUserPrompt(goal, recordedSteps, goalAchieved, interventions);
2485
+ }
2486
+ function buildSelectorMessages3(snapshot, selector, stepDescription, url, contextMarkdown, retryContext, workingMemorySummary) {
2487
+ return getLocale() === "ja" ? buildSelectorMessages2(snapshot, selector, stepDescription, url, contextMarkdown, retryContext, workingMemorySummary) : buildSelectorMessages(snapshot, selector, stepDescription, url, contextMarkdown, retryContext, workingMemorySummary);
2488
+ }
2489
+ function buildFallbackMessages3(snapshot, step, failureHistory, url, contextMarkdown) {
2490
+ return getLocale() === "ja" ? buildFallbackMessages2(snapshot, step, failureHistory, url, contextMarkdown) : buildFallbackMessages(snapshot, step, failureHistory, url, contextMarkdown);
2491
+ }
2492
+ function buildVisionMessages3(annotations, selector, stepDescription, url, failureHistory) {
2493
+ return getLocale() === "ja" ? buildVisionMessages2(annotations, selector, stepDescription, url, failureHistory) : buildVisionMessages(annotations, selector, stepDescription, url, failureHistory);
2494
+ }
2495
+ function getSuggestionSystemPrompt3() {
2496
+ return getLocale() === "ja" ? getSuggestionSystemPrompt2() : getSuggestionSystemPrompt();
2497
+ }
2498
+ function buildSuggestionUserPrompt3(failedInfo, stepsInfo, instruction, contextMarkdown) {
2499
+ return getLocale() === "ja" ? buildSuggestionUserPrompt2(failedInfo, stepsInfo, instruction, contextMarkdown) : buildSuggestionUserPrompt(failedInfo, stepsInfo, instruction, contextMarkdown);
2500
+ }
2501
+
2502
+ // src/harness/report-sections.ts
2503
+ import { readFile as readFile4 } from "fs/promises";
2504
+ async function readDebugLog(filePath) {
2505
+ try {
2506
+ const content = await readFile4(filePath, "utf-8");
2507
+ return content.trim().split("\n").filter((line) => line.trim()).map((line) => {
2508
+ try {
2509
+ return JSON.parse(line);
2510
+ } catch {
2511
+ return {};
2512
+ }
2513
+ });
2514
+ } catch {
2515
+ return [];
2516
+ }
2517
+ }
2518
+ function formatAiMetricsSection(aiMetrics) {
2519
+ const lines = [];
2520
+ if (aiMetrics.totalCalls <= 0) return lines;
2521
+ const m = aiMetrics;
2522
+ const totalRealInput = computeTotalRealInput(m);
2523
+ const cacheRate = (computeCacheRate(m) * 100).toFixed(1);
2524
+ lines.push("### AI Metrics");
2525
+ lines.push("");
2526
+ lines.push("| \u30E1\u30C8\u30EA\u30AF\u30B9 | \u5024 |");
2527
+ lines.push("|-----------|-----|");
2528
+ lines.push(`| Total calls | ${m.totalCalls} |`);
2529
+ lines.push(`| Total input tokens | ${totalRealInput.toLocaleString()} |`);
2530
+ lines.push(`| Output tokens | ${m.totalOutputTokens.toLocaleString()} |`);
2531
+ lines.push(`| Cache read tokens | ${m.totalCachedInputTokens.toLocaleString()} |`);
2532
+ lines.push(`| Cache write tokens | ${m.totalCacheCreationTokens.toLocaleString()} |`);
2533
+ lines.push(`| Cache hit rate | ${cacheRate}% |`);
2534
+ lines.push(`| Estimated cost | $${m.estimatedCostUsd.toFixed(4)} |`);
2535
+ lines.push(`| Total AI duration | ${(m.totalDurationMs / 1e3).toFixed(1)}s |`);
2536
+ lines.push("");
2537
+ const cacheHitRate = computeCacheRate(m);
2538
+ if (cacheHitRate < 0.3 && m.totalCalls >= 3) {
2539
+ lines.push(`> **:warning: \u30AD\u30E3\u30C3\u30B7\u30E5\u30D2\u30C3\u30C8\u7387\u304C\u4F4E\u3044 (${cacheRate}%)**`);
2540
+ lines.push("> AI \u30D7\u30ED\u30F3\u30D7\u30C8\u30AD\u30E3\u30C3\u30B7\u30E5\u306E\u6D3B\u7528\u304C\u5C11\u306A\u304F\u3001\u30B3\u30B9\u30C8\u52B9\u7387\u304C\u60AA\u3044\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002");
2541
+ lines.push("> \u8003\u3048\u3089\u308C\u308B\u539F\u56E0: \u30D7\u30ED\u30F3\u30D7\u30C8\u306E\u5148\u982D\u90E8\u5206\u304C\u6BCE\u56DE\u7570\u306A\u308B\u3001\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u304C\u5927\u304D\u304F\u5909\u52D5\u3059\u308B\u3001\u540C\u4E00\u30BB\u30EC\u30AF\u30BF\u306E\u518D\u89E3\u6C7A\u304C\u767A\u751F\u3057\u3066\u3044\u306A\u3044\u3002");
2542
+ lines.push("");
2543
+ }
2544
+ if (Object.keys(m.byPurpose).length > 0) {
2545
+ lines.push("**By purpose:**");
2546
+ lines.push("");
2547
+ lines.push("| Purpose | Calls | Input | Output | Cache Read | Cache Write | Duration |");
2548
+ lines.push("|---------|-------|-------|--------|------------|-------------|----------|");
2549
+ for (const [purpose, b] of Object.entries(m.byPurpose)) {
2550
+ const purposeRealInput = computeTotalRealInput(b);
2551
+ lines.push(
2552
+ `| ${purpose} | ${b.calls} | ${purposeRealInput.toLocaleString()} | ${b.outputTokens.toLocaleString()} | ${b.cachedInputTokens.toLocaleString()} | ${b.cacheCreationTokens.toLocaleString()} | ${(b.durationMs / 1e3).toFixed(1)}s |`
2553
+ );
2554
+ }
2555
+ lines.push("");
2556
+ }
2557
+ if (m.byModel && Object.keys(m.byModel).length > 0) {
2558
+ lines.push("**By model:**");
2559
+ lines.push("");
2560
+ lines.push("| Model | Calls | Input | Output | Cache Read | Cache Write | Cost | Duration |");
2561
+ lines.push("|-------|-------|-------|--------|------------|-------------|------|----------|");
2562
+ for (const [modelId, b] of Object.entries(m.byModel)) {
2563
+ const modelRealInput = computeTotalRealInput(b);
2564
+ lines.push(
2565
+ `| ${modelId} | ${b.calls} | ${modelRealInput.toLocaleString()} | ${b.outputTokens.toLocaleString()} | ${b.cachedInputTokens.toLocaleString()} | ${b.cacheCreationTokens.toLocaleString()} | $${b.estimatedCostUsd.toFixed(4)} | ${(b.durationMs / 1e3).toFixed(1)}s |`
2566
+ );
2567
+ }
2568
+ lines.push("");
2569
+ }
2570
+ return lines;
2571
+ }
2572
+ function formatRuntimeEnvironment() {
2573
+ const lines = [];
2574
+ lines.push("### Runtime Environment");
2575
+ lines.push("");
2576
+ lines.push(`- **AI Model**: ${getModelId()} (${getModelProvider()})`);
2577
+ lines.push(`- **Node.js**: ${process.version}`);
2578
+ lines.push(`- **Platform**: ${process.platform} ${process.arch}`);
2579
+ lines.push("");
2580
+ return lines;
2581
+ }
2582
+ function formatSelectorResolutionStats(debugEntries, allSteps) {
2583
+ const lines = [];
2584
+ lines.push("### Selector Resolution Statistics");
2585
+ lines.push("");
2586
+ const stepsWithSelector = allSteps.filter(
2587
+ (s) => s.diagnostics?.stepAction?.selector || s.diagnostics?.deterministicResolveResult
2588
+ );
2589
+ const deterministicHits = debugEntries.filter((e) => e.event === "deterministic_resolve").length;
2590
+ const deterministicMisses = debugEntries.filter((e) => e.event === "deterministic_resolve_miss").length;
2591
+ const aiResolves = debugEntries.filter((e) => e.event === "selector_resolve").length;
2592
+ const cacheHits = debugEntries.filter((e) => e.event === "selector_cache_hit").length;
2593
+ const cacheStales = debugEntries.filter((e) => e.event === "selector_cache_stale").length;
2594
+ const retryEvents = debugEntries.filter((e) => e.event === "selector_retry").length;
2595
+ const recoveryAttempts = debugEntries.filter((e) => e.event === "recovery_attempt").length;
2596
+ const agentFallbackStarts = debugEntries.filter((e) => e.event === "agent_fallback_start").length;
2597
+ const agentFallbackResults = debugEntries.filter((e) => e.event === "agent_fallback_result");
2598
+ const agentFallbackSuccesses = agentFallbackResults.filter(
2599
+ (e) => e.data && e.data.success === true
2600
+ ).length;
2601
+ const totalResolveAttempts = deterministicHits + deterministicMisses + cacheHits;
2602
+ if (totalResolveAttempts > 0 || stepsWithSelector.length > 0) {
2603
+ lines.push("| \u30E1\u30C8\u30EA\u30AF\u30B9 | \u4EF6\u6570 |");
2604
+ lines.push("|-----------|------|");
2605
+ if (totalResolveAttempts > 0) {
2606
+ lines.push(`| \u6C7A\u5B9A\u8AD6\u7684\u89E3\u6C7A \u6210\u529F | ${deterministicHits} |`);
2607
+ lines.push(`| \u6C7A\u5B9A\u8AD6\u7684\u89E3\u6C7A \u5931\u6557\u2192AI\u59D4\u8B72 | ${deterministicMisses} |`);
2608
+ lines.push(`| AI \u30BB\u30EC\u30AF\u30BF\u89E3\u6C7A | ${aiResolves} |`);
2609
+ if (cacheHits > 0 || cacheStales > 0) {
2610
+ lines.push(`| \u30AD\u30E3\u30C3\u30B7\u30E5\u30D2\u30C3\u30C8 | ${cacheHits} |`);
2611
+ lines.push(`| \u30AD\u30E3\u30C3\u30B7\u30E5 stale\uFF08\u81EA\u52D5\u7121\u52B9\u5316\uFF09 | ${cacheStales} |`);
2612
+ }
2613
+ lines.push(`| \u30BB\u30EC\u30AF\u30BF\u30EA\u30C8\u30E9\u30A4 | ${retryEvents} |`);
2614
+ if (recoveryAttempts > 0) {
2615
+ lines.push(`| \u30B9\u30AF\u30ED\u30FC\u30EB\u56DE\u5FA9 | ${recoveryAttempts} |`);
2616
+ }
2617
+ const visionFbStarts = debugEntries.filter((e) => e.event === "vision_fallback_start").length;
2618
+ if (visionFbStarts > 0) {
2619
+ const visionFbSuccesses = debugEntries.filter(
2620
+ (e) => e.event === "vision_fallback_result" && e.data && e.data.ref
2621
+ ).length;
2622
+ lines.push(`| Vision Fallback \u767A\u52D5 | ${visionFbStarts} |`);
2623
+ lines.push(`| Vision Fallback \u6210\u529F | ${visionFbSuccesses} |`);
2624
+ }
2625
+ if (agentFallbackStarts > 0) {
2626
+ lines.push(`| Agent Fallback \u767A\u52D5 | ${agentFallbackStarts} |`);
2627
+ lines.push(`| Agent Fallback \u6210\u529F | ${agentFallbackSuccesses} |`);
2628
+ }
2629
+ } else {
2630
+ const totalRetries = allSteps.reduce((sum, s) => sum + (s.retryCount ?? 0), 0);
2631
+ const stepsWithRetry = allSteps.filter((s) => s.retryCount && s.retryCount > 0);
2632
+ lines.push(`| \u30BB\u30EC\u30AF\u30BF\u89E3\u6C7A\u5BFE\u8C61\u30B9\u30C6\u30C3\u30D7 | ${stepsWithSelector.length} |`);
2633
+ lines.push(`| \u30EA\u30C8\u30E9\u30A4\u767A\u751F\u30B9\u30C6\u30C3\u30D7 | ${stepsWithRetry.length} |`);
2634
+ lines.push(`| \u5408\u8A08\u30EA\u30C8\u30E9\u30A4\u56DE\u6570 | ${totalRetries} |`);
2635
+ }
2636
+ lines.push("");
2637
+ if (totalResolveAttempts > 0) {
2638
+ const deterministicRate = deterministicHits + deterministicMisses > 0 ? Math.round(deterministicHits / (deterministicHits + deterministicMisses) * 100) : 0;
2639
+ lines.push(`**\u6C7A\u5B9A\u8AD6\u7684\u89E3\u6C7A\u7387**: ${deterministicRate}% \u2014 `);
2640
+ if (deterministicRate < 50) {
2641
+ lines.push("\u4F4E\u3044\u3002`deterministic-resolver.ts` \u306E\u30D1\u30BF\u30FC\u30F3\u30DE\u30C3\u30C1\u30F3\u30B0\u7CBE\u5EA6\u6539\u5584\u306E\u4F59\u5730\u3042\u308A\u3002");
2642
+ } else if (deterministicRate < 80) {
2643
+ lines.push("\u4E2D\u7A0B\u5EA6\u3002\u983B\u51FA\u306E\u30DE\u30C3\u30C1\u5931\u6557\u30D1\u30BF\u30FC\u30F3\u3092\u5206\u6790\u3057\u8FFD\u52A0\u3059\u308B\u3068 AI \u547C\u3073\u51FA\u3057\u3092\u524A\u6E1B\u3067\u304D\u308B\u3002");
2644
+ } else {
2645
+ lines.push("\u9AD8\u3044\u3002\u6C7A\u5B9A\u8AD6\u7684\u89E3\u6C7A\u304C\u6709\u52B9\u306B\u6A5F\u80FD\u3057\u3066\u3044\u308B\u3002");
2646
+ }
2647
+ lines.push("");
2648
+ }
2649
+ }
2650
+ return lines;
2651
+ }
2652
+ function formatRetryDistribution(allSteps) {
2653
+ const lines = [];
2654
+ lines.push("### Retry Distribution");
2655
+ lines.push("");
2656
+ const retryDistribution = /* @__PURE__ */ new Map();
2657
+ for (const step of allSteps) {
2658
+ const count = step.retryCount ?? 0;
2659
+ retryDistribution.set(count, (retryDistribution.get(count) ?? 0) + 1);
2660
+ }
2661
+ if (retryDistribution.size > 0) {
2662
+ lines.push("| \u30EA\u30C8\u30E9\u30A4\u56DE\u6570 | \u30B9\u30C6\u30C3\u30D7\u6570 |");
2663
+ lines.push("|-------------|-----------|");
2664
+ for (const [count, steps] of [...retryDistribution.entries()].sort((a, b) => a[0] - b[0])) {
2665
+ lines.push(`| ${count} | ${steps} |`);
2666
+ }
2667
+ lines.push("");
2668
+ }
2669
+ return lines;
2670
+ }
2671
+ function getSystemImprovementHint(category) {
2672
+ switch (category) {
2673
+ case "element_not_found":
2674
+ return "\u6C7A\u5B9A\u8AD6\u7684\u89E3\u6C7A\u306E\u30D1\u30BF\u30FC\u30F3\u8FFD\u52A0\u3001\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u30D5\u30A3\u30EB\u30BF\u306E\u898B\u76F4\u3057";
2675
+ case "element_stale":
2676
+ return "\u30AD\u30E3\u30C3\u30B7\u30E5\u7121\u52B9\u5316\u30ED\u30B8\u30C3\u30AF\u306E\u6539\u5584\u3001DOM\u5B89\u5B9A\u5316\u5F85\u6A5F\u306E\u8ABF\u6574";
2677
+ case "page_structure_changed":
2678
+ return "\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u5DEE\u5206\u691C\u77E5\u306E\u611F\u5EA6\u8ABF\u6574\u3001\u52D5\u7684\u30B3\u30F3\u30C6\u30F3\u30C4\u5BFE\u5FDC";
2679
+ case "navigation_timeout":
2680
+ return "\u30CA\u30D3\u30B2\u30FC\u30B7\u30E7\u30F3\u5F85\u6A5F\u306E\u6BB5\u968E\u7684\u30BF\u30A4\u30E0\u30A2\u30A6\u30C8\u8ABF\u6574\u3001SPA\u691C\u77E5\u306E\u6539\u5584";
2681
+ case "action_failed":
2682
+ return "\u30A2\u30AF\u30B7\u30E7\u30F3\u30D0\u30EA\u30C7\u30FC\u30B7\u30E7\u30F3\u306E\u7DB2\u7F85\u6027\u5411\u4E0A\u3001role\u4E92\u63DB\u30DE\u30C3\u30D4\u30F3\u30B0\u306E\u62E1\u5145";
2683
+ case "unknown":
2684
+ return "\u30A8\u30E9\u30FC\u5206\u985E\u30ED\u30B8\u30C3\u30AF\u3078\u306E\u65B0\u30AB\u30C6\u30B4\u30EA\u8FFD\u52A0\u3092\u691C\u8A0E";
2685
+ default:
2686
+ return "\u65B0\u3057\u3044\u30AB\u30C6\u30B4\u30EA \u2014 error-classifier.ts \u306B\u8FFD\u52A0\u3092\u691C\u8A0E";
2687
+ }
2688
+ }
2689
+ function formatFailureCategoryDistribution(failedSteps) {
2690
+ const lines = [];
2691
+ if (failedSteps.length === 0) return lines;
2692
+ lines.push("### Failure Category Distribution");
2693
+ lines.push("");
2694
+ const categoryCount = /* @__PURE__ */ new Map();
2695
+ for (const step of failedSteps) {
2696
+ const cat = step.failureCategory ?? "unclassified";
2697
+ categoryCount.set(cat, (categoryCount.get(cat) ?? 0) + 1);
2698
+ }
2699
+ lines.push("| \u30AB\u30C6\u30B4\u30EA | \u4EF6\u6570 | \u6539\u5584\u30D2\u30F3\u30C8 |");
2700
+ lines.push("|---------|------|-----------|");
2701
+ for (const [cat, count] of [...categoryCount.entries()].sort((a, b) => b[1] - a[1])) {
2702
+ const hint = getSystemImprovementHint(cat);
2703
+ lines.push(`| \`${cat}\` | ${count} | ${hint} |`);
2704
+ }
2705
+ lines.push("");
2706
+ return lines;
2707
+ }
2708
+ function analyzeRecoveryEffectiveness(entries, allSteps) {
2709
+ const results = [];
2710
+ const retrySteps = allSteps.filter((s) => s.retryCount && s.retryCount > 0);
2711
+ if (retrySteps.length > 0) {
2712
+ const retrySuccesses = retrySteps.filter((s) => s.status === "success").length;
2713
+ results.push({ name: "\u30B9\u30DE\u30FC\u30C8\u30EA\u30C8\u30E9\u30A4", total: retrySteps.length, success: retrySuccesses });
2714
+ }
2715
+ const scrollRecoveries = entries.filter((e) => e.event === "recovery_attempt");
2716
+ if (scrollRecoveries.length > 0) {
2717
+ const scrollStepOrdinals = new Set(scrollRecoveries.map((e) => e.step).filter((s) => s !== void 0));
2718
+ const scrollSuccesses = allSteps.filter(
2719
+ (s) => scrollStepOrdinals.has(s.ordinal) && s.status === "success"
2720
+ ).length;
2721
+ results.push({ name: "\u30B9\u30AF\u30ED\u30FC\u30EB\u56DE\u5FA9", total: scrollRecoveries.length, success: scrollSuccesses });
2722
+ }
2723
+ const visionFallbackStarts = entries.filter((e) => e.event === "vision_fallback_start").length;
2724
+ if (visionFallbackStarts > 0) {
2725
+ const visionSuccesses = entries.filter(
2726
+ (e) => e.event === "vision_fallback_result" && e.data && e.data.ref
2727
+ ).length;
2728
+ results.push({ name: "Vision Fallback", total: visionFallbackStarts, success: visionSuccesses });
2729
+ }
2730
+ const fbStarts = entries.filter((e) => e.event === "agent_fallback_start").length;
2731
+ if (fbStarts > 0) {
2732
+ const fbSuccesses = entries.filter(
2733
+ (e) => e.event === "agent_fallback_result" && e.data && e.data.success === true
2734
+ ).length;
2735
+ results.push({ name: "Agent Fallback", total: fbStarts, success: fbSuccesses });
2736
+ }
2737
+ const deterministicMisses = entries.filter((e) => e.event === "deterministic_resolve_miss");
2738
+ if (deterministicMisses.length > 0) {
2739
+ const missStepOrdinals = new Set(deterministicMisses.map((e) => e.step).filter((s) => s !== void 0));
2740
+ const aiRecoveries = allSteps.filter(
2741
+ (s) => missStepOrdinals.has(s.ordinal) && s.status === "success"
2742
+ ).length;
2743
+ results.push({ name: "AI \u30D5\u30A9\u30FC\u30EB\u30D0\u30C3\u30AF\uFF08\u6C7A\u5B9A\u8AD6\u7684\u5931\u6557\u5F8C\uFF09", total: deterministicMisses.length, success: aiRecoveries });
2744
+ }
2745
+ return results;
2746
+ }
2747
+ function formatRecoveryEffectiveness(entries, allSteps) {
2748
+ const lines = [];
2749
+ if (entries.length === 0) return lines;
2750
+ const recoveryResults = analyzeRecoveryEffectiveness(entries, allSteps);
2751
+ if (recoveryResults.length === 0) return lines;
2752
+ lines.push("### Recovery Mechanism Effectiveness");
2753
+ lines.push("");
2754
+ lines.push("| \u56DE\u5FA9\u6A5F\u69CB | \u767A\u52D5\u56DE\u6570 | \u6210\u529F | \u5931\u6557 | \u6210\u529F\u7387 |");
2755
+ lines.push("|---------|---------|------|------|--------|");
2756
+ for (const r of recoveryResults) {
2757
+ const rate = r.total > 0 ? Math.round(r.success / r.total * 100) : 0;
2758
+ lines.push(`| ${r.name} | ${r.total} | ${r.success} | ${r.total - r.success} | ${rate}% |`);
2759
+ }
2760
+ lines.push("");
2761
+ return lines;
2762
+ }
2763
+ function formatPerformanceBottlenecks(allSteps) {
2764
+ const lines = [];
2765
+ const slowSteps = [...allSteps].filter((s) => s.durationMs > 0).sort((a, b) => b.durationMs - a.durationMs).slice(0, 5);
2766
+ if (slowSteps.length > 0 && slowSteps[0].durationMs > 3e3) {
2767
+ lines.push("### Performance Bottlenecks");
2768
+ lines.push("");
2769
+ lines.push("| Step | \u6240\u8981\u6642\u9593 | \u30EA\u30C8\u30E9\u30A4 | \u72B6\u614B | \u8AAC\u660E |");
2770
+ lines.push("|------|---------|---------|------|------|");
2771
+ for (const step of slowSteps) {
2772
+ if (step.durationMs <= 3e3) break;
2773
+ const status = step.status === "failed" ? ":x:" : ":white_check_mark:";
2774
+ lines.push(
2775
+ `| #${step.ordinal} | ${(step.durationMs / 1e3).toFixed(1)}s | ${step.retryCount ?? 0} | ${status} | ${step.description} |`
2776
+ );
2777
+ }
2778
+ lines.push("");
2779
+ }
2780
+ return lines;
2781
+ }
2782
+ function analyzeAiSelectorQuality(entries) {
2783
+ const issues = [];
2784
+ const aiResponses = entries.filter((e) => e.event === "ai_selector_response");
2785
+ const stepAiCounts = /* @__PURE__ */ new Map();
2786
+ for (const e of aiResponses) {
2787
+ if (e.step !== void 0) {
2788
+ stepAiCounts.set(e.step, (stepAiCounts.get(e.step) ?? 0) + 1);
2789
+ }
2790
+ }
2791
+ for (const [step, count] of stepAiCounts) {
2792
+ if (count > 1) {
2793
+ issues.push({
2794
+ step,
2795
+ description: `AI \u304C ${count} \u56DE\u547C\u3073\u51FA\u3055\u308C\u305F\uFF08\u524D\u56DE\u5FDC\u7B54\u304C\u4E0D\u6B63\u78BA\uFF09\u3002\u30D7\u30ED\u30F3\u30D7\u30C8\u306E\u6539\u5584\u4F59\u5730\u3042\u308A`
2796
+ });
2797
+ }
2798
+ }
2799
+ const unchangedRetries = entries.filter(
2800
+ (e) => e.event === "snapshot_unchanged" && e.data && !e.data.skippingAI
2801
+ );
2802
+ if (unchangedRetries.length > 0) {
2803
+ for (const e of unchangedRetries) {
2804
+ if (e.step !== void 0) {
2805
+ issues.push({
2806
+ step: e.step,
2807
+ description: "\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u672A\u5909\u5316\u306A\u306E\u306B AI \u304C\u518D\u547C\u3073\u51FA\u3057\u3055\u308C\u305F\u3002\u30B9\u30AD\u30C3\u30D7\u30ED\u30B8\u30C3\u30AF\u306E\u78BA\u8A8D\u304C\u5FC5\u8981"
2808
+ });
2809
+ }
2810
+ }
2811
+ }
2812
+ const emptyRefResponses = entries.filter(
2813
+ (e) => e.event === "ai_selector_response" && e.data && typeof e.data.parsedResult === "object" && e.data.parsedResult !== null && e.data.parsedResult.ref === ""
2814
+ );
2815
+ if (emptyRefResponses.length > 0) {
2816
+ const steps = [...new Set(emptyRefResponses.map((e) => e.step).filter((s) => s !== void 0))];
2817
+ for (const step of steps) {
2818
+ issues.push({
2819
+ step,
2820
+ description: "AI \u304C\u7A7A ref \u3092\u8FD4\u3057\u305F\uFF08\u8981\u7D20\u4E0D\u5728\u5224\u5B9A\uFF09\u3002\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u30D5\u30A3\u30EB\u30BF\u304C\u8981\u7D20\u3092\u9664\u53BB\u3057\u3066\u3044\u306A\u3044\u304B\u78BA\u8A8D"
2821
+ });
2822
+ }
2823
+ }
2824
+ return issues;
2825
+ }
2826
+ function formatAiSelectorQualityIssues(entries) {
2827
+ const lines = [];
2828
+ const aiQualityIssues = analyzeAiSelectorQuality(entries);
2829
+ if (aiQualityIssues.length === 0) return lines;
2830
+ lines.push("### AI Selector Resolution Issues");
2831
+ lines.push("");
2832
+ lines.push("> AI \u30BB\u30EC\u30AF\u30BF\u89E3\u6C7A\u3067\u554F\u984C\u304C\u691C\u51FA\u3055\u308C\u305F\u30B1\u30FC\u30B9\u3002\u30D7\u30ED\u30F3\u30D7\u30C8\u6539\u5584\u306E\u6750\u6599\u3002");
2833
+ lines.push("");
2834
+ for (const issue of aiQualityIssues.slice(0, 5)) {
2835
+ lines.push(`- **Step #${issue.step}**: ${issue.description}`);
2836
+ }
2837
+ lines.push("");
2838
+ return lines;
2839
+ }
2840
+ var DEFAULT_RECENT_EVENT_TYPES = [
2841
+ "action",
2842
+ "snapshot",
2843
+ "selector_resolve",
2844
+ "selector_retry",
2845
+ "selector_cache_hit",
2846
+ "selector_cache_stale",
2847
+ "deterministic_resolve",
2848
+ "deterministic_resolve_miss",
2849
+ "vision_fallback_start",
2850
+ "vision_fallback_result",
2851
+ "agent_fallback_start",
2852
+ "agent_fallback_result",
2853
+ "recovery_attempt",
2854
+ "extract",
2855
+ "extract_result",
2856
+ "memory_operation"
2857
+ ];
2858
+ function formatDebugLogSections(entries, options = {}) {
2859
+ const lines = [];
2860
+ if (entries.length === 0) return lines;
2861
+ const maxErrors = options.maxErrors ?? 20;
2862
+ const maxRecent = options.maxRecent ?? 15;
2863
+ const recentEventTypes = options.recentEventTypes ?? DEFAULT_RECENT_EVENT_TYPES;
2864
+ if (options.showFailedStepEvents && options.failedOrdinals && options.failedOrdinals.size > 0) {
2865
+ const failedStepEntries = entries.filter(
2866
+ (e) => e.step !== void 0 && options.failedOrdinals.has(e.step)
2867
+ );
2868
+ if (failedStepEntries.length > 0) {
2869
+ lines.push("## Debug Log (Failed Step Events)");
2870
+ lines.push("```json");
2871
+ for (const entry of failedStepEntries.slice(-30)) {
2872
+ lines.push(JSON.stringify(entry));
2873
+ }
2874
+ lines.push("```");
2875
+ lines.push("");
2876
+ }
2877
+ }
2878
+ const errorEntries = entries.filter(
2879
+ (e) => e.event === "error" || e.event === "step_error" || e.event === "validation_failed" || e.data && typeof e.data === "object" && "result" in e.data && typeof e.data.result === "object" && e.data.result !== null && "success" in e.data.result && e.data.result.success === false
2880
+ );
2881
+ if (errorEntries.length > 0) {
2882
+ lines.push("## Debug Log (Error Events)");
2883
+ lines.push("```json");
2884
+ for (const entry of errorEntries.slice(-maxErrors)) {
2885
+ lines.push(JSON.stringify(entry));
2886
+ }
2887
+ lines.push("```");
2888
+ lines.push("");
2889
+ }
2890
+ const actionEntries = entries.filter(
2891
+ (e) => e.event !== void 0 && recentEventTypes.includes(e.event)
2892
+ );
2893
+ const recentActions = actionEntries.slice(-maxRecent);
2894
+ if (recentActions.length > 0) {
2895
+ lines.push("## Debug Log (Recent Events)");
2896
+ lines.push("```json");
2897
+ for (const entry of recentActions) {
2898
+ lines.push(JSON.stringify(entry));
2899
+ }
2900
+ lines.push("```");
2901
+ lines.push("");
2902
+ }
2903
+ return lines;
2904
+ }
2905
+
2906
+ // src/browser/snapshot-filter.ts
2907
+ var KEEP_ROLES = /* @__PURE__ */ new Set([
2908
+ "heading",
2909
+ "navigation",
2910
+ "main",
2911
+ "banner",
2912
+ "form",
2913
+ "dialog",
2914
+ "alertdialog",
2915
+ "alert",
2916
+ "region",
2917
+ "search",
2918
+ "tablist",
2919
+ "tab",
2920
+ "tabpanel",
2921
+ "menu",
2922
+ "menubar",
2923
+ "menuitem",
2924
+ "toolbar",
2925
+ "radiogroup"
2926
+ ]);
2927
+ var NO_COLLAPSE_ROLES = /* @__PURE__ */ new Set([
2928
+ "radio",
2929
+ "checkbox",
2930
+ "option",
2931
+ "tab",
2932
+ "menuitemradio",
2933
+ "menuitemcheckbox",
2934
+ "textbox"
2935
+ ]);
2936
+ var HIDDEN_PATTERNS = [
2937
+ /\[hidden\]/,
2938
+ /\[aria-hidden=true\]/,
2939
+ /\[aria-hidden="true"\]/
2940
+ ];
2941
+ var SEMANTIC_NONE_ROLE_PATTERN = /^\s*(presentation|none)\b/;
2942
+ var PAGINATION_TEXT_PATTERN = /\bpage\s+\d+\s+(?:of|\/)\s+\d+\b|\bshowing\s+\d+[\s\-–]+\d+\s+of\s+\d+\b|\d+\s*[\-–]\s*\d+\s+of\s+\d+\b|\d+\s*\/\s*\d+\s*ページ|\d+件中/i;
2943
+ function filterSnapshot(snapshot, options) {
2944
+ const lines = snapshot.split("\n");
2945
+ const originalCount = lines.length;
2946
+ const keepIndices = /* @__PURE__ */ new Set();
2947
+ const refPattern = /\[ref=e\d+\]/;
2948
+ const indentStack = [];
2949
+ for (let i = 0; i < lines.length; i++) {
2950
+ const line = lines[i];
2951
+ const trimmed = line.trimStart();
2952
+ const indent = line.length - trimmed.length;
2953
+ while (indentStack.length > 0 && indentStack[indentStack.length - 1].indent >= indent) {
2954
+ indentStack.pop();
2955
+ }
2956
+ indentStack.push({ indent, idx: i });
2957
+ const hasRef = refPattern.test(line);
2958
+ if (hasRef) {
2959
+ keepIndices.add(i);
2960
+ continue;
2961
+ }
2962
+ const roleMatch = trimmed.match(/^(\S+)/);
2963
+ if (roleMatch && KEEP_ROLES.has(roleMatch[1])) {
2964
+ if (!isHiddenLine(line)) {
2965
+ keepIndices.add(i);
2966
+ }
2967
+ }
2968
+ if (!keepIndices.has(i) && PAGINATION_TEXT_PATTERN.test(trimmed)) {
2969
+ if (!isHiddenLine(line)) {
2970
+ keepIndices.add(i);
2971
+ }
2972
+ }
2973
+ }
2974
+ const sortedKeepIndices = [...keepIndices].sort((a, b) => a - b);
2975
+ const collapsedResult = collapseRedundantLists(lines, sortedKeepIndices);
2976
+ const filledSelectors = options?.filledSelectors;
2977
+ if (filledSelectors && filledSelectors.size >= 3) {
2978
+ const filledIndices = [];
2979
+ for (const idx of collapsedResult.keptIndices) {
2980
+ const line = lines[idx];
2981
+ if (!line) continue;
2982
+ const refMatch = line.match(/\[ref=(e\d+)\]/);
2983
+ if (refMatch) {
2984
+ const ref = `@${refMatch[1]}`;
2985
+ if (filledSelectors.has(ref)) {
2986
+ filledIndices.push(idx);
2987
+ }
2988
+ }
2989
+ }
2990
+ if (filledIndices.length >= 3) {
2991
+ const filledSet = new Set(filledIndices);
2992
+ collapsedResult.keptIndices = collapsedResult.keptIndices.filter(
2993
+ (idx) => !filledSet.has(idx)
2994
+ );
2995
+ const firstFilledIdx = filledIndices[0];
2996
+ const firstLine = lines[firstFilledIdx];
2997
+ const indent = firstLine ? Math.floor((firstLine.length - firstLine.trimStart().length) / 2) : 2;
2998
+ collapsedResult.collapseMarkers.push({
2999
+ afterLineIndex: firstFilledIdx > 0 ? firstFilledIdx - 1 : 0,
3000
+ omittedCount: filledIndices.length,
3001
+ indentLevel: indent
3002
+ });
3003
+ }
3004
+ }
3005
+ const parentIndices = /* @__PURE__ */ new Set();
3006
+ for (const idx of collapsedResult.keptIndices) {
3007
+ const line = lines[idx];
3008
+ if (!line) continue;
3009
+ const indent = line.length - line.trimStart().length;
3010
+ if (indent > 0) {
3011
+ for (let j = idx - 1; j >= 0; j--) {
3012
+ const parentLine = lines[j];
3013
+ if (!parentLine) continue;
3014
+ const parentIndent = parentLine.length - parentLine.trimStart().length;
3015
+ if (parentIndent < indent && parentLine.trimStart().length > 0) {
3016
+ parentIndices.add(j);
3017
+ break;
3018
+ }
3019
+ }
3020
+ }
3021
+ }
3022
+ const finalIndices = new Set(collapsedResult.keptIndices);
3023
+ for (const idx of parentIndices) {
3024
+ finalIndices.add(idx);
3025
+ }
3026
+ const filteredLines = [];
3027
+ const finalSorted = [...finalIndices].sort((a, b) => a - b);
3028
+ for (const idx of finalSorted) {
3029
+ const line = lines[idx];
3030
+ if (line.length > 300) {
3031
+ const refMatch = line.match(/\s*\[ref=e\d+\].*$/);
3032
+ if (refMatch) {
3033
+ const refSuffix = refMatch[0];
3034
+ const maxContent = 300 - refSuffix.length - 3;
3035
+ filteredLines.push(
3036
+ maxContent > 0 ? line.slice(0, maxContent) + "..." + refSuffix : line.slice(0, 297) + "..."
3037
+ );
3038
+ } else {
3039
+ filteredLines.push(line.slice(0, 297) + "...");
3040
+ }
3041
+ } else {
3042
+ filteredLines.push(line);
3043
+ }
3044
+ }
3045
+ for (const marker of collapsedResult.collapseMarkers) {
3046
+ const insertAfterIdx = filteredLines.findIndex((_, i) => {
3047
+ const originalIdx = finalSorted[i];
3048
+ return originalIdx !== void 0 && originalIdx >= marker.afterLineIndex;
3049
+ });
3050
+ if (insertAfterIdx >= 0) {
3051
+ const indent = " ".repeat(marker.indentLevel);
3052
+ filteredLines.splice(insertAfterIdx + 1, 0, `${indent}[... ${marker.omittedCount}\u4EF6\u7701\u7565]`);
3053
+ }
3054
+ }
3055
+ if (filteredLines.length > 300) {
3056
+ const truncatedCount = filteredLines.length;
3057
+ filteredLines.length = 300;
3058
+ filteredLines.push(`[... ${truncatedCount - 300}\u884C\u7701\u7565]`);
3059
+ }
3060
+ const filteredCount = filteredLines.length;
3061
+ const estimatedTokens = Math.ceil(filteredLines.join("\n").length / 4);
3062
+ const meta = `[\u30D5\u30A3\u30EB\u30BF\u6E08\u307F: ${originalCount}\u884C \u2192 ${filteredCount}\u884C, \u63A8\u5B9A ~${estimatedTokens} tokens]`;
3063
+ return `${meta}
3064
+ ${filteredLines.join("\n")}`;
3065
+ }
3066
+ function isHiddenLine(line) {
3067
+ return HIDDEN_PATTERNS.some((p2) => p2.test(line)) || SEMANTIC_NONE_ROLE_PATTERN.test(line.trimStart());
3068
+ }
3069
+ function collapseRedundantLists(lines, sortedIndices) {
3070
+ const keptIndices = [];
3071
+ const collapseMarkers = [];
3072
+ const refPattern = /\[ref=e\d+\]/;
3073
+ const roleCache = /* @__PURE__ */ new Map();
3074
+ function getCachedRole(idx) {
3075
+ let role = roleCache.get(idx);
3076
+ if (role === void 0) {
3077
+ role = extractRole(lines[idx]);
3078
+ roleCache.set(idx, role);
3079
+ }
3080
+ return role;
3081
+ }
3082
+ let i = 0;
3083
+ while (i < sortedIndices.length) {
3084
+ const startIdx = sortedIndices[i];
3085
+ const startLine = lines[startIdx];
3086
+ if (!startLine) {
3087
+ keptIndices.push(startIdx);
3088
+ i++;
3089
+ continue;
3090
+ }
3091
+ if (!refPattern.test(startLine)) {
3092
+ keptIndices.push(startIdx);
3093
+ i++;
3094
+ continue;
3095
+ }
3096
+ const indent = startLine.length - startLine.trimStart().length;
3097
+ const role = getCachedRole(startIdx);
3098
+ if (!role) {
3099
+ keptIndices.push(startIdx);
3100
+ i++;
3101
+ continue;
3102
+ }
3103
+ let runLength = 1;
3104
+ let j = i + 1;
3105
+ while (j < sortedIndices.length) {
3106
+ const nextIdx = sortedIndices[j];
3107
+ const nextLine = lines[nextIdx];
3108
+ if (!nextLine || !refPattern.test(nextLine)) break;
3109
+ const nextIndent = nextLine.length - nextLine.trimStart().length;
3110
+ const nextRole = getCachedRole(nextIdx);
3111
+ if (nextIndent !== indent || nextRole !== role) break;
3112
+ runLength++;
3113
+ j++;
3114
+ }
3115
+ if (runLength >= 3 && !NO_COLLAPSE_ROLES.has(role)) {
3116
+ for (let k = i; k < i + 2; k++) {
3117
+ keptIndices.push(sortedIndices[k]);
3118
+ }
3119
+ collapseMarkers.push({
3120
+ afterLineIndex: sortedIndices[i + 1],
3121
+ omittedCount: runLength - 2,
3122
+ indentLevel: Math.floor(indent / 2)
3123
+ });
3124
+ i = j;
3125
+ } else {
3126
+ for (let k = i; k < j; k++) {
3127
+ keptIndices.push(sortedIndices[k]);
3128
+ }
3129
+ i = j;
3130
+ }
3131
+ }
3132
+ return { keptIndices, collapseMarkers };
3133
+ }
3134
+ function extractRole(line) {
3135
+ const trimmed = line.trimStart();
3136
+ const cleaned = trimmed.replace(/^-\s+/, "");
3137
+ const roleBeforeRef = cleaned.match(/^(\S+)(?:\s+"[^"]*")?\s*\[ref=e\d+\]/);
3138
+ if (roleBeforeRef) return roleBeforeRef[1];
3139
+ const refFirst = cleaned.match(/\[ref=e\d+\]\s+(\S+)/);
3140
+ if (refFirst) return refFirst[1];
3141
+ const roleMatch = cleaned.match(/^(\S+)/);
3142
+ return roleMatch ? roleMatch[1] : null;
3143
+ }
3144
+
3145
+ export {
3146
+ loadSecrets,
3147
+ NoopLogger,
3148
+ NoopSpinner,
3149
+ AgentBrowser,
3150
+ filterSnapshot,
3151
+ findElementInSnapshot,
3152
+ parseAllElements,
3153
+ countElements,
3154
+ classifyFailure,
3155
+ getRecoveryHint,
3156
+ noopLogger,
3157
+ createDebugLogger,
3158
+ parseAndAppendToMemory,
3159
+ sleep,
3160
+ getAgentInstructions3 as getAgentInstructions,
3161
+ getInitialUserMessage3 as getInitialUserMessage,
3162
+ getReviewSystemPrompt3 as getReviewSystemPrompt,
3163
+ createReviewUserPrompt3 as createReviewUserPrompt,
3164
+ buildSelectorMessages3 as buildSelectorMessages,
3165
+ buildFallbackMessages3 as buildFallbackMessages,
3166
+ buildVisionMessages3 as buildVisionMessages,
3167
+ getSuggestionSystemPrompt3 as getSuggestionSystemPrompt,
3168
+ buildSuggestionUserPrompt3 as buildSuggestionUserPrompt,
3169
+ InMemoryDataStore,
3170
+ DownloadManager,
3171
+ formatDuration,
3172
+ serializeGeneratorOptions,
3173
+ serializeExecutorOptions,
3174
+ renderOptionsMarkdown,
3175
+ readDebugLog,
3176
+ formatAiMetricsSection,
3177
+ formatRuntimeEnvironment,
3178
+ formatSelectorResolutionStats,
3179
+ formatRetryDistribution,
3180
+ formatFailureCategoryDistribution,
3181
+ formatRecoveryEffectiveness,
3182
+ formatPerformanceBottlenecks,
3183
+ formatAiSelectorQualityIssues,
3184
+ formatDebugLogSections
3185
+ };
3186
+ //# sourceMappingURL=chunk-U6GPCKY3.js.map