Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@predicatesystems/runtime",
"version": "1.5.4",
"version": "1.5.5",
"description": "TypeScript SDK for Sentience AI Agent Browser Automation",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
95 changes: 95 additions & 0 deletions src/agents/planner-executor/planner-executor-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,7 @@ export class PlannerExecutorAgent {
}

// Update current URL
const urlBeforeAction = currentUrl;
let urlAfter = await runtime.getCurrentUrl();
currentUrl = urlAfter;
fallbackUsed = fallbackUsed || finalOutcome.usedVision;
Expand Down Expand Up @@ -1320,6 +1321,19 @@ export class PlannerExecutorAgent {
success = true;
}

if (
!success &&
finalOutcome.status === StepStatus.SUCCESS &&
this.isSearchNavigationComplete(task, plannerAction, urlBeforeAction, urlAfter)
) {
if (this.config.verbose) {
console.log(
`[SEARCH-COMPLETE] Search task appears complete after navigation from ${urlBeforeAction} to ${urlAfter}`
);
}
success = true;
}

const isExtractAction =
plannerAction.action === 'EXTRACT' || plannerAction.action === 'SCROLL_AND_COUNT';
const taskHasInteractionLocal =
Expand Down Expand Up @@ -1703,6 +1717,7 @@ export class PlannerExecutorAgent {
// Step outcome callback errors are non-blocking
}

const urlBeforeActionResume = currentUrl;
let urlAfter = await runtime.getCurrentUrl();
currentUrl = urlAfter;
fallbackUsed = fallbackUsed || finalOutcome.usedVision;
Expand Down Expand Up @@ -1862,6 +1877,19 @@ export class PlannerExecutorAgent {
success = true;
}

if (
!success &&
finalOutcome.status === StepStatus.SUCCESS &&
this.isSearchNavigationComplete(task, plannerAction, urlBeforeActionResume, urlAfter)
) {
if (this.config.verbose) {
console.log(
`[SEARCH-COMPLETE] Search task appears complete after navigation from ${urlBeforeActionResume} to ${urlAfter}`
);
}
success = true;
}

const taskHasInteraction =
/\b(search|navigate|go to|click|add to|fill|submit|login|sign)\b/i.test(task);
const hasNonExtractAction = this.actionHistory.some(
Expand Down Expand Up @@ -5017,6 +5045,73 @@ COUNT:`;
}
}

private isSearchNavigationComplete(
task: string,
plannerAction: StepwisePlannerResponse,
urlBefore: string,
urlAfter: string
): boolean {
if (plannerAction.action !== 'CLICK' && plannerAction.action !== 'TYPE_AND_SUBMIT') {
return false;
}

const taskLower = task.toLowerCase();

const isSearchTask = /\b(?:search(?:\s+for)?|find|look\s*up)\b/i.test(taskLower);
if (!isSearchTask) {
return false;
}

const hasFollowUpAction =
/\b(?:and|then)\s+(?:add|remove|delete|create|update|fill|submit|buy|purchase|add\s+to\s+cart|checkout|continue)\b/i.test(
taskLower
);
if (hasFollowUpAction) {
return false;
}

const hasMultiStep = /\b(?:and\s+continue|multi[\s-]?step|then\s+\w+)\b/i.test(taskLower);
if (hasMultiStep) {
return false;
}

if (!urlBefore || !urlAfter || urlBefore === urlAfter) {
return false;
}

try {
const before = new URL(urlBefore);
const after = new URL(urlAfter);

if (
before.origin === after.origin &&
before.pathname === after.pathname &&
before.search === after.search
) {
return false;
}

const domainChanged = before.hostname !== after.hostname;
const pathChanged =
before.pathname.replace(/\/+$/, '') !== after.pathname.replace(/\/+$/, '');

if (!domainChanged && !pathChanged) {
return false;
}

const stillOnSearchPage =
/\/(search|results|listing|s\b|q\b)/i.test(after.pathname) ||
/[?&](q|query|search|s)=/i.test(after.search);
if (stillOnSearchPage) {
return false;
}

return true;
} catch {
return false;
}
}

private normalizeNavigationUrl(url: string): string {
return url.trim().replace(/\/+$/, '').toLowerCase();
}
Expand Down
3 changes: 3 additions & 0 deletions src/agents/planner-executor/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ WHEN TO USE DONE:
- "Search and click product" task: DONE only AFTER clicking a product link
- "Search only" task: DONE after search results appear
- "Log in" task: DONE only AFTER the page navigates away from /login
- "Search for X" or "Find X" task: DONE after you have navigated to a result page (URL changed away from search results)
- "Go to X" or "Open X" task: DONE after you are on the target page
- If goal has multiple steps, complete ALL steps before returning DONE
- 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.

STEP ORDERING:
- If the task mentions a specific page or URL you are NOT on yet, NAVIGATE there FIRST before doing anything else.
Expand Down
Loading