Skip to content

Add bounded CDP heartbeat and sanitized disconnect diagnostics - #2894

Open
miguelg719 wants to merge 10 commits into
evals/consolidation-03-capture-recoveryfrom
evals/consolidation-04-cdp-diagnostics
Open

Add bounded CDP heartbeat and sanitized disconnect diagnostics#2894
miguelg719 wants to merge 10 commits into
evals/consolidation-03-capture-recoveryfrom
evals/consolidation-04-cdp-diagnostics

Conversation

@miguelg719

@miguelg719 miguelg719 commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Adds configurable single-flight CDP heartbeat with bounded waits and lifecycle cleanup. Sanitized CDP_DROP diagnostics retain transport failure context without logging raw payloads or credentials.

Automatic reconnect is excluded. Error followed by close records the first failure once; heartbeat timers are unrefed and cleared during shutdown.

Nested transport causes are retained in a bounded, sanitized CDP_DROP diagnostic, without changing terminal behavior.

Validation: 49 CDP tests, 44 SDK AST guards and SDK typecheck/format checks; CDP lifecycle/heartbeat/drop-diagnostic unit tests.

Stack position: 4/17. Base: evals/consolidation-03-capture-recovery. Each PR contains the change relative to its immediate predecessor.

Reviewer entry points:

Readiness: local validation is described above; GitHub CI and automated review feedback are pending. This PR is open as non-draft so automated reviewers can run.

@changeset-bot

changeset-bot Bot commented Sep 7, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 05ca9da

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
Name Type
@browserbasehq/stagehand Patch
@browserbasehq/stagehand-integrations Patch
@browserbasehq/stagehand-integrations-example-eve-facade Patch
@browserbasehq/stagehand-integrations-example-pi-facade Patch
@browserbasehq/stagehand-integrations-claude-agent-sdk Patch
@browserbasehq/stagehand-integrations-example-claude-code-facade Patch
@browserbasehq/stagehand-integrations-codex-sdk Patch
@browserbasehq/stagehand-integrations-example-codex-facade Patch
@browserbasehq/stagehand-integrations-cursor-sdk Patch
@browserbasehq/stagehand-integrations-deepagents-sdk Patch
@browserbasehq/stagehand-integrations-eve-sdk Patch
@browserbasehq/stagehand-integrations-fx-sdk Patch
@browserbasehq/stagehand-integrations-mastra-sdk Patch
@browserbasehq/stagehand-integrations-example-mastra-facade Patch
@browserbasehq/stagehand-integrations-pi-sdk Patch
@browserbasehq/stagehand-integrations-example-vercel-ai-facade Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 3 files

Architecture diagram
sequenceDiagram
    participant App as Application
    participant CDP as CDPClient
    participant Sock as WebSocket
    participant Diag as Diagnostics
    participant Env as Environment Config

    Note over App,Env: CDP Connection Lifecycle with Heartbeat

    App->>CDP: new CDPClient(socket, wssUrl)
    CDP->>Env: Read STAGEHAND_CDP_HEARTBEAT_MS
    Env-->>CDP: Interval (default 20s)
    CDP->>CDP: startHeartbeat()

    loop Every heartbeatMs
        CDP->>CDP: Check closed/OPEN/outstanding
        alt No heartbeat pending
            CDP->>CDP: Create AbortController + timeout
            CDP->>Sock: sendCommand("Browser.getVersion")
            Sock-->>CDP: Response (clears pending)
        else Heartbeat pending
            CDP->>CDP: Skip (single-flight)
        end
        Note over CDP: timeout unref'd, min(interval, 10s)
    end

    Note over App,CDP: Normal Command Flow

    App->>CDP: sendCommand(method, params)
    CDP->>Sock: Send JSON-RPC message
    Sock-->>CDP: Message received
    alt Valid message
        CDP-->>App: Resolve response
    else Invalid payload
        CDP->>CDP: finishDrop(error, "error")
    end

    Note over CDP,Diag: Connection Drop Diagnostics

    alt Socket error event
        Sock-->>CDP: error event
        CDP->>CDP: finishDrop(CDPConnectionClosedError, "error")
        opt STAGEHAND_CDP_LOG = "1"
            CDP->>Diag: logDrop() - sanitized metadata
            Diag->>Diag: Redact URLs, tokens, bearer
            Diag-->>CDP: Write stderr + optional file
        end
        CDP->>Sock: close()
        CDP-->>App: onerror(reason) - once
    else Socket close event
        Sock-->>CDP: close event (code, reason)
        CDP->>CDP: finishDrop(CDPConnectionClosedError, "close")
        opt STAGEHAND_CDP_LOG = "1"
            CDP->>Diag: logDrop() - sanitized
        end
        CDP-->>App: onclose(reason)
    end

    Note over CDP: stopHeartbeat() - clear interval<br/>abort pending, clear timers

    alt Commands after close
        App->>CDP: sendCommand()
        CDP-->>App: Reject CDPConnectionClosedError
    end

    Note over App,CDP: Explicit Shutdown

    App->>CDP: close()
    CDP->>CDP: stopHeartbeat() + clear callbacks
    CDP-->>App: Closed (no further events)
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/sdk-ts/src/cdpClient.ts Outdated
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.

2 participants