zan-browser 3.0.66 → 3.0.69

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.
Files changed (37) hide show
  1. package/dist/agent/agent.d.ts +0 -1
  2. package/dist/agent/agent.d.ts.map +1 -1
  3. package/dist/agent/agent.js +39 -133
  4. package/dist/agent/agent.js.map +1 -1
  5. package/dist/agent/candidate-evaluator.d.ts +13 -0
  6. package/dist/agent/candidate-evaluator.d.ts.map +1 -0
  7. package/dist/agent/candidate-evaluator.js +223 -0
  8. package/dist/agent/candidate-evaluator.js.map +1 -0
  9. package/dist/agent/enforcement.d.ts +11 -0
  10. package/dist/agent/enforcement.d.ts.map +1 -0
  11. package/dist/agent/enforcement.js +46 -0
  12. package/dist/agent/enforcement.js.map +1 -0
  13. package/dist/agent/extraction.d.ts +11 -0
  14. package/dist/agent/extraction.d.ts.map +1 -0
  15. package/dist/agent/extraction.js +96 -0
  16. package/dist/agent/extraction.js.map +1 -0
  17. package/dist/agent/goal-builder.d.ts +11 -0
  18. package/dist/agent/goal-builder.d.ts.map +1 -0
  19. package/dist/agent/goal-builder.js +57 -0
  20. package/dist/agent/goal-builder.js.map +1 -0
  21. package/dist/agent/orchestrator.d.ts +1 -6
  22. package/dist/agent/orchestrator.d.ts.map +1 -1
  23. package/dist/agent/orchestrator.js +40 -347
  24. package/dist/agent/orchestrator.js.map +1 -1
  25. package/dist/agent/pre-validator.d.ts +19 -0
  26. package/dist/agent/pre-validator.d.ts.map +1 -0
  27. package/dist/agent/pre-validator.js +201 -0
  28. package/dist/agent/pre-validator.js.map +1 -0
  29. package/dist/agent/replicability-checker.d.ts +9 -0
  30. package/dist/agent/replicability-checker.d.ts.map +1 -0
  31. package/dist/agent/replicability-checker.js +91 -0
  32. package/dist/agent/replicability-checker.js.map +1 -0
  33. package/dist/agent/state-updater.d.ts +19 -0
  34. package/dist/agent/state-updater.d.ts.map +1 -0
  35. package/dist/agent/state-updater.js +101 -0
  36. package/dist/agent/state-updater.js.map +1 -0
  37. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extraction.d.ts","sourceRoot":"","sources":["../../src/agent/extraction.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAWD,2EAA2E;AAC3E,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,SAAS,EAAE,EACxB,QAAQ,EAAE,YAAY,EACtB,GAAG,EAAE,WAAW,EAChB,YAAY,EAAE,mBAAmB,GAChC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAgClC"}
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ // ─── Seed page extraction ────────────────────────────────────────────────────
3
+ // Multi-strategy extraction for pages whose URL contains a seed value.
4
+ // Tries scrape → dismiss modal → scrape again → eval_js for SSR data.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.tryExtraction = tryExtraction;
7
+ const DISMISS_KEYWORDS = /cookie|accept|agree|close|dismiss|got it|i understand|consent/i;
8
+ const SSR_GLOBALS = [
9
+ "window.__NEXT_DATA__",
10
+ "window.__NUXT__",
11
+ "window.__APP_STATE__",
12
+ "window.__INITIAL_STATE__",
13
+ ];
14
+ /** Try to extract data from the current page using multiple strategies. */
15
+ async function tryExtraction(pageUrl, _seedValues, registry, ctx, conversation) {
16
+ // Strategy 1: scrape directly
17
+ const scrape1 = await tryScrape(registry, ctx);
18
+ if (scrape1 && scrape1.length > 200) {
19
+ return { data: scrape1, url: pageUrl };
20
+ }
21
+ // Strategy 2: dismiss modal/banner if present, then scrape again
22
+ const observeRes = await tryObserve(registry, ctx);
23
+ if (observeRes) {
24
+ const dismissId = findDismissButton(observeRes);
25
+ if (dismissId) {
26
+ try {
27
+ await registry.execute("click", { elementId: dismissId }, ctx);
28
+ // Brief wait for modal animation
29
+ await new Promise((r) => setTimeout(r, 500));
30
+ }
31
+ catch { /* non-fatal */ }
32
+ const scrape2 = await tryScrape(registry, ctx);
33
+ if (scrape2 && scrape2.length > 200) {
34
+ return { data: scrape2, url: pageUrl };
35
+ }
36
+ }
37
+ }
38
+ // Strategy 3: eval_js for SSR embedded data
39
+ const ssrData = await trySSRExtraction(registry, ctx);
40
+ if (ssrData && ssrData.length > 200) {
41
+ return { data: ssrData, url: pageUrl };
42
+ }
43
+ return null;
44
+ }
45
+ async function tryScrape(registry, ctx) {
46
+ try {
47
+ const res = await registry.execute("scrape", {}, ctx);
48
+ if (res.success) {
49
+ const data = res.data;
50
+ return data?.text ?? (typeof res.data === "string" ? res.data : null);
51
+ }
52
+ }
53
+ catch { /* non-fatal */ }
54
+ return null;
55
+ }
56
+ async function tryObserve(registry, ctx) {
57
+ try {
58
+ const res = await registry.execute("observe", {}, ctx);
59
+ if (res.success) {
60
+ return res.summary;
61
+ }
62
+ }
63
+ catch { /* non-fatal */ }
64
+ return null;
65
+ }
66
+ function findDismissButton(observeSummary) {
67
+ // Parse the ActionSpace output looking for buttons/links with dismiss keywords
68
+ const lines = observeSummary.split("\n");
69
+ for (const line of lines) {
70
+ if (DISMISS_KEYWORDS.test(line)) {
71
+ // Look for element IDs like B1, L2, etc.
72
+ const idMatch = line.match(/\b([BLO]\d+)\b/);
73
+ if (idMatch) {
74
+ return idMatch[1];
75
+ }
76
+ }
77
+ }
78
+ return null;
79
+ }
80
+ async function trySSRExtraction(registry, ctx) {
81
+ const expr = SSR_GLOBALS
82
+ .map((g) => `(typeof ${g} !== 'undefined' ? JSON.stringify(${g}).slice(0, 5000) : null)`)
83
+ .join(" || ");
84
+ try {
85
+ const res = await registry.execute("eval_js", { expression: expr }, ctx);
86
+ if (res.success && res.data) {
87
+ const text = typeof res.data === "string" ? res.data : JSON.stringify(res.data);
88
+ if (text && text !== "null" && text.length > 10) {
89
+ return text;
90
+ }
91
+ }
92
+ }
93
+ catch { /* non-fatal */ }
94
+ return null;
95
+ }
96
+ //# sourceMappingURL=extraction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extraction.js","sourceRoot":"","sources":["../../src/agent/extraction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { SeedValue } from "../types";
2
+ import type { Rejection } from "./orchestrator";
3
+ import type { DiscoveryState } from "./discovery-state";
4
+ export interface GoalBuilderInput {
5
+ originalGoal: string;
6
+ seedValues: SeedValue[];
7
+ rejections: Rejection[];
8
+ state: DiscoveryState;
9
+ }
10
+ export declare function buildEnrichedGoal(input: GoalBuilderInput): string;
11
+ //# sourceMappingURL=goal-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-builder.d.ts","sourceRoot":"","sources":["../../src/agent/goal-builder.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,KAAK,EAAE,cAAc,CAAC;CACvB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CA4DjE"}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ // ─── Goal builder ────────────────────────────────────────────────────────────
3
+ // Builds the enriched goal string injected into each agent attempt.
4
+ // Contains scrape hints, success criteria, seed values, and accumulated context.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildEnrichedGoal = buildEnrichedGoal;
7
+ function buildEnrichedGoal(input) {
8
+ const { originalGoal, seedValues, rejections, state } = input;
9
+ const lines = [];
10
+ // What counts as success — permissive if a prior rejection suggested scrape
11
+ const scrapeHinted = rejections.some((r) => r.reason.toLowerCase().includes("scrape"));
12
+ // If a prior rejection identified a seed page, inject immediate action directive
13
+ if (scrapeHinted) {
14
+ const seedPageMatch = rejections
15
+ .map((r) => r.reason.match(/The page (https?:\/\/\S+) has the seed value/))
16
+ .filter((m) => m !== null).pop();
17
+ if (seedPageMatch) {
18
+ const seedPageUrl = seedPageMatch[1];
19
+ lines.push(`IMMEDIATE ACTION: The previous attempt found that ${seedPageUrl} contains the data rendered in HTML. ` +
20
+ `You are already on ${seedPageUrl}. Observe the page immediately. If the DOM shows the data you need, ` +
21
+ "use scrape as your first action — do not navigate away, do not read network logs first.");
22
+ lines.push("");
23
+ }
24
+ }
25
+ // Core goal
26
+ lines.push(`GOAL: ${originalGoal}`);
27
+ lines.push("");
28
+ if (scrapeHinted) {
29
+ lines.push("SUCCESS CRITERIA: Find a reproducible URL that returns the requested data. " +
30
+ "A JSON API endpoint is preferred, but if the data is only available via HTML rendering, " +
31
+ "scraping a parameterizable page URL (one that contains the seed value in the path) is also " +
32
+ "valid — set data_found=true and declare done.");
33
+ }
34
+ else {
35
+ lines.push("SUCCESS CRITERIA: Find an HTTP endpoint (API, JSON feed, or structured data URL) " +
36
+ "that can be called directly with fetch/curl — no browser session required at runtime. " +
37
+ "DOM extractions, scraped HTML text, and framework data-transport routes (/_next/data/, " +
38
+ "__data.json) do NOT count. The result must be a reproducible URL with a real HTTP response.");
39
+ }
40
+ // Seed values context
41
+ if (seedValues.length > 0) {
42
+ lines.push("");
43
+ lines.push("SEED VALUES (use these verbatim when searching/filling):");
44
+ for (const s of seedValues) {
45
+ lines.push(` • ${s.paramName} (${s.description}): "${s.exampleValue}"`);
46
+ }
47
+ }
48
+ // Accumulated discovery context from shared state
49
+ const stateSummary = state.summarize();
50
+ if (stateSummary) {
51
+ lines.push("");
52
+ lines.push("ACCUMULATED CONTEXT:");
53
+ lines.push(stateSummary);
54
+ }
55
+ return lines.join("\n");
56
+ }
57
+ //# sourceMappingURL=goal-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-builder.js","sourceRoot":"","sources":["../../src/agent/goal-builder.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,oEAAoE;AACpE,iFAAiF;;AAajF,8CA4DC;AA5DD,SAAgB,iBAAiB,CAAC,KAAuB;IACvD,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAC9D,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,4EAA4E;IAC5E,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEvF,iFAAiF;IACjF,IAAI,YAAY,EAAE,CAAC;QACjB,MAAM,aAAa,GAAG,UAAU;aAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;aAC1E,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;QACnC,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,WAAW,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;YACrC,KAAK,CAAC,IAAI,CACR,qDAAqD,WAAW,uCAAuC;gBACvG,sBAAsB,WAAW,sEAAsE;gBACvG,yFAAyF,CAC1F,CAAC;YACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IAED,YAAY;IACZ,KAAK,CAAC,IAAI,CAAC,SAAS,YAAY,EAAE,CAAC,CAAC;IACpC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,IAAI,YAAY,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,CACR,6EAA6E;YAC7E,0FAA0F;YAC1F,6FAA6F;YAC7F,+CAA+C,CAChD,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CACR,mFAAmF;YACnF,wFAAwF;YACxF,yFAAyF;YACzF,6FAA6F,CAC9F,CAAC;IACJ,CAAC;IAED,sBAAsB;IACtB,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAC;QACvE,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,WAAW,OAAO,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAED,kDAAkD;IAClD,MAAM,YAAY,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;IACvC,IAAI,YAAY,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -25,6 +25,7 @@ export interface EndpointResult {
25
25
  reproducible: boolean;
