How the ego-lite Learning Subsystem Discovers and Loads Site-Specific Tools
The ego-lite learning subsystem uses a three-stage pipeline—discovery via siteSkillsForUrl(), context loading via loadLearnedContext(), and dynamic execution—to locate, validate, and run Node-based and browser-based tools tailored to specific websites.
The learning subsystem in ego-lite is the engine that lets AI agents automatically acquire site-specific knowledge and capabilities. When an agent visits a URL, this subsystem identifies what that site can do, loads relevant documentation, and makes specialized tools available through a clean runtime API. This article walks through exactly how discovery, loading, and execution work, with direct references to the source code implementation.
Stage 1: Discovery of Matching Site Skills
The discovery process begins with siteSkillsForUrl(url, options) in package/ego-browser/src/learning/index.ts. This function returns all site-specific skills that apply to a given URL by scanning the learnings directory tree and matching against domain rules.
The internal flow works as follows:
iterLearningDirs(root)walks each site-skill folder under the configuredsiteSkillsRootpathloadLearningManifestloads themanifest.jsonfrom each folderurlHostnamechecks whether the supplied URL's hostname matches the manifest'sdomainsarray
The result is an array of LearningEntry objects, each containing:
- Site ID and display name
- Paths to Markdown notes
- Declared
nodeToolsandbrowserTools
This design lets you organize site skills in a flat directory structure while supporting multiple domains per skill through the manifest's domains field.
Stage 2: Loading Knowledge and Tool Signatures
Once candidate skills are discovered, loadLearnedContext(url, options)—also in src/learning/index.ts—transforms them into an executable context.
For each matching entry, the function performs two parallel tasks:
Knowledge aggregation
- Reads every Markdown file under the
notes/subdirectory - Validates paths with
isLearningNotePath - Builds
LearnedKnowledgeNoteobjects containing the raw content
Tool signature compilation
- Extracts
nodeToolsandbrowserToolsfrom the manifest - Converts each definition into a
LearnedToolSignaturewith description, parameters, return type, and example call
The output is a LearnedContext object containing:
siteIdanddomainknowledge: array of aggregated notestools: map of available tool signatures
This context object is what the helper layer exposes to agents through site.runTool and site.runBrowserTool.
Stage 3: Dynamic Import and Execution
The execution layer handles two distinct tool types with different loading strategies.
Node Tools: Dynamic Import with Cache Busting
Node-based site tools execute in the main process via runNodeSiteTool(siteId, toolName, args, ctx, options):
// Located in src/learning/index.ts
await runNodeSiteTool(
"xcom", // siteId from manifest
"timeline", // tool name in nodeTools
{ limit: 10 }, // arguments object
{ user: "alice" } // execution context
);
The implementation in the source code:
- Calls
findSiteSkill(siteId)to locate the manifest - Extracts the tool schema and resolves the file path via
relativeSitePath - Dynamically imports with cache busting:
import(\${pathToFileURL(toolPath).href}?t=${Date.now()}`)` - Retrieves
schema.callableand invokes it with context and arguments
The timestamp query parameter ensures that updated tool implementations are picked up without restarting the agent.
Browser Tools: Source String Injection
Browser tools execute in the page context via loadBrowserToolSource(siteId, toolName, options):
// Load raw source
const src = await loadBrowserToolSource("google", "search-extract");
// Wrap and execute
const wrapped = wrapBrowserTool(src, { query: "ego-lite" });
const result = await eval(wrapped);
The subsystem:
- Resolves the tool file path using the same
relativeSitePathlogic - Reads the raw JavaScript source
wrapBrowserTool(source, args)embeds it in an async IIFE with argument injection- The agent executes this via
await site.runBrowserTool(url, "toolName", args)
This approach lets browser tools manipulate the DOM and access page globals while keeping the agent's core logic isolated.
Validation and Safety
All manifests pass through strict schema validation before loading:
| Validator | Purpose |
|---|---|
validateLearning |
Validates individual learning format |
validateLearnings |
Validates collections of learnings |
validateSiteSkills |
Validates complete site-skill definitions |
These functions in src/learning/validate-learning-format.ts enforce required fields like name, version, domains, and tool declarations. Invalid manifests are rejected during the discovery phase, preventing malformed site skills from reaching the execution layer.
Integration with Agent Helpers
The learning APIs surface to agents through helperContext() in src/helpers.ts. This wires the discovery and execution functions into the site object that agents interact with:
// Agent-facing API (internally uses the learning subsystem)
await site.runTool(url, "toolName", args); // Node tool
await site.runBrowserTool(url, "toolName", args); // Browser tool
The helper layer handles URL-to-skill resolution automatically, so agents don't need to manage site IDs or manifests directly.
Complete Working Example
Here's a full workflow combining all three stages:
import {
loadLearnedContext,
runNodeSiteTool,
loadBrowserToolSource,
wrapBrowserTool
} from "ego-browser/learning";
// 1. Discover and load context
const ctx = await loadLearnedContext(
"https://x.com/timeline",
{ root: "/my/agent/workspace" }
);
if (ctx.exists) {
console.log("Site:", ctx.siteId);
console.log("Notes:", ctx.knowledge.length);
console.log("Tools:", Object.keys(ctx.tools));
// 2. Run Node tool
const timeline = await runNodeSiteTool(
ctx.siteId,
"fetch-timeline",
{ limit: 20, includeReplies: false },
{ apiKey: process.env.X_API_KEY }
);
// 3. Execute browser tool for page interaction
const source = await loadBrowserToolSource(ctx.siteId, "infinite-scroll");
const scrollResult = await site.runBrowserTool(
"https://x.com/timeline",
"infinite-scroll",
{ maxScrolls: 3 }
);
}
Key Source Files
| File | Role |
|---|---|
package/ego-browser/src/learning/index.ts |
Main API: siteSkillsForUrl, loadLearnedContext, runNodeSiteTool |
package/ego-browser/src/learning/check-domain-learning.ts |
Directory iteration, manifest loading, URL matching |
package/ego-browser/src/learning/validate-learning-format.ts |
Schema validation for manifests and site skills |
package/ego-browser/src/helpers.ts |
Integration with agent helper surface |
skills/ego-browser/learnings/<site>/manifest.json |
Example site-skill definitions |
Summary
The ego-lite learning subsystem implements a robust discover-load-run pipeline for site-specific tooling:
- Discovery walks the learnings directory and matches URLs to manifests via
siteSkillsForUrl() - Loading aggregates notes and compiles tool signatures through
loadLearnedContext() - Execution dynamically imports Node tools with cache busting or injects browser tool source for page-context execution
- Validation enforces schema compliance at every stage to prevent runtime errors
This architecture separates site-specific knowledge from agent logic, enabling extensible, maintainable automation across any web platform.
Frequently Asked Questions
What file structure does ego-lite expect for site skills?
Site skills live in a learnings/ directory with one folder per site. Each folder contains a manifest.json declaring name, domains, nodeTools, browserTools, and a notes/ subdirectory with Markdown documentation. The siteSkillsRoot configuration points to this base directory.
How does ego-lite prevent stale tool code from being used?
For Node tools, runNodeSiteTool() appends ?t=${Date.now()} to the module URL before dynamic import. This cache-busting query parameter forces Node.js to re-evaluate the module on every call. Browser tools are read fresh from disk via loadBrowserToolSource() with no caching layer.
Can a single site skill match multiple domains?
Yes. The domains array in manifest.json supports multiple hostnames and patterns. The urlHostname utility checks whether any supplied URL matches any entry in this array, enabling one skill definition to cover subdomains, regional variants, or related services.
What's the difference between nodeTools and browserTools in the manifest?
nodeTools execute in the Node.js runtime with full system access—ideal for API calls, file operations, or database queries. browserTools execute as JavaScript within the browser page context, giving them access to the DOM, window object, and page-specific globals for scraping or UI automation.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →