@tangle-network/browser-agent-driver 0.23.0 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +357 -126
- package/dist/brain/index.d.ts +6 -0
- package/dist/brain/index.d.ts.map +1 -1
- package/dist/brain/index.js +43 -12
- package/dist/brain/index.js.map +1 -1
- package/dist/memory/knowledge.d.ts +6 -0
- package/dist/memory/knowledge.d.ts.map +1 -1
- package/dist/memory/knowledge.js +15 -0
- package/dist/memory/knowledge.js.map +1 -1
- package/dist/run-state.d.ts +4 -0
- package/dist/run-state.d.ts.map +1 -1
- package/dist/run-state.js +2 -0
- package/dist/run-state.js.map +1 -1
- package/dist/runner/goal-decomposer.d.ts +38 -0
- package/dist/runner/goal-decomposer.d.ts.map +1 -0
- package/dist/runner/goal-decomposer.js +125 -0
- package/dist/runner/goal-decomposer.js.map +1 -0
- package/dist/runner/parallel-runner.d.ts +61 -0
- package/dist/runner/parallel-runner.d.ts.map +1 -0
- package/dist/runner/parallel-runner.js +133 -0
- package/dist/runner/parallel-runner.js.map +1 -0
- package/dist/runner/pattern-extractor.d.ts +40 -0
- package/dist/runner/pattern-extractor.d.ts.map +1 -0
- package/dist/runner/pattern-extractor.js +122 -0
- package/dist/runner/pattern-extractor.js.map +1 -0
- package/dist/runner/runner.d.ts.map +1 -1
- package/dist/runner/runner.js +109 -6
- package/dist/runner/runner.js.map +1 -1
- package/dist/types.d.ts +32 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/runner/runner.js
CHANGED
|
@@ -282,6 +282,37 @@ export class BrowserAgent {
|
|
|
282
282
|
this.runRegistry = options.runRegistry;
|
|
283
283
|
}
|
|
284
284
|
async run(scenario) {
|
|
285
|
+
// Gen 21: parallel tab execution for compound goals.
|
|
286
|
+
// Pre-flight: check if the goal should be decomposed into parallel sub-goals.
|
|
287
|
+
if (this.config.parallelTabs?.enabled && scenario.goal && scenario.startUrl) {
|
|
288
|
+
const context = this.driver.getPage?.()?.context();
|
|
289
|
+
if (context) {
|
|
290
|
+
const { decomposeGoal } = await import('./goal-decomposer.js');
|
|
291
|
+
const decomposition = await decomposeGoal(scenario.goal, scenario.startUrl, {
|
|
292
|
+
provider: this.config.provider || 'openai',
|
|
293
|
+
model: this.config.navModel || 'gpt-4.1-mini',
|
|
294
|
+
apiKey: this.config.apiKey,
|
|
295
|
+
});
|
|
296
|
+
if (decomposition.type === 'compound' && decomposition.subGoals) {
|
|
297
|
+
const { runParallel } = await import('./parallel-runner.js');
|
|
298
|
+
const result = await runParallel({
|
|
299
|
+
context,
|
|
300
|
+
config: this.config,
|
|
301
|
+
originalGoal: scenario.goal,
|
|
302
|
+
subGoals: decomposition.subGoals,
|
|
303
|
+
scenario,
|
|
304
|
+
onTurn: this.onTurn ? (_label, turn) => this.onTurn(turn) : undefined,
|
|
305
|
+
projectStore: this.projectStore,
|
|
306
|
+
});
|
|
307
|
+
return {
|
|
308
|
+
success: result.success,
|
|
309
|
+
reason: result.mergedResult,
|
|
310
|
+
turns: [],
|
|
311
|
+
totalMs: result.totalMs,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
285
316
|
// Gen 14: vision mode gets more turns — each turn takes ~15s (screenshot
|
|
286
317
|
// encode + image tokens) vs ~5s for DOM-first. Without the boost, vision
|
|
287
318
|
// runs out of turns before completing multi-step tasks.
|
|
@@ -322,7 +353,7 @@ export class BrowserAgent {
|
|
|
322
353
|
phaseTimings,
|
|
323
354
|
wasteMetrics: deriveWasteMetrics(turns, runState.verificationRejectionCount, runState.firstSufficientEvidenceTurn),
|
|
324
355
|
};
|
|
325
|
-
this.saveMemory(scenario, agentResult);
|
|
356
|
+
this.saveMemory(scenario, agentResult, turns);
|
|
326
357
|
// Complete run manifest
|
|
327
358
|
const lastTurn = agentResult.turns[agentResult.turns.length - 1];
|
|
328
359
|
this.runRegistry?.completeRun(runId, {
|
|
@@ -395,6 +426,19 @@ export class BrowserAgent {
|
|
|
395
426
|
await navPromise;
|
|
396
427
|
phaseTimings.initialNavigateMs = Date.now() - navigateStartedAt;
|
|
397
428
|
this.onPhaseTiming?.('navigate', phaseTimings.initialNavigateMs);
|
|
429
|
+
// Gen 24b: page warm-up — simulate human settling time after page load.
|
|
430
|
+
// DataDome and similar ML anti-bot systems measure time-to-first-interaction.
|
|
431
|
+
// Bots act within 100ms; humans take 1-3 seconds to orient on a new page.
|
|
432
|
+
// Also adds a small random scroll to generate natural mouse/scroll events.
|
|
433
|
+
const page = this.driver.getPage?.();
|
|
434
|
+
if (page) {
|
|
435
|
+
const settleMs = 1500 + Math.floor(Math.random() * 1500); // 1.5-3s
|
|
436
|
+
await page.waitForTimeout(settleMs);
|
|
437
|
+
// Small exploratory scroll — humans look around before acting
|
|
438
|
+
await page.mouse.move(300 + Math.random() * 400, 200 + Math.random() * 200);
|
|
439
|
+
await page.mouse.wheel(0, 100 + Math.random() * 200);
|
|
440
|
+
await page.waitForTimeout(300 + Math.floor(Math.random() * 500));
|
|
441
|
+
}
|
|
398
442
|
}
|
|
399
443
|
// Don't wait on warmup before entering the loop — it races against the
|
|
400
444
|
// first observe and decode. Make sure any unhandled rejection is silenced.
|
|
@@ -410,8 +454,9 @@ export class BrowserAgent {
|
|
|
410
454
|
});
|
|
411
455
|
const supervisorConfig = {
|
|
412
456
|
enabled: this.config.supervisor?.enabled ?? DEFAULT_SUPERVISOR.enabled,
|
|
413
|
-
|
|
414
|
-
|
|
457
|
+
// Gen 28: models.supervisor overrides supervisor.model, falls back to main
|
|
458
|
+
model: this.config.models?.supervisor?.model || this.config.supervisor?.model || this.config.model || 'gpt-5.4',
|
|
459
|
+
provider: (this.config.models?.supervisor?.provider || this.config.supervisor?.provider || this.config.provider || 'openai'),
|
|
415
460
|
useVision: this.config.supervisor?.useVision ?? DEFAULT_SUPERVISOR.useVision,
|
|
416
461
|
minTurnsBeforeInvoke: this.config.supervisor?.minTurnsBeforeInvoke ?? DEFAULT_SUPERVISOR.minTurnsBeforeInvoke,
|
|
417
462
|
cooldownTurns: this.config.supervisor?.cooldownTurns ?? DEFAULT_SUPERVISOR.cooldownTurns,
|
|
@@ -1185,11 +1230,26 @@ export class BrowserAgent {
|
|
|
1185
1230
|
}
|
|
1186
1231
|
}
|
|
1187
1232
|
else {
|
|
1233
|
+
// Gen 24b: micro-movements during LLM "thinking" — anti-bot systems
|
|
1234
|
+
// flag frozen cursors. Run small random mouse drifts in parallel with
|
|
1235
|
+
// the LLM call. Stops when the decision arrives.
|
|
1236
|
+
let microMoving = true;
|
|
1237
|
+
const microMoveLoop = (async () => {
|
|
1238
|
+
const p = this.driver.getPage?.();
|
|
1239
|
+
if (!p)
|
|
1240
|
+
return;
|
|
1241
|
+
while (microMoving) {
|
|
1242
|
+
await p.mouse.move(300 + Math.random() * 600, 200 + Math.random() * 400, { steps: 3 }).catch(() => { });
|
|
1243
|
+
await p.waitForTimeout(800 + Math.floor(Math.random() * 1200)).catch(() => { });
|
|
1244
|
+
}
|
|
1245
|
+
})();
|
|
1188
1246
|
decision = await withRetry(() => this.brain.decide(scenario.goal, decisionState, finalExtraContext || undefined, { current: i, max: maxTurns }, { forceVision }), retries, retryDelayMs, (attempt, err) => {
|
|
1189
1247
|
if (this.config.debug) {
|
|
1190
1248
|
console.log(`[Runner] LLM retry ${attempt}: ${err.message}`);
|
|
1191
1249
|
}
|
|
1192
1250
|
}, scenario.signal);
|
|
1251
|
+
microMoving = false;
|
|
1252
|
+
void microMoveLoop; // ensure no unhandled rejection
|
|
1193
1253
|
if (cacheKey && this.decisionCache) {
|
|
1194
1254
|
// Store the fresh decision so a future identical turn replays it.
|
|
1195
1255
|
this.decisionCache.set(cacheKey, decision);
|
|
@@ -1547,8 +1607,22 @@ export class BrowserAgent {
|
|
|
1547
1607
|
runState.verificationRejectionCount++;
|
|
1548
1608
|
turn.verificationFailure = goalResult.missing.join('; ') || 'Goal verification failed';
|
|
1549
1609
|
runState.firstSufficientEvidenceTurn ??= i;
|
|
1610
|
+
// Gen 24b: checkpoint replay on 2nd rejection. Navigate back
|
|
1611
|
+
// to a previous page where the agent had correct data, instead
|
|
1612
|
+
// of continuing from the wrong-path state.
|
|
1613
|
+
let replayNote = '';
|
|
1614
|
+
if (runState.verificationRejectionCount === 2 && runState.checkpoints.length >= 2) {
|
|
1615
|
+
// Go back to the second-to-last checkpoint (before the wrong path)
|
|
1616
|
+
const target = runState.checkpoints[runState.checkpoints.length - 2];
|
|
1617
|
+
if (target) {
|
|
1618
|
+
try {
|
|
1619
|
+
await this.driver.execute({ action: 'navigate', url: target.url });
|
|
1620
|
+
replayNote = ` ROLLED BACK to ${target.url} (checkpoint from turn ${target.turn}). You went down the wrong path — try a different approach from this known-good page.`;
|
|
1621
|
+
}
|
|
1622
|
+
catch { /* rollback failed, continue from current state */ }
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1550
1625
|
// Gen 19: progressive strategy-shift escalation on rejection.
|
|
1551
|
-
// Each rejection level suggests a MORE different approach.
|
|
1552
1626
|
let escalation;
|
|
1553
1627
|
if (runState.verificationRejectionCount >= 3) {
|
|
1554
1628
|
escalation = ' STRATEGY SHIFT REQUIRED: Your previous approaches have failed 3 times. Try a COMPLETELY different method: use navigate to go to a different search engine or URL, try extractWithIndex instead of runScript, or scroll to look for the data in a different part of the page. Do NOT repeat what you just tried.';
|
|
@@ -1559,7 +1633,7 @@ export class BrowserAgent {
|
|
|
1559
1633
|
else {
|
|
1560
1634
|
escalation = ' Re-read the GOAL carefully — your result is missing specific data the goal asked for. Find and include it before completing.';
|
|
1561
1635
|
}
|
|
1562
|
-
this.brain.injectFeedback(`REJECTED (${goalResult.confidence.toFixed(2)}). Missing: ${goalResult.missing.join('; ')}.${escalation}`);
|
|
1636
|
+
this.brain.injectFeedback(`REJECTED (${goalResult.confidence.toFixed(2)}). Missing: ${goalResult.missing.join('; ')}.${escalation}${replayNote}`);
|
|
1563
1637
|
turn.durationMs = Date.now() - turnStart;
|
|
1564
1638
|
turns.push(turn);
|
|
1565
1639
|
this.onTurn?.(turn);
|
|
@@ -1740,6 +1814,16 @@ export class BrowserAgent {
|
|
|
1740
1814
|
console.log(`[Runner] Fill warning: ${warning}`);
|
|
1741
1815
|
}
|
|
1742
1816
|
}
|
|
1817
|
+
// Gen 24b: save checkpoint when URL changes after successful action.
|
|
1818
|
+
// These are rollback points for wrong-path recovery.
|
|
1819
|
+
const postUrl = this.driver.getPage?.()?.url() || '';
|
|
1820
|
+
const lastCheckpointUrl = runState.checkpoints[runState.checkpoints.length - 1]?.url;
|
|
1821
|
+
if (postUrl && postUrl !== 'about:blank' && postUrl !== lastCheckpointUrl) {
|
|
1822
|
+
runState.checkpoints.push({ url: postUrl, turn: i });
|
|
1823
|
+
// Keep max 5 checkpoints to avoid unbounded growth
|
|
1824
|
+
if (runState.checkpoints.length > 5)
|
|
1825
|
+
runState.checkpoints.shift();
|
|
1826
|
+
}
|
|
1743
1827
|
// Capture element bounding box for replay overlays
|
|
1744
1828
|
if (execResult.bounds) {
|
|
1745
1829
|
turn.actionBounds = execResult.bounds;
|
|
@@ -1931,10 +2015,29 @@ export class BrowserAgent {
|
|
|
1931
2015
|
return selected;
|
|
1932
2016
|
}
|
|
1933
2017
|
/** Persist knowledge, selector cache, and session history to disk */
|
|
1934
|
-
saveMemory(scenario, result) {
|
|
2018
|
+
saveMemory(scenario, result, turns) {
|
|
1935
2019
|
try {
|
|
1936
2020
|
if (this.knowledge && scenario && result) {
|
|
1937
2021
|
this.knowledge.recordSession(buildSession(scenario, result));
|
|
2022
|
+
// Gen 26b: extract reusable patterns from successful runs.
|
|
2023
|
+
// Patterns gain confidence with repeated observation and auto-decay
|
|
2024
|
+
// when contradicted. Low-confidence facts are pruned automatically.
|
|
2025
|
+
if (result.success && turns && turns.length > 0) {
|
|
2026
|
+
const domain = safeHostname(scenario.startUrl || '') || '';
|
|
2027
|
+
if (domain) {
|
|
2028
|
+
// Dynamic import to keep the module tree clean
|
|
2029
|
+
import('./pattern-extractor.js').then(({ extractPatterns, recordPatterns }) => {
|
|
2030
|
+
const patterns = extractPatterns(turns, domain, result.success);
|
|
2031
|
+
if (patterns.length > 0) {
|
|
2032
|
+
recordPatterns(this.knowledge, patterns);
|
|
2033
|
+
this.knowledge.save();
|
|
2034
|
+
if (this.config.debug) {
|
|
2035
|
+
console.log(`[Runner] Recorded ${patterns.length} patterns for ${domain}`);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
}).catch(() => { });
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
1938
2041
|
}
|
|
1939
2042
|
this.knowledge?.save();
|
|
1940
2043
|
this.selectorCache?.save();
|