@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.
- package/dist/ai-model-NZ7QJNJW.js +32 -0
- package/dist/ai-model-NZ7QJNJW.js.map +1 -0
- package/dist/chunk-KDEQ2AAE.js +93 -0
- package/dist/chunk-KDEQ2AAE.js.map +1 -0
- package/dist/chunk-U6GPCKY3.js +3186 -0
- package/dist/chunk-U6GPCKY3.js.map +1 -0
- package/dist/chunk-VNQYQSMI.js +922 -0
- package/dist/chunk-VNQYQSMI.js.map +1 -0
- package/dist/chunk-XAEHXRUC.js +607 -0
- package/dist/chunk-XAEHXRUC.js.map +1 -0
- package/dist/cli.js +55 -0
- package/dist/cli.js.map +1 -0
- package/dist/compose-EHDWVF7N.js +543 -0
- package/dist/compose-EHDWVF7N.js.map +1 -0
- package/dist/fix-instruction-JLCLTXAN.js +406 -0
- package/dist/fix-instruction-JLCLTXAN.js.map +1 -0
- package/dist/instruction-executor-M5Q6HYSM.js +5202 -0
- package/dist/instruction-executor-M5Q6HYSM.js.map +1 -0
- package/dist/instruction-generator-5RPRYTVR.js +2470 -0
- package/dist/instruction-generator-5RPRYTVR.js.map +1 -0
- package/package.json +29 -0
|
@@ -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
|