From 36064c73f7e064b273d0132b091d34423e97adc0 Mon Sep 17 00:00:00 2001 From: SentienceDEV Date: Wed, 20 May 2026 21:15:08 -0700 Subject: [PATCH 1/2] handle search results verification --- package.json | 2 +- .../planner-executor-agent.ts | 91 +++++++++++++++++++ src/agents/planner-executor/prompts.ts | 3 + 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index a2b5e56..ef269b5 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/agents/planner-executor/planner-executor-agent.ts b/src/agents/planner-executor/planner-executor-agent.ts index 560a9e7..fb68d63 100644 --- a/src/agents/planner-executor/planner-executor-agent.ts +++ b/src/agents/planner-executor/planner-executor-agent.ts @@ -1156,6 +1156,7 @@ export class PlannerExecutorAgent { } // Update current URL + const urlBeforeAction = currentUrl; let urlAfter = await runtime.getCurrentUrl(); currentUrl = urlAfter; fallbackUsed = fallbackUsed || finalOutcome.usedVision; @@ -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 = @@ -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; @@ -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( @@ -5017,6 +5045,69 @@ 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|find|look\s*up|go\s*to|navigate\s*to|open|visit|browse)\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)\b/i.test( + taskLower + ); + if (hasFollowUpAction) { + 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(); } diff --git a/src/agents/planner-executor/prompts.ts b/src/agents/planner-executor/prompts.ts index f58f220..c6c05ac 100644 --- a/src/agents/planner-executor/prompts.ts +++ b/src/agents/planner-executor/prompts.ts @@ -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. From 478d3c16b71ec5bcdbb6287db6b90357cb2ff5b6 Mon Sep 17 00:00:00 2001 From: SentienceDEV Date: Wed, 20 May 2026 21:30:08 -0700 Subject: [PATCH 2/2] fix: tighten isSearchNavigationComplete heuristic to avoid false positives Restrict search task pattern to search/find/look-up only (remove open/ visit/browse/go-to which matched non-search tasks like 'Open cart'). Add guards for 'and continue' and multi-step task patterns. --- src/agents/planner-executor/planner-executor-agent.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/agents/planner-executor/planner-executor-agent.ts b/src/agents/planner-executor/planner-executor-agent.ts index fb68d63..f87ec04 100644 --- a/src/agents/planner-executor/planner-executor-agent.ts +++ b/src/agents/planner-executor/planner-executor-agent.ts @@ -5057,20 +5057,24 @@ COUNT:`; const taskLower = task.toLowerCase(); - const isSearchTask = - /\b(search|find|look\s*up|go\s*to|navigate\s*to|open|visit|browse)\b/i.test(taskLower); + 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)\b/i.test( + /\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; }