Skip to content

Commit 4f3977b

Browse files
authored
Merge pull request #220 from PredicateSystems/network_idle
handle search results verification
2 parents 92a2032 + 478d3c1 commit 4f3977b

3 files changed

Lines changed: 99 additions & 1 deletion

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@predicatesystems/runtime",
3-
"version": "1.5.4",
3+
"version": "1.5.5",
44
"description": "TypeScript SDK for Sentience AI Agent Browser Automation",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",

src/agents/planner-executor/planner-executor-agent.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,6 +1156,7 @@ export class PlannerExecutorAgent {
11561156
}
11571157

11581158
// Update current URL
1159+
const urlBeforeAction = currentUrl;
11591160
let urlAfter = await runtime.getCurrentUrl();
11601161
currentUrl = urlAfter;
11611162
fallbackUsed = fallbackUsed || finalOutcome.usedVision;
@@ -1320,6 +1321,19 @@ export class PlannerExecutorAgent {
13201321
success = true;
13211322
}
13221323

1324+
if (
1325+
!success &&
1326+
finalOutcome.status === StepStatus.SUCCESS &&
1327+
this.isSearchNavigationComplete(task, plannerAction, urlBeforeAction, urlAfter)
1328+
) {
1329+
if (this.config.verbose) {
1330+
console.log(
1331+
`[SEARCH-COMPLETE] Search task appears complete after navigation from ${urlBeforeAction} to ${urlAfter}`
1332+
);
1333+
}
1334+
success = true;
1335+
}
1336+
13231337
const isExtractAction =
13241338
plannerAction.action === 'EXTRACT' || plannerAction.action === 'SCROLL_AND_COUNT';
13251339
const taskHasInteractionLocal =
@@ -1703,6 +1717,7 @@ export class PlannerExecutorAgent {
17031717
// Step outcome callback errors are non-blocking
17041718
}
17051719

1720+
const urlBeforeActionResume = currentUrl;
17061721
let urlAfter = await runtime.getCurrentUrl();
17071722
currentUrl = urlAfter;
17081723
fallbackUsed = fallbackUsed || finalOutcome.usedVision;
@@ -1862,6 +1877,19 @@ export class PlannerExecutorAgent {
18621877
success = true;
18631878
}
18641879

1880+
if (
1881+
!success &&
1882+
finalOutcome.status === StepStatus.SUCCESS &&
1883+
this.isSearchNavigationComplete(task, plannerAction, urlBeforeActionResume, urlAfter)
1884+
) {
1885+
if (this.config.verbose) {
1886+
console.log(
1887+
`[SEARCH-COMPLETE] Search task appears complete after navigation from ${urlBeforeActionResume} to ${urlAfter}`
1888+
);
1889+
}
1890+
success = true;
1891+
}
1892+
18651893
const taskHasInteraction =
18661894
/\b(search|navigate|go to|click|add to|fill|submit|login|sign)\b/i.test(task);
18671895
const hasNonExtractAction = this.actionHistory.some(
@@ -5017,6 +5045,73 @@ COUNT:`;
50175045
}
50185046
}
50195047

5048+
private isSearchNavigationComplete(
5049+
task: string,
5050+
plannerAction: StepwisePlannerResponse,
5051+
urlBefore: string,
5052+
urlAfter: string
5053+
): boolean {
5054+
if (plannerAction.action !== 'CLICK' && plannerAction.action !== 'TYPE_AND_SUBMIT') {
5055+
return false;
5056+
}
5057+
5058+
const taskLower = task.toLowerCase();
5059+
5060+
const isSearchTask = /\b(?:search(?:\s+for)?|find|look\s*up)\b/i.test(taskLower);
5061+
if (!isSearchTask) {
5062+
return false;
5063+
}
5064+
5065+
const hasFollowUpAction =
5066+
/\b(?:and|then)\s+(?:add|remove|delete|create|update|fill|submit|buy|purchase|add\s+to\s+cart|checkout|continue)\b/i.test(
5067+
taskLower
5068+
);
5069+
if (hasFollowUpAction) {
5070+
return false;
5071+
}
5072+
5073+
const hasMultiStep = /\b(?:and\s+continue|multi[\s-]?step|then\s+\w+)\b/i.test(taskLower);
5074+
if (hasMultiStep) {
5075+
return false;
5076+
}
5077+
5078+
if (!urlBefore || !urlAfter || urlBefore === urlAfter) {
5079+
return false;
5080+
}
5081+
5082+
try {
5083+
const before = new URL(urlBefore);
5084+
const after = new URL(urlAfter);
5085+
5086+
if (
5087+
before.origin === after.origin &&
5088+
before.pathname === after.pathname &&
5089+
before.search === after.search
5090+
) {
5091+
return false;
5092+
}
5093+
5094+
const domainChanged = before.hostname !== after.hostname;
5095+
const pathChanged =
5096+
before.pathname.replace(/\/+$/, '') !== after.pathname.replace(/\/+$/, '');
5097+
5098+
if (!domainChanged && !pathChanged) {
5099+
return false;
5100+
}
5101+
5102+
const stillOnSearchPage =
5103+
/\/(search|results|listing|s\b|q\b)/i.test(after.pathname) ||
5104+
/[?&](q|query|search|s)=/i.test(after.search);
5105+
if (stillOnSearchPage) {
5106+
return false;
5107+
}
5108+
5109+
return true;
5110+
} catch {
5111+
return false;
5112+
}
5113+
}
5114+
50205115
private normalizeNavigationUrl(url: string): string {
50215116
return url.trim().replace(/\/+$/, '').toLowerCase();
50225117
}

src/agents/planner-executor/prompts.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@ WHEN TO USE DONE:
7878
- "Search and click product" task: DONE only AFTER clicking a product link
7979
- "Search only" task: DONE after search results appear
8080
- "Log in" task: DONE only AFTER the page navigates away from /login
81+
- "Search for X" or "Find X" task: DONE after you have navigated to a result page (URL changed away from search results)
82+
- "Go to X" or "Open X" task: DONE after you are on the target page
8183
- If goal has multiple steps, complete ALL steps before returning DONE
84+
- IMPORTANT: If the previous action was CLICK and the URL changed to a new page (not a search results page), the search task is likely DONE. Return DONE instead of taking more actions.
8285
8386
STEP ORDERING:
8487
- If the task mentions a specific page or URL you are NOT on yet, NAVIGATE there FIRST before doing anything else.

0 commit comments

Comments
 (0)