feat: emit action events for real-time Playwright step visibility#1687
feat: emit action events for real-time Playwright step visibility#1687mainnebula wants to merge 2 commits intobrowserbase:mainfrom
Conversation
…rowserbase#186) Add an event system that lets users observe Playwright actions as they execute, rather than only seeing results after completion. - Add ActionEvent interface (phase, method, selector, description, etc.) - Emit "action" events on the existing bus from ActHandler - Add on()/off() convenience methods to Stagehand class - Cover both primary execution and self-heal retry paths
🦋 Changeset detectedLatest commit: 63533a4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
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 |
Greptile OverviewGreptile SummaryAdds real-time action event visibility by emitting
Issues found:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant Stagehand
participant ActHandler
participant EventBus
participant PerformMethod
User->>Stagehand: on("action", listener)
Stagehand->>EventBus: register listener
User->>Stagehand: act(instruction)
Stagehand->>ActHandler: act(params)
ActHandler->>EventBus: emit("action", {phase: "start"})
EventBus->>User: notify listener (start)
ActHandler->>PerformMethod: performUnderstudyMethod()
alt Success
PerformMethod-->>ActHandler: success
ActHandler->>EventBus: emit("action", {phase: "complete"})
EventBus->>User: notify listener (complete)
else Error (with selfHeal)
PerformMethod-->>ActHandler: error
ActHandler->>EventBus: emit("action", {phase: "error"})
EventBus->>User: notify listener (error)
ActHandler->>ActHandler: self-heal retry
ActHandler->>EventBus: emit("action", {phase: "start"})
ActHandler->>PerformMethod: performUnderstudyMethod(newSelector)
PerformMethod-->>ActHandler: success
ActHandler->>EventBus: emit("action", {phase: "complete"})
EventBus->>User: notify listener (complete)
end
ActHandler-->>Stagehand: ActResult
Stagehand-->>User: ActResult
Last reviewed commit: 95fc7d6 |
There was a problem hiding this comment.
3 issues found across 3 files
Confidence score: 3/5
- There is concrete user-facing risk: action events are only emitted for local executions, so the new
on("action")API is silently non-functional in API mode (packages/core/lib/v3/v3.ts). - Self-heal retry failures/timeouts can return/throw without emitting a terminal error action event, leaving subscribers with a started action and no terminal state (
packages/core/lib/v3/handlers/actHandler.ts). - Overall risk is moderate due to medium-severity eventing gaps that can break consumers’ expectations about action lifecycle notifications.
- Pay close attention to
packages/core/lib/v3/handlers/actHandler.ts,packages/core/lib/v3/v3.ts,packages/core/lib/v3/types/public/methods.ts- action event emission and schema consistency.
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/core/lib/v3/handlers/actHandler.ts">
<violation number="1" location="packages/core/lib/v3/handlers/actHandler.ts:349">
P2: Self-heal retry failures and timeouts return/throw without emitting an `error` action event, leaving a started action without a terminal event for subscribers.</violation>
</file>
<file name="packages/core/lib/v3/v3.ts">
<violation number="1" location="packages/core/lib/v3/v3.ts:696">
P2: Action events are only emitted for local executions; API/BROWSERBASE `act()` calls bypass ActHandler and StagehandAPIClient has no event bus. This makes the new `on("action")` API silently non-functional in API mode, causing inconsistent behavior across environments.</violation>
</file>
<file name="packages/core/lib/v3/types/public/methods.ts">
<violation number="1" location="packages/core/lib/v3/types/public/methods.ts:39">
P3: The `url` field is defined in the `ActionEvent` interface but is never populated in the implementation. Either remove this unused field, or populate it with `page.url()` when emitting action events to provide the promised functionality.</violation>
</file>
Architecture diagram
sequenceDiagram
participant User as User Code
participant SH as Stagehand (V3)
participant Bus as internal: bus (EventEmitter)
participant AH as ActHandler
participant PW as Playwright / Browser
Note over User, Bus: Setup Phase
User->>SH: NEW: on("action", callback)
SH->>Bus: Register listener
Note over User, PW: Execution Phase
User->>SH: act(description)
SH->>AH: execute action logic
AH->>Bus: NEW: emit "action" (phase: "start")
Bus-->>User: Trigger callback with ActionEvent
AH->>PW: performUnderstudyMethod()
alt Action Success
PW-->>AH: success
AH->>Bus: NEW: emit "action" (phase: "complete")
Bus-->>User: Trigger callback
else Action Failure
PW-->>AH: error (e.g. selector not found)
AH->>Bus: NEW: emit "action" (phase: "error", error: msg)
Bus-->>User: Trigger callback
opt Self-Heal Enabled
AH->>AH: Request fallback selector via LLM
Note over AH, Bus: CHANGED: Retry flow also emits events
AH->>Bus: NEW: emit "action" (phase: "start") [retry]
AH->>PW: performUnderstudyMethod() with new selector
alt Retry Success
PW-->>AH: success
AH->>Bus: NEW: emit "action" (phase: "complete")
else Retry Failure
PW-->>AH: error
Note right of AH: Final failure returned to Stagehand
end
end
end
AH-->>SH: Return ActResult
SH-->>User: Result (success/failure)
Since this is your first cubic review, here's how it works:
- cubic automatically reviews your code and comments on bugs and improvements
- Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
- Add one-off context when rerunning by tagging
@cubic-dev-aiwith guidance or docs links (includingllms.txt) - Ask questions if you need clarification on any suggestion
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| @@ -1,10 +1,16 @@ | |||
| // lib/v3/handlers/actHandler.ts | |||
There was a problem hiding this comment.
P2: Self-heal retry failures and timeouts return/throw without emitting an error action event, leaving a started action without a terminal event for subscribers.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/lib/v3/handlers/actHandler.ts, line 349:
<comment>Self-heal retry failures and timeouts return/throw without emitting an `error` action event, leaving a started action without a terminal event for subscribers.</comment>
<file context>
@@ -312,14 +338,19 @@ export class ActHandler {
success: true,
message: `Action [${method}] performed successfully on selector: ${action.selector}`,
- actionDescription: action.description || `action (${method})`,
+ actionDescription,
actions: [
{
</file context>
| inferenceTimeMs, | ||
| ), | ||
| this.domSettleTimeoutMs, | ||
| this.bus, |
There was a problem hiding this comment.
P2: Action events are only emitted for local executions; API/BROWSERBASE act() calls bypass ActHandler and StagehandAPIClient has no event bus. This makes the new on("action") API silently non-functional in API mode, causing inconsistent behavior across environments.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/lib/v3/v3.ts, line 696:
<comment>Action events are only emitted for local executions; API/BROWSERBASE `act()` calls bypass ActHandler and StagehandAPIClient has no event bus. This makes the new `on("action")` API silently non-functional in API mode, causing inconsistent behavior across environments.</comment>
<file context>
@@ -663,6 +693,7 @@ export class V3 {
inferenceTimeMs,
),
this.domSettleTimeoutMs,
+ this.bus,
);
this.extractHandler = new ExtractHandler(
</file context>
| selector: string; | ||
| description: string; | ||
| arguments?: string[]; | ||
| url?: string; |
There was a problem hiding this comment.
P3: The url field is defined in the ActionEvent interface but is never populated in the implementation. Either remove this unused field, or populate it with page.url() when emitting action events to provide the promised functionality.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/core/lib/v3/types/public/methods.ts, line 39:
<comment>The `url` field is defined in the `ActionEvent` interface but is never populated in the implementation. Either remove this unused field, or populate it with `page.url()` when emitting action events to provide the promised functionality.</comment>
<file context>
@@ -30,6 +30,17 @@ export interface Action {
+ selector: string;
+ description: string;
+ arguments?: string[];
+ url?: string;
+ timestamp: number;
+ error?: string;
</file context>
Summary
Closes #186
ActionEventinterface and emits"action"events on the existingbusEventEmitter whenever a Playwright action starts, completes, or errorson()/off()convenience methods to theStagehandclass so users can subscribe without accessingbusdirectlyActHandleragent()callsv3.act()internallyUsage
ActionEvent shape
Test plan
ActionEventtype exported in declaration fileon()/off()methods available onStagehand.prototype"action"events and runact()to confirm events fireThis issue was highlighted as important by Token Steward
Summary by cubic
Adds real-time “action” events for Playwright steps and simple Stagehand.on/off helpers so users can observe start, complete, and error as they happen. Addresses steward/186 by making steps visible during execution, including self-heal retries. Includes a changeset for a minor release.
Written for commit 63533a4. Summary will update on new commits. Review in cubic