Skip to content

Attach: session controls, HITL approval, dry-run#36

Merged
jddunn merged 1 commit into
masterfrom
feat/attach-session-controls
Jul 21, 2026
Merged

Attach: session controls, HITL approval, dry-run#36
jddunn merged 1 commit into
masterfrom
feat/attach-session-controls

Conversation

@jddunn

@jddunn jddunn commented Jul 21, 2026

Copy link
Copy Markdown
Member

Robustness + control features for the attach lane:

  • HITL approval (onNavigate): an approver can deny a policy-clean navigation — per-page operator gating.
  • Dry-run: navigation/reads are validated + reported but not performed — preview a plan or run in CI safely.
  • Pause/resume + richer status (paused, dryRun, navigations, lastUrl).
  • browser_attach_control tool: pause/resume/dry-run at runtime.
  • Pack + env expose dryRun and deadlineMs.
    All unit-tested.

Summary by Sourcery

Add human-in-the-loop and dry-run controls to browser attach sessions, with richer status tracking and runtime tooling.

New Features:

  • Introduce HITL navigation approval hook that can block policy-approved navigations.
  • Add dry-run mode to simulate navigation and reads without touching the browser.
  • Expose session control tool to pause, resume, toggle dry-run, and fetch status at runtime.

Enhancements:

  • Extend attach session status with paused flag, dry-run indicator, navigation count, and last navigated URL.
  • Propagate dry-run and per-operation deadline configuration through browser automation options and pack registration.

Tests:

  • Add unit tests covering HITL approval behavior, dry-run operation and status, pause/resume gating, navigation tracking, tool wiring, and pack registration changes.

Summary by CodeRabbit

  • New Features
    • Added runtime controls to pause, resume, and inspect browser sessions.
    • Added dry-run mode to simulate navigation and reading without browser interaction.
    • Added navigation approval checks before visiting URLs.
    • Added navigation counts and last-visited URL to session status.
    • Added configurable operation deadlines for attach mode.
  • Bug Fixes
    • Prevented browser operations from running while a session is paused.

AttachController gains: an onNavigate HITL approver (can deny a policy-clean
page), dry-run mode (simulate navigation/reads without touching the browser),
pause/resume, and richer status (paused/dryRun/navigations/lastUrl). New
browser_attach_control tool drives pause/resume/dry-run at runtime. Pack +
env expose dryRun and deadlineMs for flexibility.
@sourcery-ai

sourcery-ai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds human-in-the-loop navigation gating, dry-run mode, pause/resume and richer status tracking to the attach controller, exposes a runtime browser_attach_control tool, and threads dryRun/deadline options through the browser-automation pack with full unit test coverage and registration updates.

Sequence diagram for AttachController.goto with HITL approval and dry-run

sequenceDiagram
  actor Agent
  participant AttachController
  participant UrlPolicyEngine
  participant NavigationApprover
  participant AttachBackend

  Agent->>AttachController: goto(url)
  AttachController->>UrlPolicyEngine: isNavigationAllowed(url, urlPolicy)
  UrlPolicyEngine-->>AttachController: verdict
  alt [policy blocked]
    AttachController-->>Agent: AttachError POLICY_BLOCKED
  else [policy allowed]
    alt [onNavigate configured]
      AttachController->>NavigationApprover: onNavigate(url)
      NavigationApprover-->>AttachController: decision
      alt [decision denied]
        AttachController-->>Agent: AttachError POLICY_BLOCKED
      else [decision approved]
        alt [dryRun enabled]
          AttachController-->>Agent: return url
        else [dryRun disabled]
          AttachController->>AttachBackend: gotoTab(tab, url)
          AttachBackend-->>AttachController: landedUrl
          AttachController-->>Agent: return landedUrl
        end
      end
    else [no onNavigate]
      alt [dryRun enabled]
        AttachController-->>Agent: return url
      else [dryRun disabled]
        AttachController->>AttachBackend: gotoTab(tab, url)
        AttachBackend-->>AttachController: landedUrl
        AttachController-->>Agent: return landedUrl
      end
    end
  end