26
26
  whyReproducible: string;
27
27
  goodExamples?: string[];
28
+ notes?: string;
28
29
  }
29
30
  export interface OrchestratorConfig {
30
31
  goal: string;
@@ -55,13 +56,7 @@ export declare class AgentOrchestrator {
55
56
  run(config: OrchestratorConfig): Promise<OrchestratorResult>;
56
57
  /** Inject external feedback from the pipeline (validator, request-selector, etc.) */
57
58
  injectFeedback(rejection: Rejection): void;
58
- private buildEnrichedGoal;
59
- private evaluateResult;
60
- private preValidateEndpoint;
61
- private buildUrlTemplate;
62
- private explainReproducibility;
63
59
  private diagnoseFailure;
64
- private recordFailedEndpoints;
65
60
  private buildFailureSummary;
66
61
  }
67
62
  //# sourceMappingURL=orchestrator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/agent/orchestrator.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAI3D,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,WAAW,GAAG,kBAAkB,GAAG,UAAU,CAAC;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;CACtD;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,GAAG,CAAc;IACzB,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,KAAK,CAAiB;IAG9B,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,SAAS,CAAK;gBAEV,QAAQ,EAAE,YAAY,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,MAAM;IAOrF,GAAG,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAsIlE,qFAAqF;IACrF,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAM1C,OAAO,CAAC,iBAAiB;IA6DzB,OAAO,CAAC,cAAc;YA6JR,mBAAmB;IAsIjC,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,eAAe;IAwBvB,OAAO,CAAC,qBAAqB;IAiB7B,OAAO,CAAC,mBAAmB;CAe5B"}
1
+ {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/agent/orchestrator.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAQ3D,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,WAAW,GAAG,kBAAkB,GAAG,UAAU,CAAC;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;CACtD;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,GAAG,CAAc;IACzB,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,KAAK,CAAiB;IAG9B,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,SAAS,CAAK;gBAEV,QAAQ,EAAE,YAAY,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,MAAM;IAOrF,GAAG,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAwKlE,qFAAqF;IACrF,cAAc,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAM1C,OAAO,CAAC,eAAe;IAwBvB,OAAO,CAAC,mBAAmB;CAe5B"}
@@ -7,14 +7,14 @@
7
7
  // The core insight: the agent's real goal is not "find data" but "find a
8
8
  // reproducible source that works without a browser at runtime". The orchestrator
9
9
  // communicates that distinction and feeds back why previous attempts failed.
10
- var __importDefault = (this && this.__importDefault) || function (mod) {
11
- return (mod && mod.__esModule) ? mod : { "default": mod };
12
- };
13
10
  Object.defineProperty(exports, "__esModule", { value: true });
14
11
  exports.AgentOrchestrator = void 0;
15
- const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
16
12
  const agent_1 = require("./agent");
17
13
  const conversation_1 = require("./conversation");
14
+ const goal_builder_1 = require("./goal-builder");
15
+ const candidate_evaluator_1 = require("./candidate-evaluator");
16
+ const pre_validator_1 = require("./pre-validator");
17
+ const replicability_checker_1 = require("./replicability-checker");
18
18
  // ─── Orchestrator ────────────────────────────────────────────────────────────
19
19
  class AgentOrchestrator {
20
20
  registry;
@@ -53,7 +53,12 @@ class AgentOrchestrator {
53
53
  this.ctx.interceptor?.clear();
54
54
  }
55
55
  // 1. Build enriched goal with accumulated context
56
- const enrichedGoal = this.buildEnrichedGoal();
56
+ const enrichedGoal = (0, goal_builder_1.buildEnrichedGoal)({
57
+ originalGoal: this.originalGoal,
58
+ seedValues: this.seedValues,
59
+ rejections: this.state.rejections,
60
+ state: this.state,
61
+ });
57
62
  // 2. Fresh conversation for each attempt
58
63
  const conversation = new conversation_1.ConversationHistory();
59
64
  // 2b. If a prior rejection identified a seed page, navigate there before the loop starts
@@ -79,7 +84,12 @@ class AgentOrchestrator {
79
84
  priorContext: attempt > 0 ? this.state.summarize() : undefined,
80
85
  });
81
86
  // 4. Evaluate result — is it a reproducible endpoint?
82
- const endpoint = this.evaluateResult(agentResult);
87
+ const endpoint = (0, candidate_evaluator_1.evaluateResult)({
88
+ agentResult,
89
+ state: this.state,
90
+ seedValues: this.seedValues,
91
+ ctx: this.ctx,
92
+ });
83
93
  const record = {
84
94
  agentResult,
85
95
  goalGiven: enrichedGoal,
@@ -100,9 +110,32 @@ class AgentOrchestrator {
100
110
  totalTime: Date.now() - this.startTime,
101
111
  };
102
112
  }
113
+ // Replicability check — skip pre-validation for non-replicable endpoints
114
+ const replicability = (0, replicability_checker_1.checkReplicability)(endpoint.request, this.seedValues);
115
+ if (!replicability.replicable) {
116
+ progress(`Endpoint not replicable: ${replicability.reason}`);
117
+ record.rejectionReason = `Endpoint not replicable: ${replicability.reason}`;
118
+ this.state.triedEndpoints.set(endpoint.request.url, replicability.reason);
119
+ this.state.rejections.push({
120
+ source: "validator",
121
+ reason: `Endpoint ${endpoint.request.url} rejected: ${replicability.reason}`,
122
+ capturedUrl: endpoint.request.url,
123
+ });
124
+ continue;
125
+ }
126
+ if (replicability.notes) {
127
+ endpoint.notes = replicability.notes;
128
+ }
103
129
  // Pre-validate: confirm endpoint returns data for different inputs
104
130
  progress(`Pre-validating endpoint: ${endpoint.urlTemplate}`);
105
- const validation = await this.preValidateEndpoint(endpoint, progress);
131
+ const validation = await (0, pre_validator_1.preValidateEndpoint)({
132
+ endpoint,
133
+ seedValues: this.seedValues,
134
+ originalGoal: this.originalGoal,
135
+ ctx: this.ctx,
136
+ state: this.state,
137
+ progress,
138
+ });
106
139
  if (validation.valid) {
107
140
  endpoint.goodExamples = validation.goodExamples;
108
141
  progress(`Attempt ${attempt + 1}: endpoint validated → ${endpoint.urlTemplate}`);
@@ -148,333 +181,6 @@ class AgentOrchestrator {
148
181
  this.state.rejections.push(rejection);
149
182
  }
150
183
  // ─── Private ───────────────────────────────────────────────────────────────
151
- buildEnrichedGoal() {
152
- const lines = [];
153
- // What counts as success — permissive if a prior rejection suggested scrape
154
- const scrapeHinted = this.state.rejections.some((r) => r.reason.toLowerCase().includes("scrape"));
155
- // If a prior rejection identified a seed page, inject immediate action directive
156
- if (scrapeHinted) {
157
- const seedPageMatch = this.state.rejections
158
- .map((r) => r.reason.match(/The page (https?:\/\/\S+) has the seed value/))
159
- .filter((m) => m !== null).pop();
160
- if (seedPageMatch) {
161
- const seedPageUrl = seedPageMatch[1];
162
- lines.push(`IMMEDIATE ACTION: The previous attempt found that ${seedPageUrl} contains the data rendered in HTML. ` +
163
- `You are already on ${seedPageUrl}. Observe the page immediately. If the DOM shows the data you need, ` +
164
- "use scrape as your first action — do not navigate away, do not read network logs first.");
165
- lines.push("");
166
- }
167
- }
168
- // Core goal
169
- lines.push(`GOAL: ${this.originalGoal}`);
170
- lines.push("");
171
- if (scrapeHinted) {
172
- lines.push("SUCCESS CRITERIA: Find a reproducible URL that returns the requested data. " +
173
- "A JSON API endpoint is preferred, but if the data is only available via HTML rendering, " +
174
- "scraping a parameterizable page URL (one that contains the seed value in the path) is also " +
175
- "valid — set data_found=true and declare done.");
176
- }
177
- else {
178
- lines.push("SUCCESS CRITERIA: Find an HTTP endpoint (API, JSON feed, or structured data URL) " +
179
- "that can be called directly with fetch/curl — no browser session required at runtime. " +
180
- "DOM extractions, scraped HTML text, and framework data-transport routes (/_next/data/, " +
181
- "__data.json) do NOT count. The result must be a reproducible URL with a real HTTP response.");
182
- }
183
- // Seed values context
184
- if (this.seedValues.length > 0) {
185
- lines.push("");
186
- lines.push("SEED VALUES (use these verbatim when searching/filling):");
187
- for (const s of this.seedValues) {
188
- lines.push(` • ${s.paramName} (${s.description}): "${s.exampleValue}"`);
189
- }
190
- }
191
- // Accumulated discovery context from shared state
192
- const stateSummary = this.state.summarize();
193
- if (stateSummary) {
194
- lines.push("");
195
- lines.push("ACCUMULATED CONTEXT:");
196
- lines.push(stateSummary);
197
- }
198
- return lines.join("\n");
199
- }
200
- evaluateResult(agentResult) {
201
- // Build set of URLs the agent explicitly tested via fetch_url with success
202
- const verifiedUrls = new Set();
203
- for (const step of agentResult.steps) {
204
- if (step.toolName === "fetch_url" && step.result.success && step.params.url) {
205
- verifiedUrls.add(String(step.params.url));
206
- }
207
- }
208
- const candidates = [];
209
- const allCaptured = [
210
- ...agentResult.capturedRequests,
211
- ...(this.ctx.interceptor?.getAll() ?? []),
212
- ];
213
- // Deduplicate by URL
214
- const seenUrls = new Set();
215
- // Path 1: fetch_url verified endpoints with score >= 50
216
- for (const url of verifiedUrls) {
217
- const match = allCaptured.find((r) => r.url === url && !r.isDomExtraction && !r.isAsset && r.score >= 50);
218
- if (match && !seenUrls.has(url)) {
219
- candidates.push(match);
220
- seenUrls.add(url);
221
- }
222
- }
223
- // Path 2: high-score XHRs from browser navigation (THE CRITICAL FIX)
224
- // Accepts XHRs captured by navigating and interacting — not just fetch_url verified
225
- for (const r of allCaptured) {
226
- if (r.score >= 60 &&
227
- !r.isDomExtraction &&
228
- !r.isAsset &&
229
- r.isXHR &&
230
- !this.state.triedEndpoints.has(r.url) &&
231
- !seenUrls.has(r.url)) {
232
- candidates.push(r);
233
- seenUrls.add(r.url);
234
- }
235
- }
236
- // Path 3: successful fetch_url in the last 5 steps of a successful run
237
- // fetch_url bypasses the browser — its results never appear in the interceptor.
238
- // If the agent succeeded and a recent fetch_url returned data, accept it directly.
239
- if (agentResult.success) {
240
- const recentSteps = agentResult.steps.slice(-5).reverse();
241
- for (const step of recentSteps) {
242
- if (step.toolName === "fetch_url" &&
243
- step.result.success &&
244
- step.params.url &&
245
- !seenUrls.has(String(step.params.url)) &&
246
- !this.state.triedEndpoints.has(String(step.params.url))) {
247
- const fetchUrl = String(step.params.url);
248
- const fetchData = step.result.data;
249
- const bodyLen = fetchData?.body?.length ?? 0;
250
- // Only accept if it returned substantial content
251
- if (bodyLen > 100) {
252
- const synthetic = {
253
- id: `fetch_url_${Date.now()}`,
254
- timestamp: Date.now(),
255
- method: String(step.params.method ?? "GET"),
256
- url: fetchUrl,
257
- requestHeaders: {},
258
- requestBody: step.params.body ? String(step.params.body) : undefined,
259
- status: fetchData?.status ?? 200,
260
- responseHeaders: {},
261
- responseBody: fetchData?.body,
262
- contentType: fetchData?.contentType ?? "",
263
- isXHR: false,
264
- isAsset: false,
265
- score: 75,
266
- isDomExtraction: false,
267
- };
268
- candidates.push(synthetic);
269
- seenUrls.add(fetchUrl);
270
- }
271
- }
272
- }
273
- }
274
- // Path 4: successful scrape on a page whose URL contains a seed value
275
- const seeds = this.seedValues.map((s) => s.exampleValue.toLowerCase());
276
- for (let i = 0; i < agentResult.steps.length; i++) {
277
- const step = agentResult.steps[i];
278
- if (step.toolName === "scrape" && step.result.success) {
279
- // Find the most recent navigate before this scrape
280
- const pageUrl = [...agentResult.steps]
281
- .slice(0, i)
282
- .reverse()
283
- .find((s) => s.toolName === "navigate" && s.result.success && s.params.url)
284
- ?.params.url;
285
- if (pageUrl && seeds.some((sv) => pageUrl.toLowerCase().includes(sv))) {
286
- const scrapeData = step.result.data;
287
- if (scrapeData?.text && scrapeData.text.length > 100) {
288
- const syntheticUrl = String(pageUrl);
289
- if (!seenUrls.has(syntheticUrl)) {
290
- candidates.push({
291
- id: `scrape_${Date.now()}`,
292
- timestamp: Date.now(),
293
- method: "GET",
294
- url: syntheticUrl,
295
- requestHeaders: {},
296
- status: 200,
297
- responseHeaders: {},
298
- responseBody: scrapeData.text,
299
- contentType: "text/html",
300
- isXHR: false,
301
- isAsset: false,
302
- score: 70,
303
- isDomExtraction: true,
304
- });
305
- seenUrls.add(syntheticUrl);
306
- }
307
- }
308
- }
309
- }
310
- }
311
- // Exclude endpoints that already failed or were pre-validated in previous attempts
312
- const viable = candidates.filter((r) => !this.state.triedEndpoints.has(r.url) &&
313
- !this.state.preValidatedEndpoints.has(r.url));
314
- if (viable.length === 0) {
315
- this.recordFailedEndpoints(agentResult);
316
- return null;
317
- }
318
- // Prefer endpoints where seed value appears in URL (parameterizable), then by score
319
- viable.sort((a, b) => {
320
- const aHasSeed = seeds.some((sv) => a.url.toLowerCase().includes(sv));
321
- const bHasSeed = seeds.some((sv) => b.url.toLowerCase().includes(sv));
322
- if (aHasSeed && !bHasSeed)
323
- return -1;
324
- if (!aHasSeed && bHasSeed)
325
- return 1;
326
- return b.score - a.score;
327
- });
328
- const best = viable[0];
329
- // Build URL template by replacing seed values
330
- const urlTemplate = this.buildUrlTemplate(best.url);
331
- return {
332
- request: best,
333
- urlTemplate,
334
- score: best.score,
335
- reproducible: true,
336
- whyReproducible: this.explainReproducibility(best),
337
- };
338
- }
339
- async preValidateEndpoint(endpoint, progress) {
340
- this.state.preValidatedEndpoints.add(endpoint.request.url);
341
- const template = endpoint.urlTemplate;
342
- const hasParams = template.includes("{");
343
- // Build test URL sets from seed values
344
- const paramSets = [];
345
- if (hasParams && this.seedValues.length > 0) {
346
- // Primary set: exampleValues
347
- const primary = new Map();
348
- for (const s of this.seedValues) {
349
- primary.set(s.paramName, s.exampleValue);
350
- }
351
- paramSets.push({ label: this.seedValues.map((s) => s.exampleValue).join(", "), values: primary });
352
- // Alternative sets from alternativeExamples
353
- const maxAlternatives = 3;
354
- // Find the max number of alternative examples across all seeds
355
- const altCount = Math.min(maxAlternatives, Math.max(...this.seedValues.map((s) => s.alternativeExamples?.length ?? 0)));
356
- for (let i = 0; i < altCount; i++) {
357
- const alt = new Map();
358
- for (const s of this.seedValues) {
359
- const altVal = s.alternativeExamples?.[i];
360
- alt.set(s.paramName, altVal ?? s.exampleValue);
361
- }
362
- paramSets.push({ label: [...alt.values()].join(", "), values: alt });
363
- }
364
- }
365
- else {
366
- // No params or no seeds — test the URL as-is
367
- paramSets.push({ label: "original", values: new Map() });
368
- }
369
- // Limit to 4 sets total
370
- const testSets = paramSets.slice(0, 4);
371
- // Fetch each URL
372
- const results = [];
373
- for (let i = 0; i < testSets.length; i++) {
374
- const set = testSets[i];
375
- let testUrl = template;
376
- for (const [param, value] of set.values) {
377
- testUrl = testUrl.split(`{${param}}`).join(value);
378
- }
379
- progress(`Pre-validate [${i + 1}/${testSets.length}]: ${set.label}`);
380
- try {
381
- const resp = await fetch(testUrl, {
382
- signal: AbortSignal.timeout(10_000),
383
- headers: { "User-Agent": "Mozilla/5.0 (compatible; ZanBrowser/1.0)" },
384
- });
385
- const body = await resp.text();
386
- results.push({
387
- label: set.label,
388
- url: testUrl,
389
- status: resp.status,
390
- bodyPreview: body.slice(0, 500),
391
- });
392
- }
393
- catch (err) {
394
- results.push({
395
- label: set.label,
396
- url: testUrl,
397
- status: 0,
398
- bodyPreview: "",
399
- error: err.message,
400
- });
401
- }
402
- // Rate limit pause between calls (skip after last)
403
- if (i < testSets.length - 1) {
404
- await new Promise((r) => setTimeout(r, 5000));
405
- }
406
- }
407
- // LLM reasoning call (Haiku — lightweight)
408
- const client = new sdk_1.default({ apiKey: this.ctx.anthropicApiKey });
409
- const llmPrompt = `You are validating whether an API endpoint is generic and returns real data for different inputs.
410
-
411
- GOAL: ${this.originalGoal}
412
- ENDPOINT TEMPLATE: ${template}
413
-
414
- TEST RESULTS:
415
- ${results.map((r) => `- Input: ${r.label}\n URL: ${r.url}\n Status: ${r.status}${r.error ? ` (error: ${r.error})` : ""}\n Body preview: ${r.bodyPreview.slice(0, 300)}`).join("\n\n")}
416
-
417
- Answer these questions:
418
- 1. Do the results confirm this endpoint returns real data relevant to the goal?
419
- 2. Are null/empty responses expected (e.g. a product that doesn't exist) or do they indicate the endpoint is wrong?
420
- 3. Is there enough evidence that the endpoint is generic (works for different inputs)?
421
-
422
- Respond ONLY with valid JSON:
423
- {"valid": true/false, "reason": "one sentence explanation", "goodExamples": ["values that returned real data"]}`;
424
- try {
425
- const resp = await client.messages.create({
426
- model: "claude-sonnet-4-6",
427
- max_tokens: 400,
428
- messages: [{ role: "user", content: llmPrompt }],
429
- });
430
- const text = resp.content[0].type === "text" ? resp.content[0].text : "{}";
431
- // Extract JSON from response (handle possible markdown wrapping)
432
- const jsonMatch = text.match(/\{[\s\S]*\}/);
433
- if (jsonMatch) {
434
- const parsed = JSON.parse(jsonMatch[0]);
435
- return {
436
- valid: parsed.valid === true,
437
- reason: String(parsed.reason ?? "unknown"),
438
- goodExamples: Array.isArray(parsed.goodExamples) ? parsed.goodExamples : [],
439
- };
440
- }
441
- }
442
- catch (err) {
443
- progress(`Pre-validation LLM error: ${err.message}`);
444
- }
445
- // If LLM fails, check if at least one fetch returned 200 with substantial body
446
- const anySuccess = results.some((r) => r.status >= 200 && r.status < 400 && r.bodyPreview.length > 100);
447
- return {
448
- valid: anySuccess,
449
- reason: anySuccess ? "LLM validation failed but at least one fetch returned data" : "Could not validate endpoint",
450
- goodExamples: results.filter((r) => r.status >= 200 && r.status < 400 && r.bodyPreview.length > 100).map((r) => r.label),
451
- };
452
- }
453
- buildUrlTemplate(url) {
454
- let template = url;
455
- for (const seed of this.seedValues) {
456
- if (template.includes(seed.exampleValue)) {
457
- template = template.split(seed.exampleValue).join(`{${seed.paramName}}`);
458
- }
459
- }
460
- return template;
461
- }
462
- explainReproducibility(req) {
463
- const parts = [];
464
- if (req.isXHR)
465
- parts.push("real XHR/fetch request");
466
- if (req.contentType?.includes("json"))
467
- parts.push("returns JSON");
468
- if (req.score >= 80)
469
- parts.push("high relevance score");
470
- if (req.method === "GET")
471
- parts.push("stateless GET — no session dependency");
472
- if (req.method === "POST" && req.requestBody)
473
- parts.push("POST with known payload structure");
474
- return parts.length > 0
475
- ? `Reproducible: ${parts.join(", ")}`
476
- : "Captured as a real network request with structured response";
477
- }
478
184
  diagnoseFailure(agentResult) {
479
185
  if (!agentResult.success) {
480
186
  return agentResult.reason;
@@ -494,19 +200,6 @@ Respond ONLY with valid JSON:
494
200
  }
495
201
  return "Captured requests did not meet reproducibility criteria";
496
202
  }
497
- recordFailedEndpoints(agentResult) {
498
- // Record fetch_url attempts that didn't produce a good result
499
- for (const step of agentResult.steps) {
500
- if (step.toolName === "fetch_url" && step.params.url) {
501
- const url = String(step.params.url);
502
- if (!this.state.triedEndpoints.has(url)) {
503
- this.state.triedEndpoints.set(url, step.result.success
504
- ? "returned data but not structured/useful enough"
505
- : String(step.result.error ?? "request failed"));
506
- }
507
- }
508
- }
509
- }
510
203
  buildFailureSummary() {
511
204
  const parts = [];
512
205
  parts.push(`Exhausted ${this.state.attempts.length} attempts (${this.state.totalSteps} total steps)`);