agentic-browser 2.2.0 → 2.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/AGENTS.md +1 -1
- package/README.md +1 -1
- package/dist/cli/index.mjs +2 -2
- package/dist/index.mjs +1 -1
- package/dist/mcp/index.mjs +9 -1
- package/dist/{runtime-CRkUsvhV.mjs → runtime-BCGndjhR.mjs} +296 -14
- package/package.json +5 -5
package/AGENTS.md
CHANGED
|
@@ -108,7 +108,7 @@ AgenticBrowserCore → ControlApi → SessionManager → BrowserController (CDP)
|
|
|
108
108
|
|
|
109
109
|
## Environment Variables
|
|
110
110
|
|
|
111
|
-
- `
|
|
111
|
+
- `AGENTIC_BROWSER_DIR` — base dir for sessions/memory/events (default: `.agentic-browser`)
|
|
112
112
|
- `AGENTIC_BROWSER_CHROME_EXECUTABLE_PATH` — explicit Chrome path (auto-discovered if not set)
|
|
113
113
|
- `AGENTIC_BROWSER_CDP_URL` — connect to an already-running Chrome via CDP (e.g. `http://127.0.0.1:9222`)
|
|
114
114
|
- `AGENTIC_BROWSER_USER_PROFILE` — use real Chrome profile (`default`, `true`, or absolute path)
|
package/README.md
CHANGED
|
@@ -267,7 +267,7 @@ agentic-browser memory:stats
|
|
|
267
267
|
## Important Notes for LLMs
|
|
268
268
|
|
|
269
269
|
- Exactly **one** managed session is supported at a time.
|
|
270
|
-
- Session state is persisted in
|
|
270
|
+
- Session state is persisted in `~/.agentic-browser/` (override with `AGENTIC_BROWSER_DIR`).
|
|
271
271
|
- All commands print exactly one JSON line to `stdout`.
|
|
272
272
|
- The typed `agent` commands are the preferred interface for LLMs. Use low-level `command:run` only when you need raw JSON payload control.
|
|
273
273
|
- Parse only `stdout` as result object and use exit code for success/failure.
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { r as createCliRuntime } from "../runtime-
|
|
2
|
+
import { r as createCliRuntime } from "../runtime-BCGndjhR.mjs";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import crypto from "node:crypto";
|
|
@@ -7,7 +7,7 @@ import { Command } from "commander";
|
|
|
7
7
|
//#region src/cli/commands/agent.ts
|
|
8
8
|
const STATE_FILE_NAME = "agent-state.json";
|
|
9
9
|
function stateFilePath(runtime) {
|
|
10
|
-
return path.join(runtime.context.config.
|
|
10
|
+
return path.join(runtime.context.config.dataDir, STATE_FILE_NAME);
|
|
11
11
|
}
|
|
12
12
|
function loadState(runtime) {
|
|
13
13
|
const filePath = stateFilePath(runtime);
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { i as createMockAgenticBrowserCore, n as createAgenticBrowserCore, t as AgenticBrowserCore } from "./runtime-
|
|
2
|
+
import { i as createMockAgenticBrowserCore, n as createAgenticBrowserCore, t as AgenticBrowserCore } from "./runtime-BCGndjhR.mjs";
|
|
3
3
|
export { AgenticBrowserCore, createAgenticBrowserCore, createMockAgenticBrowserCore };
|
package/dist/mcp/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as createAgenticBrowserCore } from "../runtime-
|
|
2
|
+
import { n as createAgenticBrowserCore } from "../runtime-BCGndjhR.mjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -186,6 +186,14 @@ function compactMemoryResults(results) {
|
|
|
186
186
|
if (Array.isArray(compactAlias.fallbackSelectors) && compactAlias.fallbackSelectors.length === 0) delete compactAlias.fallbackSelectors;
|
|
187
187
|
return compactAlias;
|
|
188
188
|
});
|
|
189
|
+
if (compact.siteProfile) {
|
|
190
|
+
const profile = compact.siteProfile;
|
|
191
|
+
compact.siteProfile = {
|
|
192
|
+
selectorPatterns: (profile.selectorPatterns ?? []).map((p) => `${p.pattern} (x${p.frequency})`),
|
|
193
|
+
cookieBanner: profile.cookieBanner,
|
|
194
|
+
visitCount: profile.visitCount
|
|
195
|
+
};
|
|
196
|
+
}
|
|
189
197
|
return compact;
|
|
190
198
|
}) };
|
|
191
199
|
}
|
|
@@ -2114,7 +2114,7 @@ var SessionManager = class {
|
|
|
2114
2114
|
constructor(ctx, browser) {
|
|
2115
2115
|
this.ctx = ctx;
|
|
2116
2116
|
this.browser = browser;
|
|
2117
|
-
this.store = new SessionStore(this.ctx.config.
|
|
2117
|
+
this.store = new SessionStore(this.ctx.config.dataDir);
|
|
2118
2118
|
}
|
|
2119
2119
|
async createSession(input) {
|
|
2120
2120
|
if (input.browser !== "chrome") throw new Error("Only chrome is supported");
|
|
@@ -2235,7 +2235,19 @@ var SessionManager = class {
|
|
|
2235
2235
|
}
|
|
2236
2236
|
async getContent(sessionId, options) {
|
|
2237
2237
|
const record = await this.ensureSession(sessionId);
|
|
2238
|
-
|
|
2238
|
+
const result = await this.browser.getContent(record.targetWsUrl, options);
|
|
2239
|
+
if (options.mode === "summary" && result.structuredContent) {
|
|
2240
|
+
const sc = result.structuredContent;
|
|
2241
|
+
const url = typeof sc.url === "string" ? sc.url : record.lastUrl;
|
|
2242
|
+
const parsed = this.parseUrl(url);
|
|
2243
|
+
const headingTexts = (sc.headings ?? []).map((h) => typeof h === "string" ? h : h.text ?? "");
|
|
2244
|
+
const landmarkTexts = (sc.landmarks ?? []).map((l) => typeof l === "string" ? l : l.role ?? "");
|
|
2245
|
+
this.ctx.memoryService.updateProfileFromContent(parsed.domain, parsed.pathPattern, {
|
|
2246
|
+
headings: headingTexts,
|
|
2247
|
+
landmarks: landmarkTexts
|
|
2248
|
+
});
|
|
2249
|
+
}
|
|
2250
|
+
return result;
|
|
2239
2251
|
}
|
|
2240
2252
|
async getInteractiveElements(sessionId, options) {
|
|
2241
2253
|
const record = await this.ensureSession(sessionId);
|
|
@@ -2243,7 +2255,10 @@ var SessionManager = class {
|
|
|
2243
2255
|
}
|
|
2244
2256
|
async dismissCookieBanner(sessionId) {
|
|
2245
2257
|
const record = await this.ensureSession(sessionId);
|
|
2246
|
-
|
|
2258
|
+
const result = await this.browser.dismissCookieBanner(record.targetWsUrl);
|
|
2259
|
+
const parsed = this.parseUrl(record.lastUrl);
|
|
2260
|
+
this.ctx.memoryService.updateProfileCookieBanner(parsed.domain, result);
|
|
2261
|
+
return result;
|
|
2247
2262
|
}
|
|
2248
2263
|
setStatus(status, reason) {
|
|
2249
2264
|
const active = this.store.getActive();
|
|
@@ -2361,7 +2376,7 @@ var SessionManager = class {
|
|
|
2361
2376
|
removedSessionIds.push(record.session.sessionId);
|
|
2362
2377
|
}
|
|
2363
2378
|
const keepIds = new Set(keep.map((record) => record.session.sessionId));
|
|
2364
|
-
const profilesDir = path.join(this.ctx.config.
|
|
2379
|
+
const profilesDir = path.join(this.ctx.config.dataDir, "profiles");
|
|
2365
2380
|
const removedProfileDirs = [];
|
|
2366
2381
|
if (fs.existsSync(profilesDir)) for (const entry of fs.readdirSync(profilesDir)) {
|
|
2367
2382
|
const fullPath = path.join(profilesDir, entry);
|
|
@@ -2549,7 +2564,7 @@ function loadConfig(env = process.env) {
|
|
|
2549
2564
|
host: env.AGENTIC_BROWSER_HOST ?? "127.0.0.1",
|
|
2550
2565
|
wsPort,
|
|
2551
2566
|
commandTimeoutMs,
|
|
2552
|
-
|
|
2567
|
+
dataDir: env.AGENTIC_BROWSER_DIR ?? path.join(os.homedir(), ".agentic-browser"),
|
|
2553
2568
|
browserExecutablePath: env.AGENTIC_BROWSER_CHROME_EXECUTABLE_PATH ?? env.AGENTIC_BROWSER_CHROME_PATH,
|
|
2554
2569
|
cdpUrl: env.AGENTIC_BROWSER_CDP_URL,
|
|
2555
2570
|
userProfileDir,
|
|
@@ -2751,7 +2766,7 @@ function selectorSignal(insight) {
|
|
|
2751
2766
|
const evidenceStrength = Math.min(selectorEvidence / 5, 1);
|
|
2752
2767
|
return .7 * recipeCoverage + .3 * evidenceStrength;
|
|
2753
2768
|
}
|
|
2754
|
-
function scoreInsight(insight, normalizedIntent, normalizedDomain) {
|
|
2769
|
+
function scoreInsight(insight, normalizedIntent, normalizedDomain, profileLookup) {
|
|
2755
2770
|
const insightIntent = normalize(insight.taskIntent);
|
|
2756
2771
|
const intentMatch = insightIntent === normalizedIntent ? 1 : 0;
|
|
2757
2772
|
const intentPartial = intentMatch === 1 || insightIntent.includes(normalizedIntent) || normalizedIntent.includes(insightIntent) ? .65 : 0;
|
|
@@ -2759,7 +2774,7 @@ function scoreInsight(insight, normalizedIntent, normalizedDomain) {
|
|
|
2759
2774
|
const reliability = .6 * confidenceFromCounts(insight.successCount, insight.failureCount) + .4 * freshnessWeight(insight.freshness);
|
|
2760
2775
|
const selectorQuality = selectorSignal(insight);
|
|
2761
2776
|
const score = .5 * Math.max(intentMatch, intentPartial) + .2 * domainMatch + .15 * reliability + .15 * selectorQuality;
|
|
2762
|
-
|
|
2777
|
+
const result = {
|
|
2763
2778
|
insightId: insight.insightId,
|
|
2764
2779
|
taskIntent: insight.taskIntent,
|
|
2765
2780
|
siteDomain: insight.siteDomain,
|
|
@@ -2770,6 +2785,14 @@ function scoreInsight(insight, normalizedIntent, normalizedDomain) {
|
|
|
2770
2785
|
selectorAliases: buildSelectorAliases(insight),
|
|
2771
2786
|
score
|
|
2772
2787
|
};
|
|
2788
|
+
const profile = profileLookup?.(insight.siteDomain);
|
|
2789
|
+
if (profile) result.siteProfile = {
|
|
2790
|
+
selectorPatterns: profile.selectorPatterns.slice(0, 5),
|
|
2791
|
+
layoutFingerprints: profile.layoutFingerprints.slice(0, 3),
|
|
2792
|
+
cookieBanner: profile.cookieBanner,
|
|
2793
|
+
visitCount: profile.visitCount
|
|
2794
|
+
};
|
|
2795
|
+
return result;
|
|
2773
2796
|
}
|
|
2774
2797
|
var MemoryIndex = class {
|
|
2775
2798
|
/** Domain → insights index, rebuilt lazily when insight list changes. */
|
|
@@ -2790,7 +2813,7 @@ var MemoryIndex = class {
|
|
|
2790
2813
|
}
|
|
2791
2814
|
this.indexedRefs = insights.slice();
|
|
2792
2815
|
}
|
|
2793
|
-
search(insights, input) {
|
|
2816
|
+
search(insights, input, profileLookup) {
|
|
2794
2817
|
const normalizedIntent = normalize(input.taskIntent);
|
|
2795
2818
|
const normalizedDomain = input.siteDomain ? normalize(input.siteDomain) : void 0;
|
|
2796
2819
|
const limit = input.limit ?? 10;
|
|
@@ -2799,7 +2822,7 @@ var MemoryIndex = class {
|
|
|
2799
2822
|
if (limit === 1 && normalizedDomain) {
|
|
2800
2823
|
let best;
|
|
2801
2824
|
for (const insight of candidates) {
|
|
2802
|
-
const result = scoreInsight(insight, normalizedIntent, normalizedDomain);
|
|
2825
|
+
const result = scoreInsight(insight, normalizedIntent, normalizedDomain, profileLookup);
|
|
2803
2826
|
if (result.score > 0 && (!best || result.score > best.score)) {
|
|
2804
2827
|
best = result;
|
|
2805
2828
|
if (best.score >= .95) break;
|
|
@@ -2807,7 +2830,7 @@ var MemoryIndex = class {
|
|
|
2807
2830
|
}
|
|
2808
2831
|
return best ? [best] : [];
|
|
2809
2832
|
}
|
|
2810
|
-
return candidates.map((insight) => scoreInsight(insight, normalizedIntent, normalizedDomain)).filter((result) => result.score > 0).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
2833
|
+
return candidates.map((insight) => scoreInsight(insight, normalizedIntent, normalizedDomain, profileLookup)).filter((result) => result.score > 0).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
2811
2834
|
}
|
|
2812
2835
|
};
|
|
2813
2836
|
//#endregion
|
|
@@ -2838,6 +2861,20 @@ function applyFailure(insight, signal) {
|
|
|
2838
2861
|
updatedAt: now
|
|
2839
2862
|
};
|
|
2840
2863
|
}
|
|
2864
|
+
function applyAgeStaleness(insight) {
|
|
2865
|
+
const ageDays = (Date.now() - Date.parse(insight.lastVerifiedAt)) / (1440 * 60 * 1e3);
|
|
2866
|
+
if (insight.freshness === "fresh" && ageDays > 30) return {
|
|
2867
|
+
...insight,
|
|
2868
|
+
freshness: "suspect",
|
|
2869
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2870
|
+
};
|
|
2871
|
+
if (insight.freshness === "suspect" && ageDays > 44) return {
|
|
2872
|
+
...insight,
|
|
2873
|
+
freshness: "stale",
|
|
2874
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2875
|
+
};
|
|
2876
|
+
return insight;
|
|
2877
|
+
}
|
|
2841
2878
|
function applySuccess(insight) {
|
|
2842
2879
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2843
2880
|
const successCount = insight.successCount + 1;
|
|
@@ -2904,6 +2941,147 @@ const TaskInsightSchema = z.object({
|
|
|
2904
2941
|
selectorAliases: z.array(SelectorAliasSchema).default([])
|
|
2905
2942
|
});
|
|
2906
2943
|
const MemoryStateSchema = z.object({ insights: z.array(TaskInsightSchema) });
|
|
2944
|
+
const SelectorPatternSchema = z.object({
|
|
2945
|
+
pattern: z.string().min(1),
|
|
2946
|
+
frequency: z.number().int().nonnegative(),
|
|
2947
|
+
lastSeenAt: z.string().datetime()
|
|
2948
|
+
});
|
|
2949
|
+
const PageLayoutFingerprintSchema = z.object({
|
|
2950
|
+
pathPattern: z.string().min(1),
|
|
2951
|
+
headings: z.array(z.string()).default([]),
|
|
2952
|
+
landmarks: z.array(z.string()).default([]),
|
|
2953
|
+
lastSeenAt: z.string().datetime()
|
|
2954
|
+
});
|
|
2955
|
+
const SiteProfileSchema = z.object({
|
|
2956
|
+
domain: z.string().min(1),
|
|
2957
|
+
selectorPatterns: z.array(SelectorPatternSchema).default([]),
|
|
2958
|
+
layoutFingerprints: z.array(PageLayoutFingerprintSchema).default([]),
|
|
2959
|
+
navigationPaths: z.array(z.string()).default([]),
|
|
2960
|
+
cookieBanner: z.object({
|
|
2961
|
+
detected: z.boolean(),
|
|
2962
|
+
autoDismissed: z.boolean().optional(),
|
|
2963
|
+
method: z.string().optional()
|
|
2964
|
+
}).optional(),
|
|
2965
|
+
visitCount: z.number().int().nonnegative().default(0),
|
|
2966
|
+
lastVisitAt: z.string().datetime(),
|
|
2967
|
+
createdAt: z.string().datetime()
|
|
2968
|
+
});
|
|
2969
|
+
const SiteProfileStateSchema = z.object({ profiles: z.array(SiteProfileSchema) });
|
|
2970
|
+
//#endregion
|
|
2971
|
+
//#region src/memory/site-profile-store.ts
|
|
2972
|
+
const EMPTY_STATE$1 = { profiles: [] };
|
|
2973
|
+
const FLUSH_DELAY_MS$1 = 500;
|
|
2974
|
+
const REGISTERED_EXIT_HANDLERS$1 = Symbol.for("agentic-browser.site-profile-store.exit-handlers");
|
|
2975
|
+
const REGISTERED_STORES$1 = Symbol.for("agentic-browser.site-profile-store.instances");
|
|
2976
|
+
var SiteProfileStore = class {
|
|
2977
|
+
filePath;
|
|
2978
|
+
cached = null;
|
|
2979
|
+
dirty = false;
|
|
2980
|
+
flushTimer = null;
|
|
2981
|
+
constructor(baseDir) {
|
|
2982
|
+
const memoryDir = path.join(baseDir, "memory");
|
|
2983
|
+
fs.mkdirSync(memoryDir, { recursive: true });
|
|
2984
|
+
this.filePath = path.join(memoryDir, "site-profiles.json");
|
|
2985
|
+
const tmpPath = `${this.filePath}.tmp`;
|
|
2986
|
+
try {
|
|
2987
|
+
if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);
|
|
2988
|
+
} catch {}
|
|
2989
|
+
if (!fs.existsSync(this.filePath)) this.writeDisk(EMPTY_STATE$1);
|
|
2990
|
+
this.registerProcessHandlers();
|
|
2991
|
+
}
|
|
2992
|
+
list() {
|
|
2993
|
+
return this.getCache();
|
|
2994
|
+
}
|
|
2995
|
+
get(domain) {
|
|
2996
|
+
const normalized = domain.toLowerCase();
|
|
2997
|
+
return this.getCache().find((p) => p.domain === normalized);
|
|
2998
|
+
}
|
|
2999
|
+
upsert(profile) {
|
|
3000
|
+
SiteProfileSchema.parse(profile);
|
|
3001
|
+
const profiles = this.getCache();
|
|
3002
|
+
const index = profiles.findIndex((p) => p.domain === profile.domain);
|
|
3003
|
+
if (index >= 0) profiles[index] = profile;
|
|
3004
|
+
else profiles.push(profile);
|
|
3005
|
+
this.markDirty();
|
|
3006
|
+
}
|
|
3007
|
+
flushSync() {
|
|
3008
|
+
if (this.flushTimer) {
|
|
3009
|
+
clearTimeout(this.flushTimer);
|
|
3010
|
+
this.flushTimer = null;
|
|
3011
|
+
}
|
|
3012
|
+
if (this.dirty && this.cached) {
|
|
3013
|
+
this.writeDisk({ profiles: this.cached });
|
|
3014
|
+
this.dirty = false;
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
getCache() {
|
|
3018
|
+
if (this.cached) return this.cached;
|
|
3019
|
+
this.cached = this.readDisk().profiles;
|
|
3020
|
+
return this.cached;
|
|
3021
|
+
}
|
|
3022
|
+
markDirty() {
|
|
3023
|
+
this.dirty = true;
|
|
3024
|
+
if (!this.flushTimer) this.flushTimer = setTimeout(() => {
|
|
3025
|
+
this.flushTimer = null;
|
|
3026
|
+
this.flushSync();
|
|
3027
|
+
}, FLUSH_DELAY_MS$1);
|
|
3028
|
+
}
|
|
3029
|
+
readDisk() {
|
|
3030
|
+
let raw;
|
|
3031
|
+
try {
|
|
3032
|
+
raw = JSON.parse(fs.readFileSync(this.filePath, "utf8"));
|
|
3033
|
+
} catch {
|
|
3034
|
+
this.backupAndReset();
|
|
3035
|
+
return EMPTY_STATE$1;
|
|
3036
|
+
}
|
|
3037
|
+
try {
|
|
3038
|
+
return SiteProfileStateSchema.parse(raw);
|
|
3039
|
+
} catch {
|
|
3040
|
+
const obj = raw;
|
|
3041
|
+
if (Array.isArray(obj?.profiles)) {
|
|
3042
|
+
const salvaged = obj.profiles.filter((item) => SiteProfileSchema.safeParse(item).success);
|
|
3043
|
+
if (salvaged.length > 0) {
|
|
3044
|
+
const state = { profiles: salvaged };
|
|
3045
|
+
this.backupAndReset();
|
|
3046
|
+
this.writeDisk(state);
|
|
3047
|
+
return state;
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
this.backupAndReset();
|
|
3051
|
+
return EMPTY_STATE$1;
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
backupAndReset() {
|
|
3055
|
+
const corruptPath = `${this.filePath}.corrupt-${Date.now()}`;
|
|
3056
|
+
try {
|
|
3057
|
+
fs.copyFileSync(this.filePath, corruptPath);
|
|
3058
|
+
} catch {}
|
|
3059
|
+
}
|
|
3060
|
+
writeDisk(state) {
|
|
3061
|
+
const tempPath = `${this.filePath}.tmp`;
|
|
3062
|
+
fs.writeFileSync(tempPath, JSON.stringify(state, null, 2), "utf8");
|
|
3063
|
+
fs.renameSync(tempPath, this.filePath);
|
|
3064
|
+
}
|
|
3065
|
+
registerProcessHandlers() {
|
|
3066
|
+
const proc = process;
|
|
3067
|
+
const stores = proc[REGISTERED_STORES$1] ??= /* @__PURE__ */ new Set();
|
|
3068
|
+
stores.add(this);
|
|
3069
|
+
if (proc[REGISTERED_EXIT_HANDLERS$1]) return;
|
|
3070
|
+
proc[REGISTERED_EXIT_HANDLERS$1] = true;
|
|
3071
|
+
const flushAll = () => {
|
|
3072
|
+
for (const store of stores) store.flushSync();
|
|
3073
|
+
};
|
|
3074
|
+
process.on("exit", flushAll);
|
|
3075
|
+
process.on("SIGINT", () => {
|
|
3076
|
+
flushAll();
|
|
3077
|
+
process.exit(0);
|
|
3078
|
+
});
|
|
3079
|
+
process.on("SIGTERM", () => {
|
|
3080
|
+
flushAll();
|
|
3081
|
+
process.exit(0);
|
|
3082
|
+
});
|
|
3083
|
+
}
|
|
3084
|
+
};
|
|
2907
3085
|
//#endregion
|
|
2908
3086
|
//#region src/memory/task-insight-store.ts
|
|
2909
3087
|
const EMPTY_STATE = { insights: [] };
|
|
@@ -3030,25 +3208,42 @@ var TaskInsightStore = class {
|
|
|
3030
3208
|
//#endregion
|
|
3031
3209
|
//#region src/memory/memory-service.ts
|
|
3032
3210
|
const SEARCH_CACHE_TTL_MS = 2e3;
|
|
3211
|
+
const MAX_NAVIGATION_PATHS = 20;
|
|
3212
|
+
const MAX_LAYOUT_FINGERPRINTS = 10;
|
|
3213
|
+
const MAX_SELECTOR_PATTERNS = 15;
|
|
3033
3214
|
var MemoryService = class {
|
|
3034
3215
|
store;
|
|
3216
|
+
profileStore;
|
|
3035
3217
|
index;
|
|
3036
3218
|
/** Simple TTL cache for search results keyed by intent+domain+limit. */
|
|
3037
3219
|
searchCache = /* @__PURE__ */ new Map();
|
|
3038
3220
|
constructor(baseDir) {
|
|
3039
3221
|
this.store = new TaskInsightStore(baseDir);
|
|
3222
|
+
this.profileStore = new SiteProfileStore(baseDir);
|
|
3040
3223
|
this.index = new MemoryIndex();
|
|
3041
3224
|
}
|
|
3042
3225
|
/** Force an immediate synchronous flush of pending writes. */
|
|
3043
3226
|
flushSync() {
|
|
3044
3227
|
this.store.flushSync();
|
|
3228
|
+
this.profileStore.flushSync();
|
|
3045
3229
|
}
|
|
3046
3230
|
search(input) {
|
|
3047
3231
|
const cacheKey = `${input.taskIntent}\0${input.siteDomain ?? ""}\0${input.limit ?? 10}`;
|
|
3048
3232
|
const now = Date.now();
|
|
3049
3233
|
const cached = this.searchCache.get(cacheKey);
|
|
3050
3234
|
if (cached && now - cached.ts < SEARCH_CACHE_TTL_MS) return cached.results;
|
|
3051
|
-
const
|
|
3235
|
+
const insights = this.store.list();
|
|
3236
|
+
let mutated = false;
|
|
3237
|
+
for (let i = 0; i < insights.length; i++) {
|
|
3238
|
+
const decayed = applyAgeStaleness(insights[i]);
|
|
3239
|
+
if (decayed.freshness !== insights[i].freshness) {
|
|
3240
|
+
insights[i] = decayed;
|
|
3241
|
+
this.store.upsert(decayed);
|
|
3242
|
+
mutated = true;
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
if (mutated) this.searchCache.clear();
|
|
3246
|
+
const results = this.index.search(insights, input, (domain) => this.profileStore.get(domain));
|
|
3052
3247
|
this.searchCache.set(cacheKey, {
|
|
3053
3248
|
results,
|
|
3054
3249
|
ts: now
|
|
@@ -3092,6 +3287,7 @@ var MemoryService = class {
|
|
|
3092
3287
|
return verified;
|
|
3093
3288
|
}
|
|
3094
3289
|
recordSuccess(input) {
|
|
3290
|
+
this.updateSiteProfile(input.siteDomain, input.selector, input.url, input.sitePathPattern);
|
|
3095
3291
|
const insights = this.store.list();
|
|
3096
3292
|
const matched = this.findBestExactMatch(insights, input);
|
|
3097
3293
|
const evidence = this.createEvidence(input, "success");
|
|
@@ -3149,6 +3345,7 @@ var MemoryService = class {
|
|
|
3149
3345
|
return withAliases;
|
|
3150
3346
|
}
|
|
3151
3347
|
recordFailure(input, errorMessage) {
|
|
3348
|
+
this.updateSiteProfile(input.siteDomain, input.selector, input.url, input.sitePathPattern);
|
|
3152
3349
|
const insights = this.store.list();
|
|
3153
3350
|
const matched = this.findBestExactMatch(insights, input);
|
|
3154
3351
|
if (!matched) return;
|
|
@@ -3163,6 +3360,91 @@ var MemoryService = class {
|
|
|
3163
3360
|
this.invalidateSearchCache();
|
|
3164
3361
|
return failed;
|
|
3165
3362
|
}
|
|
3363
|
+
/** Update site profile from page content (call after getContent with mode="summary"). */
|
|
3364
|
+
updateProfileFromContent(domain, pathPattern, structuredContent) {
|
|
3365
|
+
if (!domain || domain === "unknown") return;
|
|
3366
|
+
const normalized = domain.toLowerCase();
|
|
3367
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3368
|
+
const profile = this.profileStore.get(normalized) ?? this.createEmptyProfile(normalized, now);
|
|
3369
|
+
const fingerprint = {
|
|
3370
|
+
pathPattern,
|
|
3371
|
+
headings: (structuredContent.headings ?? []).slice(0, 8),
|
|
3372
|
+
landmarks: (structuredContent.landmarks ?? []).slice(0, 8),
|
|
3373
|
+
lastSeenAt: now
|
|
3374
|
+
};
|
|
3375
|
+
const fpIndex = profile.layoutFingerprints.findIndex((fp) => fp.pathPattern === pathPattern);
|
|
3376
|
+
if (fpIndex >= 0) profile.layoutFingerprints[fpIndex] = fingerprint;
|
|
3377
|
+
else {
|
|
3378
|
+
profile.layoutFingerprints.push(fingerprint);
|
|
3379
|
+
if (profile.layoutFingerprints.length > MAX_LAYOUT_FINGERPRINTS) profile.layoutFingerprints = profile.layoutFingerprints.sort((a, b) => b.lastSeenAt.localeCompare(a.lastSeenAt)).slice(0, MAX_LAYOUT_FINGERPRINTS);
|
|
3380
|
+
}
|
|
3381
|
+
profile.lastVisitAt = now;
|
|
3382
|
+
this.profileStore.upsert(profile);
|
|
3383
|
+
}
|
|
3384
|
+
/** Update site profile with cookie banner behavior. */
|
|
3385
|
+
updateProfileCookieBanner(domain, result) {
|
|
3386
|
+
if (!domain || domain === "unknown") return;
|
|
3387
|
+
const normalized = domain.toLowerCase();
|
|
3388
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3389
|
+
const profile = this.profileStore.get(normalized) ?? this.createEmptyProfile(normalized, now);
|
|
3390
|
+
profile.cookieBanner = {
|
|
3391
|
+
detected: true,
|
|
3392
|
+
autoDismissed: result.dismissed,
|
|
3393
|
+
method: result.method
|
|
3394
|
+
};
|
|
3395
|
+
profile.lastVisitAt = now;
|
|
3396
|
+
this.profileStore.upsert(profile);
|
|
3397
|
+
}
|
|
3398
|
+
/** Look up a site profile by domain. */
|
|
3399
|
+
getSiteProfile(domain) {
|
|
3400
|
+
return this.profileStore.get(domain);
|
|
3401
|
+
}
|
|
3402
|
+
updateSiteProfile(domain, selector, url, pathPattern) {
|
|
3403
|
+
if (!domain || domain === "unknown") return;
|
|
3404
|
+
const normalized = domain.toLowerCase();
|
|
3405
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3406
|
+
const profile = this.profileStore.get(normalized) ?? this.createEmptyProfile(normalized, now);
|
|
3407
|
+
profile.visitCount += 1;
|
|
3408
|
+
profile.lastVisitAt = now;
|
|
3409
|
+
if (selector) {
|
|
3410
|
+
const pattern = this.classifySelectorPattern(selector);
|
|
3411
|
+
if (pattern) {
|
|
3412
|
+
const existing = profile.selectorPatterns.find((p) => p.pattern === pattern);
|
|
3413
|
+
if (existing) {
|
|
3414
|
+
existing.frequency += 1;
|
|
3415
|
+
existing.lastSeenAt = now;
|
|
3416
|
+
} else if (profile.selectorPatterns.length < MAX_SELECTOR_PATTERNS) profile.selectorPatterns.push({
|
|
3417
|
+
pattern,
|
|
3418
|
+
frequency: 1,
|
|
3419
|
+
lastSeenAt: now
|
|
3420
|
+
});
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
if (pathPattern && pathPattern !== "/") {
|
|
3424
|
+
if (!profile.navigationPaths.includes(pathPattern) && profile.navigationPaths.length < MAX_NAVIGATION_PATHS) profile.navigationPaths.push(pathPattern);
|
|
3425
|
+
}
|
|
3426
|
+
this.profileStore.upsert(profile);
|
|
3427
|
+
}
|
|
3428
|
+
createEmptyProfile(domain, now) {
|
|
3429
|
+
return {
|
|
3430
|
+
domain,
|
|
3431
|
+
selectorPatterns: [],
|
|
3432
|
+
layoutFingerprints: [],
|
|
3433
|
+
navigationPaths: [],
|
|
3434
|
+
visitCount: 0,
|
|
3435
|
+
lastVisitAt: now,
|
|
3436
|
+
createdAt: now
|
|
3437
|
+
};
|
|
3438
|
+
}
|
|
3439
|
+
classifySelectorPattern(selector) {
|
|
3440
|
+
if (selector.includes("[data-testid=")) return "data-testid";
|
|
3441
|
+
if (selector.includes("[data-cy=")) return "data-cy";
|
|
3442
|
+
if (selector.includes("[data-test=")) return "data-test";
|
|
3443
|
+
if (selector.includes("[aria-label=")) return "aria-label";
|
|
3444
|
+
if (selector.includes("[name=")) return "name-attribute";
|
|
3445
|
+
if (/^#[\w-]+$/.test(selector)) return "id-selector";
|
|
3446
|
+
if (selector.includes("[role=")) return "role-attribute";
|
|
3447
|
+
}
|
|
3166
3448
|
findBestExactMatch(insights, input) {
|
|
3167
3449
|
const intentLower = input.taskIntent.toLowerCase();
|
|
3168
3450
|
const domainLower = input.siteDomain.toLowerCase();
|
|
@@ -3251,7 +3533,7 @@ var MemoryService = class {
|
|
|
3251
3533
|
function createAppContext(env = process.env) {
|
|
3252
3534
|
const config = loadConfig(env);
|
|
3253
3535
|
const logger = new Logger("app");
|
|
3254
|
-
const eventStore = new EventStore(config.
|
|
3536
|
+
const eventStore = new EventStore(config.dataDir);
|
|
3255
3537
|
const tokenService = new SessionTokenService();
|
|
3256
3538
|
return {
|
|
3257
3539
|
config,
|
|
@@ -3263,7 +3545,7 @@ function createAppContext(env = process.env) {
|
|
|
3263
3545
|
port: config.wsPort,
|
|
3264
3546
|
tokenService
|
|
3265
3547
|
}),
|
|
3266
|
-
memoryService: new MemoryService(config.
|
|
3548
|
+
memoryService: new MemoryService(config.dataDir)
|
|
3267
3549
|
};
|
|
3268
3550
|
}
|
|
3269
3551
|
//#endregion
|
|
@@ -3332,7 +3614,7 @@ var AgenticBrowserCore = class {
|
|
|
3332
3614
|
};
|
|
3333
3615
|
function createAgenticBrowserCore(options = {}) {
|
|
3334
3616
|
const context = createAppContext(options.env);
|
|
3335
|
-
return new AgenticBrowserCore(context, options.browserController ?? new ChromeCdpBrowserController(context.config.
|
|
3617
|
+
return new AgenticBrowserCore(context, options.browserController ?? new ChromeCdpBrowserController(context.config.dataDir));
|
|
3336
3618
|
}
|
|
3337
3619
|
function createMockAgenticBrowserCore(env) {
|
|
3338
3620
|
return new AgenticBrowserCore(createAppContext(env), new MockBrowserController());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-browser",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
"@semantic-release/github": "^12.0.6",
|
|
54
54
|
"@semantic-release/npm": "^13.1.5",
|
|
55
55
|
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
56
|
-
"@types/node": "^25.
|
|
56
|
+
"@types/node": "^25.4.0",
|
|
57
57
|
"@types/ws": "^8.18.1",
|
|
58
|
-
"oxfmt": "^0.
|
|
59
|
-
"oxlint": "^1.
|
|
58
|
+
"oxfmt": "^0.37.0",
|
|
59
|
+
"oxlint": "^1.52.0",
|
|
60
60
|
"react": "^19.2.4",
|
|
61
61
|
"react-dom": "^19.2.4",
|
|
62
62
|
"semantic-release": "^25.0.3",
|
|
@@ -65,5 +65,5 @@
|
|
|
65
65
|
"vitest": "^4.0.18",
|
|
66
66
|
"vocs": "1.4.1"
|
|
67
67
|
},
|
|
68
|
-
"packageManager": "pnpm@10.
|
|
68
|
+
"packageManager": "pnpm@10.32.0"
|
|
69
69
|
}
|