Loading

File-Level Changes

Change Details Files
Introduce HITL navigation approver, dry-run mode, pause/resume controls, and enhanced status metrics in AttachController, affecting goto/read behavior and readiness checks.
  • Add NavigationApprover type and onNavigate option to allow async operator gating after URL policy checks
  • Add dryRun option and internal state (paused, dryRun, navigations, lastUrl) with corresponding getters via status()
  • Implement pause(), resume(), and setDryRun() mutators to control session behavior at runtime
  • Update goto() to apply HITL approval, respect dryRun (simulate navigation, increment counters, set lastUrl) and track navigations/landed URL in status
  • Update read() to short-circuit with a dry-run marker when in dryRun mode
  • Extend requireReady() to block operations while paused with a structured AttachError
registry/curated/system/browser-automation/src/attach/AttachController.ts
Add a browser_attach_control tool to manipulate session state (pause/resume/dry-run) and expose it via the attach tool set and ATTACH_TOOL_IDS.
  • Define AttachControlTool with pause/resume/dry_run_on/dry_run_off/status actions, using controller mutators and returning updated status via envelope()
  • Include AttachControlTool in createAttachTools() and extend ATTACH_TOOL_IDS with browser_attach_control
  • Adjust tools list expectations so attach tools set now has six tools instead of five
registry/curated/system/browser-automation/src/attach/tools.ts
registry/curated/system/browser-automation/src/attach/__tests__/tools.test.ts
Extend browser-automation options and pack registration to thread dryRun and deadlineMs into AttachController construction and tool descriptor registration.
  • Add dryRun and deadlineMs properties to BrowserAutomationOptions.attach configuration
  • Pass through dryRun and deadlineMs from extension pack options into AttachControllerOptions when creating the controller
  • Update pack-registration tests to expect the browser_attach_control tool descriptor among attach tools
registry/curated/system/browser-automation/src/index.ts
registry/curated/system/browser-automation/src/attach/__tests__/pack-registration.test.ts
Add unit tests verifying HITL navigation denial, dry-run behavior, pause/resume gating, and status metrics for navigations/lastUrl in AttachController.
  • Test that onNavigate can deny navigation even when URL policy allows, and that the backend is never called on denial
  • Test dry-run mode: goto returns target URL without backend calls, read returns a dry-run marker, and status reflects dryRun, navigations count, and lastUrl
  • Test pause(): operations throw a paused error until resume() is called, after which navigation proceeds
  • Test that real navigations (non-dry-run) correctly increment navigations and update lastUrl in status
registry/curated/system/browser-automation/src/attach/__tests__/AttachController.test.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Attach mode now supports runtime pause/resume, dry-run navigation and reads, navigation approval hooks, session telemetry, configurable deadlines, and a new control tool for changing these states.

Changes

Attach runtime controls

Layer / File(s) Summary
Controller contracts and attach configuration
registry/curated/system/browser-automation/src/attach/AttachController.ts, registry/curated/system/browser-automation/src/index.ts
Attach options and status expose dry-run, navigation approval, pause state, navigation telemetry, and deadline configuration.
Navigation and session behavior
registry/curated/system/browser-automation/src/attach/AttachController.ts, registry/curated/system/browser-automation/src/attach/__tests__/AttachController.test.ts
Navigation approval, dry-run handling, pause gating, read short-circuiting, and navigation state tracking are implemented and tested.
Runtime control tool integration
registry/curated/system/browser-automation/src/attach/tools.ts, registry/curated/system/browser-automation/src/attach/__tests__/tools.test.ts, registry/curated/system/browser-automation/src/attach/__tests__/pack-registration.test.ts
browser_attach_control supports session actions and is registered, classified, and covered by tool and pack-registration tests.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant AttachController
  participant NavigationApprover
  participant BrowserBackend
  Caller->>AttachController: goto(url)
  AttachController->>NavigationApprover: approve(url)
  NavigationApprover-->>AttachController: approval decision
  AttachController->>BrowserBackend: navigate(url) when not dry-run
  AttachController-->>Caller: target URL
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main attach-session control, HITL approval, and dry-run changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/attach-session-controls

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="registry/curated/system/browser-automation/src/attach/AttachController.ts" line_range="56-58" />
<code_context>
+ * to block the navigation with a `POLICY_BLOCKED` result. Async so a caller can
+ * prompt a real operator.
+ */
+export type NavigationApprover = (
+  url: string,
+) => boolean | { approved: boolean; reason?: string } | Promise<boolean | { approved: boolean; reason?: string }>;
+
 /** Controller configuration. */
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard against `null` decisions from `NavigationApprover` to avoid runtime errors.

