Skip to content

Self-Healing

The step() method wraps your Playwright code with self-healing. When selectors break or elements change, the agent automatically recovers by finding alternative ways to accomplish your goal.

Why Self-Healing?

Web applications change frequently. Selectors that work today may break tomorrow due to:

  • Updated class names or IDs
  • DOM restructuring
  • Dynamic content changes
  • A/B testing variations

Traditional Playwright tests fail immediately when selectors break. With step(), the agent recovers automatically.

Basic Usage

typescript
const result = await agent.step(
  page,
  async () => {
    await page.click("#submit-btn");
  },
  "Click the submit button",
);

if (result.success) {
  console.log("Action completed");
} else {
  console.log("Failed:", result.details);
}

The three parameters are:

  1. page - Playwright page instance
  2. action - Your Playwright code to execute
  3. description - What you're trying to accomplish (used by the agent if recovery is needed)

How It Works

  1. Your Playwright code runs normally
  2. If it succeeds, step() returns { success: true }
  3. If it throws (selector not found, timeout, etc.), the agent:
    • Analyzes the current page state
    • Uses the description to understand the goal
    • Finds an alternative way to accomplish it
    • Executes the alternative action

The description Parameter

The description is crucial for self-healing. It tells the agent what you're trying to accomplish, not how.

typescript
// Good descriptions - focus on intent
"Click the submit button";
"Enter email in the login form";
"Select the first product from search results";

// Bad descriptions - too implementation-focused
"Click element with id submit-btn";
"Find input and type text";

Controlling Self-Healing with maxSteps

Use maxSteps to allow multi-step recovery for complex scenarios:

typescript
// Multi-step recovery (agent can take multiple actions to recover)
await agent.step(page, action, "Click button", { maxSteps: 5 });

Global Self-Healing Strategy

Set a default strategy when creating the agent:

typescript
import { WebAgent, createAgentContext, VariableStore } from "@shiplightai/sdk-pro";

const agent = new WebAgent(
  createAgentContext({
    model: "claude-haiku-4-5",
    variableStore: new VariableStore(),
  }),
);
  • 'single' - Recover with a single AI action (default)
  • 'multi' - Multi-step recovery for complex scenarios

The maxSteps option in step() overrides the global strategy for that call.

Return Value

step() returns an AgentStepResult:

typescript
interface AgentStepResult {
  success: boolean;
  details?: string;
}

Unlike assert(), step() does not throw on failure. Check result.success to handle failures.

Examples

Wrapping a Single Action

typescript
await agent.step(page, async () => await page.click("#submit-btn"), "Click the submit button");

Wrapping Multiple Actions

typescript
await agent.step(
  page,
  async () => {
    await page.fill("#email", "user@example.com");
    await page.fill("#password", "secret");
    await page.click("#login");
  },
  "Fill login form and submit",
);

Handling Flaky Elements

typescript
// Element appears after animation/loading
await agent.step(
  page,
  async () => await page.click(".dynamic-button"),
  "Click the dynamic button that appears after loading",
  { maxSteps: 5 },
);

Migration from Playwright

Wrap existing Playwright code with step() for gradual adoption:

typescript
// Before: Pure Playwright (brittle)
await page.click("#old-submit-button");

// After: Self-healing wrapper
await agent.step(page, async () => await page.click("#old-submit-button"), "Click the submit button");

This approach lets you:

  • Keep existing selectors when they work
  • Auto-recover when they break
  • Migrate incrementally without rewriting tests

Released under the MIT License.