@agentium/browser 4.0.0 → 4.5.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/action-space.d.cts +42 -0
- package/dist/browser-agent.d.cts +134 -0
- package/dist/browser-agent.d.ts +1 -0
- package/dist/browser-agent.d.ts.map +1 -1
- package/dist/browser-provider.d.cts +242 -0
- package/dist/credential-vault.d.cts +37 -0
- package/dist/index.cjs +49 -41
- package/dist/index.d.cts +7 -0
- package/dist/index.js +50 -42
- package/dist/loop-detector.d.cts +83 -0
- package/dist/prompts.d.cts +21 -0
- package/dist/stealth.d.cts +27 -0
- package/dist/types.d.cts +487 -0
- package/package.json +11 -6
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ApprovalManager, EventBus, Logger, MemoryManager, RunContext, ToolExecutor, choice, jev } from "@agentium/core";
|
|
1
|
+
import { ApprovalManager, EventBus, Logger, MemoryManager, RunContext, ToolExecutor, choice, getAccountingContext, jev, meteredGenerate, withAccountingContext } from "@agentium/core";
|
|
2
2
|
import { z } from "zod/v3";
|
|
3
3
|
function searchUrl(query, engine = "duckduckgo") {
|
|
4
4
|
const q = encodeURIComponent(query.trim());
|
|
@@ -1621,6 +1621,35 @@ var BrowserAgent = class {
|
|
|
1621
1621
|
if (config.memory) this.memoryManager = new MemoryManager(config.memory);
|
|
1622
1622
|
}
|
|
1623
1623
|
async run(task, opts) {
|
|
1624
|
+
const parent = getAccountingContext();
|
|
1625
|
+
const tracker = this.costTracker ?? parent?.tracker;
|
|
1626
|
+
if (!tracker) return this.runAccounted(task, opts);
|
|
1627
|
+
const ctx = opts?.context ?? new RunContext({
|
|
1628
|
+
sessionId: opts?.sessionId ?? `browser_${Date.now()}`,
|
|
1629
|
+
userId: opts?.userId,
|
|
1630
|
+
tenantId: opts?.tenantId,
|
|
1631
|
+
signal: opts?.signal,
|
|
1632
|
+
runMode: opts?.runMode,
|
|
1633
|
+
executionPolicy: this.executionPolicy,
|
|
1634
|
+
eventBus: this.eventBus
|
|
1635
|
+
});
|
|
1636
|
+
return withAccountingContext({
|
|
1637
|
+
...parent,
|
|
1638
|
+
tracker,
|
|
1639
|
+
runId: ctx.runId,
|
|
1640
|
+
rootRunId: parent?.rootRunId ?? parent?.runId ?? ctx.runId,
|
|
1641
|
+
parentRunId: parent?.runId,
|
|
1642
|
+
sessionId: ctx.sessionId,
|
|
1643
|
+
tenantId: ctx.tenantId,
|
|
1644
|
+
userId: ctx.userId,
|
|
1645
|
+
agentName: this.name,
|
|
1646
|
+
eventBus: this.eventBus
|
|
1647
|
+
}, () => this.runAccounted(task, {
|
|
1648
|
+
...opts,
|
|
1649
|
+
context: ctx
|
|
1650
|
+
}));
|
|
1651
|
+
}
|
|
1652
|
+
async runAccounted(task, opts) {
|
|
1624
1653
|
const startTime = Date.now();
|
|
1625
1654
|
const maxSteps = opts?.maxSteps ?? this.maxSteps;
|
|
1626
1655
|
const sessionId = opts?.context?.sessionId ?? opts?.sessionId ?? `browser_${startTime}`;
|
|
@@ -1751,49 +1780,28 @@ var BrowserAgent = class {
|
|
|
1751
1780
|
vision: wantVision
|
|
1752
1781
|
});
|
|
1753
1782
|
let envelope = null;
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
});
|
|
1769
|
-
envelope = planned.envelope;
|
|
1770
|
-
modelUsed = planned.modelUsed;
|
|
1771
|
-
if (this.costTracker && planned.usage) this.costTracker.track({
|
|
1772
|
-
runId: sessionId,
|
|
1773
|
-
agentName: this.name,
|
|
1774
|
-
modelId: modelUsed.modelId,
|
|
1775
|
-
usage: planned.usage,
|
|
1776
|
-
sessionId,
|
|
1777
|
-
userId
|
|
1778
|
-
});
|
|
1779
|
-
} else {
|
|
1783
|
+
if (this.planner === "jev") envelope = (await this.planWithJev({
|
|
1784
|
+
task,
|
|
1785
|
+
url: pageInfo.url,
|
|
1786
|
+
title: pageInfo.title,
|
|
1787
|
+
elements,
|
|
1788
|
+
tabs,
|
|
1789
|
+
actionHistory,
|
|
1790
|
+
lastExtract: lastExtractResult,
|
|
1791
|
+
pagesBelow: scrollCtx?.pagesBelow,
|
|
1792
|
+
pagesAbove: scrollCtx?.pagesAbove,
|
|
1793
|
+
apiKey: opts?.apiKey,
|
|
1794
|
+
signal: ctx.signal
|
|
1795
|
+
})).envelope;
|
|
1796
|
+
else {
|
|
1780
1797
|
const messages = this.buildMessages(systemPrompt, historyTurns, userText, wantVision ? screenshot : null);
|
|
1781
|
-
const { response
|
|
1782
|
-
modelUsed = used;
|
|
1798
|
+
const { response } = await this.callModelWithFallback(messages, opts?.apiKey, ctx.signal);
|
|
1783
1799
|
if (!response) {
|
|
1784
1800
|
consecutiveFailures++;
|
|
1785
1801
|
actionHistory.push(`(model call failed — retrying, ${consecutiveFailures}/${this.maxFailures})`);
|
|
1786
1802
|
if (consecutiveFailures > this.maxFailures) return await this.forceDone(browser, steps, startTime, opts, extractedContent, actionHistory, "model");
|
|
1787
1803
|
continue;
|
|
1788
1804
|
}
|
|
1789
|
-
if (this.costTracker && response.usage) this.costTracker.track({
|
|
1790
|
-
runId: sessionId,
|
|
1791
|
-
agentName: this.name,
|
|
1792
|
-
modelId: modelUsed.modelId,
|
|
1793
|
-
usage: response.usage,
|
|
1794
|
-
sessionId,
|
|
1795
|
-
userId
|
|
1796
|
-
});
|
|
1797
1805
|
const raw = typeof response.message.content === "string" ? response.message.content : "";
|
|
1798
1806
|
envelope = this.parseEnvelope(raw);
|
|
1799
1807
|
}
|
|
@@ -2026,7 +2034,7 @@ var BrowserAgent = class {
|
|
|
2026
2034
|
};
|
|
2027
2035
|
try {
|
|
2028
2036
|
signal?.throwIfAborted();
|
|
2029
|
-
const r = await this.model
|
|
2037
|
+
const r = await meteredGenerate(this.model, messages, reqOpts);
|
|
2030
2038
|
signal?.throwIfAborted();
|
|
2031
2039
|
return {
|
|
2032
2040
|
response: r,
|
|
@@ -2041,7 +2049,7 @@ var BrowserAgent = class {
|
|
|
2041
2049
|
fallback: this.fallbackModel.modelId
|
|
2042
2050
|
});
|
|
2043
2051
|
try {
|
|
2044
|
-
const r = await this.fallbackModel
|
|
2052
|
+
const r = await meteredGenerate(this.fallbackModel, messages, reqOpts);
|
|
2045
2053
|
signal?.throwIfAborted();
|
|
2046
2054
|
return {
|
|
2047
2055
|
response: r,
|
|
@@ -2095,7 +2103,7 @@ var BrowserAgent = class {
|
|
|
2095
2103
|
const provider = this.getJev();
|
|
2096
2104
|
args.signal?.throwIfAborted();
|
|
2097
2105
|
try {
|
|
2098
|
-
const response = await provider
|
|
2106
|
+
const response = await meteredGenerate(provider, [{
|
|
2099
2107
|
role: "user",
|
|
2100
2108
|
content: JSON.stringify({
|
|
2101
2109
|
task: args.task,
|
|
@@ -2182,7 +2190,7 @@ var BrowserAgent = class {
|
|
|
2182
2190
|
const model = this.pageExtractionLLM ?? (this.model.providerId === "jev" ? null : this.model);
|
|
2183
2191
|
if (!model) return guessSearchQuery(task);
|
|
2184
2192
|
try {
|
|
2185
|
-
const response = await model
|
|
2193
|
+
const response = await meteredGenerate(model, [{
|
|
2186
2194
|
role: "user",
|
|
2187
2195
|
content: `Task: ${task}\nThe next browser action is ${pick}. Reply with ONLY the text to type into that field. No quotes.`
|
|
2188
2196
|
}], { signal });
|
|
@@ -2479,7 +2487,7 @@ var BrowserAgent = class {
|
|
|
2479
2487
|
role: "user",
|
|
2480
2488
|
content: `Query: ${action.query}\n\nPage content:\n${pageText}`
|
|
2481
2489
|
}];
|
|
2482
|
-
const response = await model
|
|
2490
|
+
const response = await meteredGenerate(model, messages, {
|
|
2483
2491
|
temperature: 0,
|
|
2484
2492
|
maxTokens: 2048,
|
|
2485
2493
|
signal: ctx.signal
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { BrowserAction } from "./types.cjs";
|
|
2
|
+
/**
|
|
3
|
+
* Detects two kinds of "stuck":
|
|
4
|
+
*
|
|
5
|
+
* 1. **Action loops** — the same action (after normalization) repeated too
|
|
6
|
+
* often in a rolling window. Replaces the v2.0.x exact-string compare
|
|
7
|
+
* that was easily fooled by tiny wording changes in `description`.
|
|
8
|
+
* 2. **Page stagnation** — the page hasn't changed (URL + interactive-
|
|
9
|
+
* element count + DOM text hash) for several steps in a row. Useful
|
|
10
|
+
* for catching cases where the agent keeps clicking but nothing
|
|
11
|
+
* happens (overlay blocking, dead button).
|
|
12
|
+
*
|
|
13
|
+
* Instead of hard-failing on the first sign of trouble like v2.0.x did,
|
|
14
|
+
* we emit a graduated severity level so the agent loop can:
|
|
15
|
+
* - 0/`none` — fine, do nothing
|
|
16
|
+
* - 1/`warn` — nudge the model ("you might be repeating yourself")
|
|
17
|
+
* - 2/`escalate` — stronger nudge ("you're definitely stuck, try a
|
|
18
|
+
* different approach")
|
|
19
|
+
* - 3/`abort` — force termination
|
|
20
|
+
*
|
|
21
|
+
* Thresholds are loosely modelled on browser-use's escalating nudges.
|
|
22
|
+
*/
|
|
23
|
+
export type LoopSeverity = "none" | "warn" | "escalate" | "abort";
|
|
24
|
+
export interface LoopAdvice {
|
|
25
|
+
severity: LoopSeverity;
|
|
26
|
+
message?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface PageFingerprint {
|
|
29
|
+
url: string;
|
|
30
|
+
interactiveCount: number;
|
|
31
|
+
/** Cheap hash of the first ~1000 chars of visible body text. */
|
|
32
|
+
textHash: number;
|
|
33
|
+
}
|
|
34
|
+
export declare class LoopDetector {
|
|
35
|
+
private actionWindow;
|
|
36
|
+
private pageHistory;
|
|
37
|
+
private readonly actionWindowSize;
|
|
38
|
+
private readonly pageHistorySize;
|
|
39
|
+
/** Thresholds: repeat counts at which each severity fires. */
|
|
40
|
+
private readonly actionThresholds;
|
|
41
|
+
/** Thresholds: consecutive unchanged-page counts at which each fires. */
|
|
42
|
+
private readonly pageThresholds;
|
|
43
|
+
constructor(opts?: {
|
|
44
|
+
actionWindowSize?: number;
|
|
45
|
+
pageHistorySize?: number;
|
|
46
|
+
actionThresholds?: {
|
|
47
|
+
warn?: number;
|
|
48
|
+
escalate?: number;
|
|
49
|
+
abort?: number;
|
|
50
|
+
};
|
|
51
|
+
pageThresholds?: {
|
|
52
|
+
warn?: number;
|
|
53
|
+
escalate?: number;
|
|
54
|
+
abort?: number;
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* Record an action and return advice for the runtime. `severity` of
|
|
59
|
+
* `abort` means the loop should terminate now.
|
|
60
|
+
*/
|
|
61
|
+
recordAction(action: BrowserAction): LoopAdvice;
|
|
62
|
+
/**
|
|
63
|
+
* Record a page fingerprint and return advice if the page has been
|
|
64
|
+
* stagnant. Counts consecutive identical fingerprints at the END of
|
|
65
|
+
* the history (so a single navigation resets the counter).
|
|
66
|
+
*/
|
|
67
|
+
recordPage(fp: PageFingerprint): LoopAdvice;
|
|
68
|
+
/** Combine two advices, returning the more severe one. */
|
|
69
|
+
static combine(a: LoopAdvice, b: LoopAdvice): LoopAdvice;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Normalize an action for loop comparison. The goal is to canonicalize
|
|
73
|
+
* tiny variations the LLM produces (e.g. different `description`s for
|
|
74
|
+
* the same click) into a single key while keeping actions that are
|
|
75
|
+
* fundamentally different (different `index`, `text`, `url`, …) distinct.
|
|
76
|
+
*/
|
|
77
|
+
export declare function normalizeAction(action: BrowserAction): string;
|
|
78
|
+
/**
|
|
79
|
+
* Tiny non-cryptographic string hash, stable across runs. Used for the
|
|
80
|
+
* DOM-text portion of `PageFingerprint`.
|
|
81
|
+
*/
|
|
82
|
+
export declare function fnvHash(s: string): number;
|
|
83
|
+
//# sourceMappingURL=loop-detector.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ToolDef } from "@agentium/core";
|
|
2
|
+
import type { TabInfo } from "./action-space.cjs";
|
|
3
|
+
import type { DomScrollContext } from "./types.cjs";
|
|
4
|
+
export declare function buildSystemPrompt(viewport: {
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
}, extraInstructions?: string, credentialKeys?: string[], options?: {
|
|
8
|
+
overrideSystemMessage?: string;
|
|
9
|
+
maxActionsPerStep?: number;
|
|
10
|
+
allowEvaluate?: boolean;
|
|
11
|
+
tools?: ToolDef[];
|
|
12
|
+
useVision?: boolean | "auto";
|
|
13
|
+
useDOM?: boolean;
|
|
14
|
+
useThinking?: boolean;
|
|
15
|
+
}): string;
|
|
16
|
+
export declare function buildUserMessage(task: string, pageUrl: string, pageTitle: string, stepIndex: number, actionHistory: string[], domSnapshot?: string, lastExtract?: string, scroll?: DomScrollContext, nudge?: string, stepBudget?: {
|
|
17
|
+
current: number;
|
|
18
|
+
max: number;
|
|
19
|
+
}, tabs?: TabInfo[]): string;
|
|
20
|
+
export declare function summarizeAction(action: Record<string, unknown>): string;
|
|
21
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { StealthConfig } from "./types.cjs";
|
|
2
|
+
/** Pick a random realistic user-agent string. */
|
|
3
|
+
export declare function pickUserAgent(): string;
|
|
4
|
+
/**
|
|
5
|
+
* JavaScript that runs inside every page to patch common bot-detection vectors.
|
|
6
|
+
* Injected via Playwright's `context.addInitScript()`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getStealthScript(): string;
|
|
9
|
+
/**
|
|
10
|
+
* Build Playwright context options from a StealthConfig.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildStealthContextOpts(config: StealthConfig, viewport: {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
}): Record<string, unknown>;
|
|
16
|
+
/**
|
|
17
|
+
* Build Playwright launch options for stealth.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildStealthLaunchArgs(config: StealthConfig): {
|
|
20
|
+
args: string[];
|
|
21
|
+
proxy?: {
|
|
22
|
+
server: string;
|
|
23
|
+
username?: string;
|
|
24
|
+
password?: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=stealth.d.ts.map
|