In `goto`, `NavigationApprover` results are handled as either boolean or object, but `typeof null === 'object'`. If an approver erroneously returns `null`, the branch `typeof decision === 'object' && decision.reason` will dereference `reason` on `null` and throw. Please add a null-safe check (e.g. `decision && typeof decision === 'object'`) to avoid this runtime error while keeping the existing API.
</issue_to_address>

### Comment 2
<location path="registry/curated/system/browser-automation/src/attach/tools.ts" line_range="138-147" />
<code_context>
+export class AttachControlTool {
</code_context>
<issue_to_address>
**suggestion:** Align the `execute` argument typing with `inputSchema` for extensibility.

`inputSchema` treats `action` as a generic string enum, while `execute` uses a hard-coded union (`'pause' | 'resume' | 'dry_run_on' | 'dry_run_off' | 'status'`). This requires manual updates whenever new actions are added. Define a shared `type` (or derive the union from a constant) so `inputSchema` and `execute` stay in sync automatically.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +56 to +58
export type NavigationApprover = (
url: string,
) => boolean | { approved: boolean; reason?: string } | Promise<boolean | { approved: boolean; reason?: string }>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Guard against null decisions from NavigationApprover to avoid runtime errors.

In goto, NavigationApprover results are handled as either boolean or object, but typeof null === 'object'. If an approver erroneously returns null, the branch typeof decision === 'object' && decision.reason will dereference reason on null and throw. Please add a null-safe check (e.g. decision && typeof decision === 'object') to avoid this runtime error while keeping the existing API.

Comment on lines +138 to +147
export class AttachControlTool {
readonly id = 'browser_attach_control';
readonly name = 'browser_attach_control';
readonly displayName = 'Attach: session control';
readonly description =
'Control the attach session at runtime: pause (refuse further ops), resume, or toggle dry-run (simulate navigation/reads without touching the browser). Returns the updated status.';
readonly category = 'browser';
readonly version = '0.1.0';
readonly hasSideEffects = true;
readonly inputSchema = {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Align the execute argument typing with inputSchema for extensibility.

inputSchema treats action as a generic string enum, while execute uses a hard-coded union ('pause' | 'resume' | 'dry_run_on' | 'dry_run_off' | 'status'). This requires manual updates whenever new actions are added. Define a shared type (or derive the union from a constant) so inputSchema and execute stay in sync automatically.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a805e08c49

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}
// HITL gate: after the policy passes, an approver may still block the page.
if (this.opts.onNavigate) {
const decision = await this.opts.onNavigate(url);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Put async approval under the operation guard

With an async onNavigate implementation, such as one waiting for an operator, this await happens while the controller still reports ready and before the existing deadline-wrapped navigation path starts. In hosts that can dispatch another attach tool call during that prompt, the second call can pass requireReady() and navigate/read the same agent tab before the first approved navigation resumes; if the prompt stalls, deadlineMs also no longer bounds the goto() call. Move the approval wait under a busy state/mutex and deadline, restoring ready on denial.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@registry/curated/system/browser-automation/src/attach/AttachController.ts`:
- Around line 196-204: Update the navigation approval flow in the
AttachController method containing onNavigate to await the decision through
withDeadline using the applicable deadlineMs, preventing an unresolved operator
prompt from blocking indefinitely. After the approval resolves and before
requireLease or any dry-run/backend navigation side effect, call requireReady()
again so pauses issued during the wait are enforced; preserve the existing
denial handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b348fd25-39dc-4e0d-a572-27d040d6022b

📥 Commits

Reviewing files that changed from the base of the PR and between 9363a1b and a805e08.

📒 Files selected for processing (6)
  • registry/curated/system/browser-automation/src/attach/AttachController.ts
  • registry/curated/system/browser-automation/src/attach/__tests__/AttachController.test.ts
  • registry/curated/system/browser-automation/src/attach/__tests__/pack-registration.test.ts
  • registry/curated/system/browser-automation/src/attach/__tests__/tools.test.ts
  • registry/curated/system/browser-automation/src/attach/tools.ts
  • registry/curated/system/browser-automation/src/index.ts

Comment on lines +196 to 204
if (this.opts.onNavigate) {
const decision = await this.opts.onNavigate(url);
const approved = typeof decision === 'boolean' ? decision : decision.approved;
if (!approved) {
const reason = typeof decision === 'object' && decision.reason ? decision.reason : 'operator denied';
throw new AttachError('POLICY_BLOCKED', `navigation to "${url}" denied by approver: ${reason}`);
}
}
requireLease(this.opts.leaseFile, this.lease!.nonce);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bound approval waits and re-check pause state.

Line 197 awaits onNavigate without deadlineMs, so an operator prompt that never resolves leaves goto() stuck indefinitely. A pause issued during that await is also bypassed when approval later resolves. Wrap the decision in withDeadline(...) and call requireReady() again before the dry-run/backend side effect.

Proposed fix
 if (this.opts.onNavigate) {
-  const decision = await this.opts.onNavigate(url);
+  const decision = await withDeadline(
+    Promise.resolve(this.opts.onNavigate(url)),
+    this.deadline,
+    'onNavigate',
+  );
   const approved = typeof decision === 'boolean' ? decision : decision.approved;
   if (!approved) {
     const reason = typeof decision === 'object' && decision.reason ? decision.reason : 'operator denied';
     throw new AttachError('POLICY_BLOCKED', `navigation to "${url}" denied by approver: ${reason}`);
   }
 }
+this.requireReady();
 requireLease(this.opts.leaseFile, this.lease!.nonce);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (this.opts.onNavigate) {
const decision = await this.opts.onNavigate(url);
const approved = typeof decision === 'boolean' ? decision : decision.approved;
if (!approved) {
const reason = typeof decision === 'object' && decision.reason ? decision.reason : 'operator denied';
throw new AttachError('POLICY_BLOCKED', `navigation to "${url}" denied by approver: ${reason}`);
}
}
requireLease(this.opts.leaseFile, this.lease!.nonce);
if (this.opts.onNavigate) {
const decision = await withDeadline(
Promise.resolve(this.opts.onNavigate(url)),
this.deadline,
'onNavigate',
);
const approved = typeof decision === 'boolean' ? decision : decision.approved;
if (!approved) {
const reason = typeof decision === 'object' && decision.reason ? decision.reason : 'operator denied';
throw new AttachError('POLICY_BLOCKED', `navigation to "${url}" denied by approver: ${reason}`);
}
}
this.requireReady();
requireLease(this.opts.leaseFile, this.lease!.nonce);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@registry/curated/system/browser-automation/src/attach/AttachController.ts`
around lines 196 - 204, Update the navigation approval flow in the
AttachController method containing onNavigate to await the decision through
withDeadline using the applicable deadlineMs, preventing an unresolved operator
prompt from blocking indefinitely. After the approval resolves and before
requireLease or any dry-run/backend navigation side effect, call requireReady()
again so pauses issued during the wait are enforced; preserve the existing
denial handling.

@jddunn
jddunn merged commit e4ff2c4 into master Jul 21, 2026
4 checks passed
@jddunn
jddunn deleted the feat/attach-session-controls branch July 21, 2026 05:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant