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

> Learn to extend ego-browser with custom agent_helpers.js files. Easily add new helper functions to your agent workspace and enhance its capabilities.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: how-to-guide
- Published: 2026-08-04

---

**To extend ego-browser with custom helpers, create an [`agent_helpers.js`](https://github.com/citrolabs/ego-lite/blob/main/agent_helpers.js) file in your agent workspace directory—the `helperContext()` function in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/agent_helpers.js) in the agent workspace directory
3. Dynamically importing and merging any exported members

```typescript
// 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`](https://github.com/citrolabs/ego-lite/blob/main/agent_helpers.js) File

### Required Location

Place [`agent_helpers.js`](https://github.com/citrolabs/ego-lite/blob/main/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):

```javascript
// 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):

```javascript
// 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`](https://github.com/citrolabs/ego-lite/blob/main/agent_helpers.js) exists in your workspace, any agent script can call your functions directly:

```javascript
// 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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/agent_helpers.js)** in your workspace root auto-loads via `helperContext()` in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/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`](https://github.com/citrolabs/ego-lite/blob/main/agent_helpers.js) independently. For shared utilities, maintain a common module elsewhere and re-export from each workspace's [`agent_helpers.js`](https://github.com/citrolabs/ego-lite/blob/main/agent_helpers.js), or symlink the file across workspaces if your filesystem permits.