How to Add Custom Agent Helpers to ego-browser Using `agent_helpers.js`

To extend ego-browser with custom helpers, create an agent_helpers.js file in your agent workspace directory—the helperContext() function in src/helpers.ts automatically loads and merges its exports into the agent's available helper functions.

The ego-browser package from the citrolabs/ego-lite repository provides a clean extension system for adding domain-specific automation utilities. By placing a specially-named JavaScript file in your agent workspace, you can expose custom functions that agents invoke using the same ergonomic syntax as built-in helpers like click() and navigate().

Understanding the Extension System Architecture

The helper loading mechanism resides in src/helpers.ts at line 853. The helperContext() function constructs the complete set of available helpers by:

  1. Building a base helpers object with built-in functions
  2. Checking for agent_helpers.js in the agent workspace directory
  3. Dynamically importing and merging any exported members
// src/helpers.ts (line 853 vicinity)
const path = join(state.agentWorkspace(), "agent_helpers.js");
if (await exists(path)) {
  const mod = await import(path);
  Object.assign(helpers, mod);
}

This design means custom helpers become first-class citizens—indistinguishable from native helpers in usage patterns.

Creating Your agent_helpers.js File

Required Location

Place agent_helpers.js in the root of your agent workspace. The exact path resolves via state.agentWorkspace(), which typically maps to your configured task directory.


your-workspace/
├── agent_helpers.js    ← Your extension file
├── task-description.md
└── (other assets)

Export Patterns That Work

Named exports (recommended for multiple helpers):

// agent_helpers.js
/**
 * Returns the current page title in uppercase.
 */
export async function pageTitleUpper() {
  const title = await ego.eval(`document.title`);
  return String(title).toUpperCase();
}

/**
 * Clicks an element and waits for navigation with custom timeout.
 */
export async function clickAndWait(selector) {
  await ego.click(selector);
  await ego.waitForNavigation({ timeout: 30 });
}

Default export (useful for namespaced collections):

// agent_helpers.js
export default {
  /** Returns current URL without query parameters. */
  async cleanUrl() {
    const url = await ego.eval('window.location.href');
    return url.split('?')[0];
  },

  /** Checks if element exists without throwing. */
  async hasElement(selector) {
    try {
      await ego.querySelector(selector);
      return true;
    } catch {
      return false;
    }
  }
};

Both patterns merge cleanly into the helper context. The default export's members flatten into the top-level helper namespace.

Using Custom Helpers in Agent Scripts

Once agent_helpers.js exists in your workspace, any agent script can call your functions directly:

// Example agent script passed to ego-browser
await clickAndWait('a.login-button');
const title = await pageTitleUpper();
console.log(`Logged into: ${title}`);

const baseUrl = await cleanUrl();
console.log(`Canonical URL: ${baseUrl}`);

No import statements required—the helpers are pre-bound to the execution context before your script runs.

Understanding Load Timing and Lifecycle

The extension system follows predictable lifecycle rules:

Aspect Behavior
Load frequency Once per agent session initialization
Cache invalidation Changes require task-space reset or new session
Error handling Import failures throw during helper context construction
Scope isolation Custom helpers execute in same sandbox as built-ins

If agent_helpers.js contains syntax errors, helperContext() will propagate the exception during session startup—fail fast, fix early.

Security and Sandboxing Considerations

Custom helpers inherit the same security boundary as native helpers. They cannot:

  • Access the host filesystem outside the workspace directory
  • Execute shell commands or spawn processes
  • Break out of the browser automation sandbox

This preserves ego-browser's security guarantees while enabling powerful domain-specific extensions.

Practical Implementation Checklist

  1. Verify workspace path—confirm state.agentWorkspace() resolves to your expected directory
  2. Use .js extension—the loader specifically checks for agent_helpers.js, not .mjs or .ts
  3. Export async functions—match the Promise-based pattern of built-in helpers for consistency
  4. Document your helpers—JSDoc comments improve maintainability and IDE support
  5. Test incrementally—start with one helper, verify it loads, then expand

Summary

  • agent_helpers.js in your workspace root auto-loads via helperContext() in src/helpers.ts
  • Export functions using named exports or default export—both merge into the helper namespace
  • Custom helpers execute with identical privileges and syntax as built-ins like click() and navigate()
  • The system loads once per session; changes require session reset to take effect
  • Security sandboxing prevents filesystem or process escape regardless of helper provenance

Frequently Asked Questions

What happens if agent_helpers.js is missing or has syntax errors?

If the file doesn't exist, ego-browser proceeds with built-in helpers only—no error. Syntax errors or import failures throw during helperContext() execution, halting session initialization with a stack trace pointing to your file.

Can I use TypeScript for my custom helpers?

No—the loader specifically targets agent_helpers.js and uses dynamic import() on that exact filename. Compile TypeScript to JavaScript beforehand, or use JSDoc type annotations for editor support while keeping the .js extension.

How do I debug why my custom helper isn't appearing?

Verify three things: file is named exactly agent_helpers.js (case-sensitive), file resides in the workspace root returned by state.agentWorkspace(), and you've restarted the agent session since last edit. Add console.log() inside your helper to confirm execution.

Can multiple agent workspaces share the same custom helpers?

Each workspace loads its own agent_helpers.js independently. For shared utilities, maintain a common module elsewhere and re-export from each workspace's agent_helpers.js, or symlink the file across workspaces if your filesystem permits.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →