How Helper Functions Are Injected Into Agent Scripts in Ego‑Lite

Ego‑Lite injects helper functions into agent scripts through a two‑step process: helperContext() creates a unified API object in helpers.ts, then installEgoSdk() copies these helpers onto globalThis in index.ts, making them globally available without imports.

Agent scripts in Ego‑Lite run in isolated execution environments where they need immediate access to browser automation tools like page control, networking, and CDP commands. The framework solves this by pre‑populating the global scope with a curated set of helpers rather than requiring explicit imports. This article explains exactly how that injection mechanism works, tracing the code from source to runtime.


The Two‑Step Injection Architecture

Ego‑Lite's helper injection follows a clean separation between definition and installation. Understanding both phases is essential for debugging, extending, or bypassing the default behavior.

Step 1: Building the Helper Context in helpers.ts

The helperContext() function in package/ego-browser/src/helpers.ts serves as the single source of truth for all public APIs available to agents. It constructs and returns a plain object containing facades for every major subsystem:

export function helperContext(extra: any = {}) {
  const all = {
    page: createPageFacade(),
    browser: createBrowserFacade(),
    taskSpaces: createTaskSpacesFacade(),
    site: createSiteFacade(),
    fetch: { server: serverFetch, browser: browserFetch },
    cdp,
    ...extra,
  };
  return {
    ...all,
    help: (...names) => { /* runtime help() implementation */ }
  };
}

Key properties available through this context include:

  • page – Wrapper around Playwright's Page for navigation, clicking, and DOM queries
  • browser – Browser‑level operations (new pages, contexts, etc.)
  • taskSpaces – Workspace and task management utilities
  • site – Site‑specific configurations and helpers
  • fetch – Isomorphic fetch with server and browser variants
  • cdp – Direct Chrome DevTools Protocol access
  • help() – Runtime introspection for available helpers

The extra parameter allows external callers to augment the context with custom helpers before injection.

Step 2: Installing Onto the Target Runtime in index.ts

The installEgoSdk() function in package/ego-browser/src/index.ts takes the context from step 1 and grafts it onto a target object—typically globalThis:

export function installEgoSdk(
  target: InstallTarget = globalThis,
  options: InstallEgoSdkOptions = {}
) {
  const context = options.context || helpers.helperContext();   // step 1
  for (const [name, value] of Object.entries(context)) {
    const exposed = SYNC_HELPERS.has(name)
      ? value
      : wrapReady(value, readySignal, () => readyError, [name]);
    Object.defineProperty(target, name, {
      value: exposed,
      writable: true,
      configurable: true,
      enumerable: false,
    });
  }
  // expose the helpers on ego (if present) and clean up legacy globals
  if (target.ego && typeof target.ego === "object") {
    target.ego.helpers = installed;
  }
}

Critical implementation details:

  1. Property descriptors – Helpers are installed with enumerable: false to avoid cluttering Object.keys() iterations
  2. Async safety – Non‑sync helpers are wrapped with wrapReady() to defer execution until the runtime signals readiness
  3. Legacy cleanup – The function removes stale global helpers before installation to prevent API drift
  4. Dual exposure – Helpers become available both as direct properties of target and (if ego exists) via target.ego.helpers

How the CLI Triggers Injection for Heredoc Scripts

When running scripts via the Ego‑Lite CLI, the injection happens in package/ego-browser/src/run.ts through the executionContext() function. This code path shows the practical application of the two‑step architecture:

const context = helpers.helperContext(agentHelpers);
Object.assign(globalThis, context);

The agentHelpers parameter allows per‑agent customization of the helper set. After this assignment, any code evaluated in the same realm can reference page, browser, taskSpaces, and all other helpers directly:

await page.goto('https://example.com');
console.log(await page.title());

No import, require, or module declaration is needed—the identifiers exist in the global lexical environment.


Practical Code Examples

Running a Script via CLI (Automatic Injection)

The simplest way to execute an agent script with full helper access:

echo "await page.goto('https://example.com'); console.log(await page.title());" | \
  node ./package/ego-browser/dist/out/index.js

The CLI handles installEgoSdk() invocation internally before evaluating the piped script.

Manual SDK Installation in Node.js

For testing or custom orchestration, install helpers explicitly:

import { installEgoSdk } from './package/ego-browser/src/index.js';

// Install helpers on the global object.
installEgoSdk();

// Helpers are now available globally.
await page.goto('https://example.com');
const title = await page.title();
console.log(title);

This pattern is useful when embedding Ego‑Lite into larger Node.js applications.

Extending Context with Custom Helpers

Inject additional functionality alongside the standard API:

function myHelper(msg) {
  console.log('My helper says:', msg);
}

// Import helperContext to customize the context.
import { helperContext } from './package/ego-browser/src/helpers.js';
import { installEgoSdk } from './package/ego-browser/src/index.js';

// Install with an extra helper.
installEgoSdk(globalThis, { context: helperContext({ myHelper }) });

myHelper('hello');   // works alongside page, browser, …

The ...extra spread in helperContext() ensures custom helpers merge seamlessly with built‑ins.


Helper Injection vs. Module Imports: Design Rationale

Ego‑Lite's global injection approach differs from conventional JavaScript module patterns. The design prioritizes:

Concern Global Injection Approach
Script portability Agents run unchanged across CLI, serverless, and embedded contexts
Reduced ceremony No boilerplate imports for every script
Sandbox isolation Helpers bind to a fresh globalThis per agent, preventing cross‑agent pollution
Dynamic extensibility Runtime helper modification via extra parameter

The trade‑off is intentional namespace pollution of globalThis. Ego‑Lite mitigates this by using enumerable: false and scoped execution contexts.


Summary

  • helperContext() in helpers.ts builds the complete API surface as a plain object with page, browser, taskSpaces, site, fetch, and cdp
  • installEgoSdk() in index.ts copies these properties onto any target object, defaulting to globalThis with non‑enumerable, configurable descriptors
  • run.ts execution spreads the context onto globalThis before evaluating agent scripts, enabling direct helper access without imports
  • Extensibility comes through the extra parameter in helperContext() and the context option in installEgoSdk()

Frequently Asked Questions

What happens if a script defines its own page variable?

The injection uses writable: true in the property descriptor, so a script can reassign page without throwing. However, this shadows the Ego‑Lite facade for that scope. To preserve access, reference globalThis.page explicitly or avoid top‑level declarations of conflicting names.

Can helpers be injected into a non‑global scope?

Yes. Pass any object as the target parameter to installEgoSdk(). This is useful for testing or when running multiple isolated agents in the same Node.js process with separate helper sets.

How does Ego‑Lite prevent helpers from leaking between concurrent scripts?

Each script execution creates a fresh context via executionContext() in run.ts, which calls helperContext() independently. The helpers are bound to that script's globalThis, not a shared singleton. No cross‑script state persists in the helper objects themselves.

Is the cdp helper injected differently from page or browser?

No. All helpers pass through the same installation path in installEgoSdk(). The SYNC_HELPERS set determines whether a helper gets wrapped with wrapReady() for async initialization safety, but this is the only differential treatment in the injection pipeline.

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 →