Skip to content

feat: isolate new-tab agent navigation from origin tab#593

Merged
DaniAkash merged 2 commits intomainfrom
feat/newtab-agent-navigation-isolation
Mar 27, 2026
Merged

feat: isolate new-tab agent navigation from origin tab#593
DaniAkash merged 2 commits intomainfrom
feat/newtab-agent-navigation-isolation

Conversation

@DaniAkash
Copy link
Copy Markdown
Contributor

@DaniAkash DaniAkash commented Mar 27, 2026

Summary

  • Adds an origin field ('sidepanel' | 'newtab') to the chat request so the server knows where the session originated
  • When origin === 'newtab', adapts the system prompt's execution and tool-selection sections to prohibit navigating the active tab and default all lookups to new_page (background)
  • Adds tool-level guards on navigate_page and close_page that reject attempts to act on the origin tab in newtab mode — returning an error message that teaches the agent to self-correct
  • Removes the soft NEWTAB_SYSTEM_PROMPT client-side workaround that LLMs could ignore

Motivation

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 userSystemPrompt injection 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)

File Change
api/types.ts Add origin to ChatRequestSchema
agent/types.ts Add origin to ResolvedAgentConfig
chat-service.ts Map request.origin → config
prompt.ts Adapt getExecution() and getToolSelection() for newtab
framework.ts Add ToolSessionContext with origin + originPageId
tool-adapter.ts Pass session context through to tools
ai-sdk-agent.ts Wire origin to prompt builder + tool adapter
navigation.ts Guard navigate_page + close_page against origin tab
useChatSession.ts Send origin field, remove NEWTAB_SYSTEM_PROMPT

Test plan

  • TypeScript compiles clean (server + agent)
  • All 105 existing prompt tests pass
  • All pre-commit hooks pass (biome, conventional commit)
  • Manual: open new tab, search "go to hackernews and read top stories" — verify agent opens background tab, does NOT navigate the new-tab page
  • Manual: verify sidebar chat still uses navigate_page on current tab for single-page lookups (no regression)
  • Manual: verify scheduled tasks still work (no origin field sent = defaults to 'sidepanel')

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-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 27, 2026

Greptile Summary

This PR implements a robust two-layer defense against the agent navigating away from the new-tab chat UI (TKT-592). It adds an origin field ('sidepanel' | 'newtab') that flows from the API request all the way through to the system prompt builder and individual browser tools, replacing a fragile client-side userSystemPrompt injection that weaker LLMs could override.\n\nKey changes:\n- ChatRequestSchema gains an origin field (default 'sidepanel'), preserving full backward compatibility for scheduled tasks and existing clients\n- getExecution() and getToolSelection() in prompt.ts branch on origin === 'newtab' to swap navigation guidance at the prompt level\n- navigate_page and close_page in navigation.ts gain hard tool-level guards that reject calls targeting originPageId in newtab mode, returning a self-correcting error message to the agent\n- originPageId is captured from browserContext.activeTab.pageId at session start and threaded through ToolSessionContext — the guard is safely gated on originPageId !== undefined so it only fires when the origin tab is actually known\n- The soft NEWTAB_SYSTEM_PROMPT client-side workaround is cleanly removed

Confidence Score: 5/5

Safe 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

Filename Overview
packages/browseros-agent/apps/server/src/tools/navigation.ts Adds tool-level guards on navigate_page and close_page; correctly gates on both origin==='newtab' and originPageId!==undefined to avoid false positives.
packages/browseros-agent/apps/server/src/agent/prompt.ts Adapts getExecution and getToolSelection for newtab origin; the tool-selection section's wrapper in promptSections is a redundant passthrough and should be simplified.
packages/browseros-agent/apps/server/src/tools/framework.ts Introduces ToolSessionContext with origin and originPageId; cleanly extends ToolContext without breaking existing callers.
packages/browseros-agent/apps/server/src/agent/ai-sdk-agent.ts Wires originPageId (from browserContext.activeTab.pageId) and origin into both the tool adapter and prompt builder.
packages/browseros-agent/apps/agent/entrypoints/sidepanel/index/useChatSession.ts Removes NEWTAB_SYSTEM_PROMPT soft workaround and replaces it with a typed origin field; origin defaults to 'sidepanel' when not provided.

Sequence Diagram

sequenceDiagram
    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
Loading
Prompt To Fix All With AI
This 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
@DaniAkash DaniAkash merged commit aacb47f into main Mar 27, 2026
8 of 9 checks passed
@DaniAkash DaniAkash deleted the feat/newtab-agent-navigation-isolation branch March 27, 2026 06:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Chat session used it's own tab to navigate away.

1 participant