How Site-Specific Learnings Are Loaded from the Skills Directory in ego-lite
Ego-lite loads site-specific learnings (notes, tool definitions, and manifests) from skills/ego-browser/learnings/ at runtime through a dedicated learning subsystem that discovers, validates, and executes domain-specific capabilities.
The ego-lite open-source browser automation framework extends agent capabilities through modular site skills. These learnings live in a structured directory tree and are dynamically discovered based on the current URL, letting agents access domain knowledge and custom tools without hardcoding site logic into the core engine.
Discovery: How ego-lite Finds Relevant Site Skills
The entry point for loading site-specific learnings is siteSkillsForUrl() in package/ego-browser/src/learning/index.ts. This function resolves the site-skills root from the agent workspace and searches for matching manifests.
// From package/ego-browser/src/learning/index.ts
export async function siteSkillsForUrl(url, { agentWorkspace }) {
const siteSkillsRoot = path.join(agentWorkspace, 'skills', 'ego-browser', 'learnings');
// ...looks for a manifest whose domain matches the URL
}
The discovery chain works as follows:
siteSkillsForUrl()– public API exposed via thesitehelpersiteSkillsForUrlCore()– core implementation that scanssiteSkillsRootiterLearningDirs()– traverses theskills/ego-browser/learnings/directoryloadLearningManifest()– parses each<site>/manifest.json
Domain matching logic in src/learning/check-domain-learning.ts determines whether a site's manifest applies to the current URL based on domain patterns declared in the manifest.
Manifest Loading: The Structure of Site Learnings
Each site skill resides in its own subdirectory with a manifest.json file that declares available capabilities. The manifest schema includes:
idandname– site identifier and display namenodeTools– paths to Node.js executable scriptsbrowserTools– paths to browser-executed scriptsnotes– markdown files containing domain knowledge
Example manifest structure from skills/ego-browser/learnings/x-com/manifest.json:
{
"id": "x-com",
"name": "X (Twitter)",
"domain": "x.com",
"nodeTools": ["tools/search-users.js"],
"browserTools": ["tools/extract-timeline.js"],
"notes": ["notes/rate-limits.md", "notes/api-conventions.md"]
}
Validation occurs through src/learning/validate-learning-format.ts, which ensures manifests conform to the expected schema before loading proceeds.
Context Assembly: Building the LearnedContext Object
Once a matching manifest is found, loadLearnedContext() (exposed as site.learnContext) assembles all relevant information into a structured LearnedContext object.
For each site entry, the system:
- Reads every note file under
<site>/notes/*.mdviafs.readFile - Constructs tool signatures for both Node and browser tools
- Attaches ready-to-run examples using
await site.runTool(...)orawait site.runBrowserTool(...)
The resulting LearnedContext contains:
| Property | Description |
|---|---|
exists |
Boolean indicating whether a skill was found |
siteId, siteName, domain |
Identification metadata |
knowledge |
Array of { siteId, fileName, content } objects from note files |
tools |
Array of { siteId, toolName, toolType, description, args, returns, example } objects |
// Using the loaded context in an ego-browser script
const ctx = await site.learnContext(); // Uses current page URL
if (ctx.exists) {
console.log('Site:', ctx.siteName);
console.log('Notes:', ctx.knowledge.map(k => k.fileName));
console.log('Available tools:', ctx.tools.map(t => t.toolName));
}
Tool Execution: Running Node and Browser Tools
The learning subsystem supports two execution environments for site tools.
Node Tools: Server-Side Execution
runNodeSiteTool() in src/learning/index.ts handles Node-side tool execution:
// Executes skills/ego-browser/learnings/x-com/tools/search-users.js
const result = await site.runTool('x-com', 'search-users', {
query: 'john doe'
});
console.log(result);
Implementation details from the source:
- Locates tool file using
relativeSitePath() - Dynamically imports the module
- Retrieves the declared callable function
- Invokes it with the helper context
Browser Tools: In-Page Execution
runSiteBrowserTool() loads and executes code inside the actual browser page:
// Runs a browser-side extraction tool
await site.runBrowserTool('google', 'search-extract', {
query: 'openai gpt-4'
});
The execution flow:
loadBrowserToolSource()fetches raw source as a stringwrapBrowserTool()adds necessary wrappers for the execution environmentevaluate()injects and runs the code in the page context
Helper Facade: The site Namespace
All learning functionality is exposed through the helper context defined in src/helpers.ts. The createSiteFacade() function injects the site namespace into agent scripts:
export function helperContext(extra = {}) {
return {
page: createPageFacade(),
browser: createBrowserFacade(),
site: {
skills: siteSkills,
skillsForUrl: siteSkillsForUrl,
runTool: runSiteTool,
runBrowserTool: runSiteBrowserTool,
learnContext,
},
// …other helpers
};
}
This facade pattern ensures agents interact with a clean, promise-based API regardless of the underlying file system operations and module loading complexity.
Complete Workflow Example
// Full workflow: discover, load context, and execute tools
async function interactWithSite(url) {
// Discovery phase
const skills = await site.skillsForUrl(url);
console.log('Matching site IDs:', skills.map(s => s.id));
// Context loading
const ctx = await site.learnContext();
if (!ctx.exists) {
console.log('No site-specific learning available');
return;
}
// Tool execution based on available capabilities
if (ctx.tools.some(t => t.toolName === 'search-users')) {
const users = await site.runTool(ctx.siteId, 'search-users', { query: 'ego-lite' });
console.log('Found users:', users);
}
}
Key Files in the Learning Subsystem
| Purpose | File Path |
|---|---|
Core learning API (loadLearnedContext, runNodeSiteTool, loadBrowserToolSource) |
package/ego-browser/src/learning/index.ts |
| Manifest validation and schema utilities | package/ego-browser/src/learning/validate-learning-format.ts |
| Domain matching and directory traversal | package/ego-browser/src/learning/check-domain-learning.ts |
Helper facade with site namespace |
package/ego-browser/src/helpers.ts |
| Example manifest (X/Twitter) | skills/ego-browser/learnings/x-com/manifest.json |
| Example Node tool | skills/ego-browser/learnings/x-com/tools/search-users.js |
| Example Browser tool | skills/ego-browser/learnings/google/tools/search-extract.js |
Summary
- Discovery starts at
siteSkillsForUrl(), resolving the skills directory fromstate.agentWorkspace()and matching manifests by domain - Manifest loading via
loadLearningManifest()parsesmanifest.jsonfiles declaring tools and notes - Context assembly in
loadLearnedContext()produces structuredLearnedContextobjects with knowledge arrays and tool signatures - Dual execution paths support Node tools (
runNodeSiteTool) and browser tools (loadBrowserToolSource→wrapBrowserTool→evaluate) - Helper facade exposes all functionality through the
sitenamespace injected into agent scripts
Frequently Asked Questions
Where does ego-lite look for site skills by default?
The default skills root resolves to skills/ego-browser/learnings/ relative to the agent workspace returned by state.agentWorkspace(). This path is constructed in siteSkillsForUrl() using path.join(agentWorkspace, 'skills', 'ego-browser', 'learnings').
What happens if multiple site manifests match a URL?
The siteSkillsForUrl() implementation returns an array of matching entries, allowing multiple skills to apply to overlapping domains. The loadLearnedContext() function aggregates knowledge and tools from all matches, with siteId disambiguating sources.
Can I use site tools without loading the full context?
Yes. While site.learnContext() provides comprehensive discovery, you can call site.runTool() or site.runBrowserTool() directly with explicit site and tool names. These methods internally resolve paths using the same siteSkillsForUrl() discovery logic without requiring a separate context load.
How are browser tools sandboxed during execution?
Browser tools are loaded as raw source strings via loadBrowserToolSource(), wrapped by wrapBrowserTool() to establish the execution environment, and then evaluated through the generic evaluate() helper. This runs within the page context but does not have direct access to the Node.js host environment, maintaining isolation between the agent runtime and the target page.
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 →