feat: isolate new-tab agent navigation from origin tab#593
Conversation
Add origin-aware navigation isolation so the agent never navigates
away from the new-tab chat UI. This is a two-layer defense:
1. Prompt adaptation: When origin is 'newtab', the system prompt's
execution and tool-selection sections are rewritten to prohibit
navigating the active tab and default all lookups to new_page.
2. Tool-level guards: navigate_page and close_page reject attempts
to act on the origin tab when in newtab mode, returning an error
that teaches the agent to self-correct.
The client now sends an `origin` field ('sidepanel' | 'newtab')
instead of injecting a soft NEWTAB_SYSTEM_PROMPT that LLMs could
ignore. Backwards compatible — defaults to 'sidepanel'.
Closes TKT-592, addresses TKT-564
Greptile SummaryThis PR implements a robust two-layer defense against the agent navigating away from the new-tab chat UI (TKT-592). It adds an Confidence Score: 5/5Safe to merge — the change is well-scoped, backward-compatible, and passes all existing tests. The two-layer defense (prompt adaptation + hard tool guards) is architecturally sound. All entry points default to 'sidepanel', so no regressions for scheduled tasks or existing callers. The tool guards are correctly gated on originPageId !== undefined, preventing false positives. The only finding is a trivial dead-code wrapper in promptSections that can be cleaned up independently. prompt.ts — minor redundant wrapper on the tool-selection entry in promptSections Important Files Changed
Sequence DiagramsequenceDiagram
participant Client as useChatSession
participant API as ChatRequestSchema
participant ChatSvc as chat-service
participant Agent as AiSdkAgent
participant Prompt as prompt.ts
participant Tools as tool-adapter
participant Nav as navigate_page / close_page
Client->>API: POST /chat { origin: 'newtab' }
API->>ChatSvc: validated request (origin defaulted to 'sidepanel' if omitted)
ChatSvc->>Agent: ResolvedAgentConfig { origin: 'newtab' }
Agent->>Agent: originPageId = browserContext.activeTab.pageId
Agent->>Prompt: buildSystemPrompt({ origin: 'newtab' })
Note over Prompt: getExecution() injects New-Tab Origin Rules getToolSelection() swaps nav table to new_page-only
Agent->>Tools: buildBrowserToolSet({ origin, originPageId })
Tools->>Nav: ToolContext { session: { origin, originPageId } }
Note over Nav: Guard: if origin==='newtab' && page===originPageId → error
Nav-->>Agent: "Cannot navigate the origin tab…"
Agent-->>Client: self-corrects via prompt or tool error
Prompt To Fix All With AIThis is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/agent/prompt.ts
Line: 720-723
Comment:
**Unnecessary passthrough wrapper for `tool-selection`**
`getToolSelection` was updated to accept `(_exclude: Set<string>, options?: BuildSystemPromptOptions)`, giving it exactly the same signature as `PromptSectionFn`. The wrapper is now a no-op passthrough that adds noise — compare the entry above it, `execution: getExecution`, which is registered directly. Remove the wrapper to keep the table consistent and eliminate dead code.
```suggestion
'tool-selection': getToolSelection,
```
**Rule Used:** Remove unused/dead code rather than leaving it in ... ([source](https://app.greptile.com/review/custom-context?memory=9b045db4-2630-428c-95b7-ccf048d34547))
**Learnt From**
[browseros-ai/BrowserOS-agent#126](https://github.com/browseros-ai/BrowserOS-agent/pull/126)
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "feat: isolate new-tab agent navigation f..." | Re-trigger Greptile |
- 14 new prompt tests verifying the system prompt adapts correctly for newtab vs sidepanel origin (execution rules, tool selection table, absence of conflicting single-tab guidance) - 6 new integration tests for navigate_page and close_page guards: rejects origin tab in newtab mode, allows non-origin tabs, allows all tabs in sidepanel mode, backwards compatible with no session
Summary
originfield ('sidepanel' | 'newtab') to the chat request so the server knows where the session originatedorigin === 'newtab', adapts the system prompt's execution and tool-selection sections to prohibit navigating the active tab and default all lookups tonew_page(background)navigate_pageandclose_pagethat reject attempts to act on the origin tab in newtab mode — returning an error message that teaches the agent to self-correctNEWTAB_SYSTEM_PROMPTclient-side workaround that LLMs could ignoreMotivation
When users chat from the new-tab page, the agent treats the new-tab itself as the "current tab" and navigates it away — destroying the chat UI. The existing mitigation was a soft
userSystemPromptinjection that the core system prompt's navigation instructions overrode, especially with weaker models.This is a two-layer defense: prompt adaptation handles 95% of cases, tool guards catch the rest.
Fixes #441
Changes (9 files)
api/types.tsorigintoChatRequestSchemaagent/types.tsorigintoResolvedAgentConfigchat-service.tsrequest.origin→ configprompt.tsgetExecution()andgetToolSelection()for newtabframework.tsToolSessionContextwithorigin+originPageIdtool-adapter.tsai-sdk-agent.tsnavigation.tsnavigate_page+close_pageagainst origin tabuseChatSession.tsoriginfield, removeNEWTAB_SYSTEM_PROMPTTest plan
navigate_pageon current tab for single-page lookups (no regression)originfield sent = defaults to'sidepanel')