# Understanding the Ego‑Lite Learning Subsystem for Site‑Specific Knowledge Management

> Explore the Ego-Lite learning subsystem and its three-stage pipeline for acquiring site-specific knowledge. Learn how it manages notes, tool signatures, and helper functions without hard-coding.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: deep-dive
- Published: 2026-08-28

---

**The Ego‑Lite learning subsystem provides a three‑stage pipeline—discovery, domain matching, and execution—that lets agents acquire reusable, site‑specific knowledge (notes, tool signatures, and helper functions) without hard‑coding anything in agent scripts.**

The `citrolabs/ego-lite` repository contains a sophisticated **learning subsystem** located under `package/ego-browser/src/learning`. This subsystem enables AI agents to automatically discover, load, and execute site‑specific capabilities packaged as "learning packs." Rather than embedding site logic directly in agent code, developers ship declarative knowledge bundles that the runtime discovers and validates on demand.

## How the Learning Subsystem Architecture Works

The learning subsystem follows a clear separation of concerns across three functional stages. Each stage handles a distinct responsibility in the lifecycle of site‑specific knowledge.

### Stage 1: Discovery and Indexing with `iterLearningDirs`

The entry point for all learning operations begins with filesystem scanning. The `iterLearningDirs` function traverses the **learnings root** directory (conventionally `<workspace>/learnings`) and identifies sub‑folders representing individual site skills.

```typescript
// Conceptual flow based on check-domain-learning.ts#L34-L44
import { iterLearningDirs } from 'ego-browser';

// Scans <workspace>/learnings for valid site skill folders
for await (const skillPath of iterLearningDirs()) {
  // Each folder must contain manifest.json
  const manifest = await loadManifest(skillPath);
}

```

Every folder must contain a [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) file declaring the site's identity, target domains, available notes, and tool definitions. The subsystem enforces this structure to ensure discoverability and interoperability.

### Stage 2: Domain Matching and Context Loading

Once directories are indexed, the subsystem matches URLs against available site skills. The `siteSkillsForUrl` function extracts hostnames via `urlHostname` and evaluates domain patterns against manifests using `domainMatches` predicates.

```typescript
// Based on check-domain-learning.ts#L66-L80, #L91-L99
import { siteSkillsForUrl, learningEntry } from 'ego-browser';

const url = 'https://example.com/products/123';
const matchingSkills = await siteSkillsForUrl(url);

// Convert matches to structured LearningEntry objects
const entries = matchingSkills.map(skill => learningEntry(skill));

```

The `loadLearnedContext` function aggregates all relevant data for a given URL. According to the implementation in [`package/ego-browser/src/learning/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/learning/index.ts) (lines 46-118), this function:

- Loads Markdown note files into `LearnedKnowledgeNote` objects
- Builds tool signatures for **Node‑side tools** and **browser‑side tools**
- Injects ready‑to‑run example strings for each tool

The returned `LearnedContext` object contains: `exists` (boolean), `siteId`, `domain`, `knowledge` (notes array), and `tools` (signatures array).

### Stage 3: Validation and Dynamic Execution

Before execution, manifests pass through `validateLearning` and `validateLearnings` functions defined in [`validate-learning-format.ts`](https://github.com/citrolabs/ego-lite/blob/main/validate-learning-format.ts). These validators enforce schema compliance and prevent malformed learning packs from reaching the runtime.

For tool execution, the subsystem provides two distinct pathways:

**Node‑side tools** via `runNodeSiteTool`:

```typescript
// Based on index.ts#L145-L176
import { runNodeSiteTool } from 'ego-browser';

// Dynamically imports with cache-busting query for hot-reload
const result = await runNodeSiteTool('example-site-id', 'addToCart', {
  productId: '123',
  quantity: 2
});

```

The implementation appends a cache‑busting query parameter to force module reload during development, then invokes the exported callable.

**Browser‑side tools** via `loadBrowserToolSource` and `wrapBrowserTool`:

```typescript
// Based on index.ts#L94-L96
import { loadBrowserToolSource, wrapBrowserTool } from 'ego-browser';

const source = await loadBrowserToolSource('example-site-id', 'clickButton');
const wrapped = wrapBrowserTool(source, { selector: '#buy-now' });

// Execute in browser context
await eval(wrapped);

```

The `wrapBrowserTool` function injects arguments into an Immediately Invoked Function Expression (IIFE), creating a self‑contained execution unit for the browser environment.

## Practical Code Examples for Site Knowledge Management

### Loading and Inspecting Learned Context

```javascript
import { loadLearnedContext } from 'ego-browser';

const ctx = await loadLearnedContext('https://example.com/products/123');

if (ctx.exists) {
  console.log(`🧠 Site knowledge found for ${ctx.domain}`);
  
  // Access Markdown notes
  ctx.knowledge.forEach(note => {
    console.log(`--- ${note.fileName} ---`);
    console.log(note.content);
  });
  
  // Inspect available tools
  ctx.tools.forEach(tool => {
    console.log(`Tool: ${tool.toolName} (${tool.toolType})`);
    console.log(`Description: ${tool.description}`);
    console.log(`Example: ${tool.example}\n`);
  });
}

```

### Executing a Node‑Side Site Tool

```javascript
import { runNodeSiteTool } from 'ego-browser';

const cartResult = await runNodeSiteTool(
  'example-ecommerce',
  'addToCart',
  { productId: 'SKU-456', quantity: 1 }
);

if (cartResult.success) {
  console.log(`✅ Added to cart: ${cartResult.cartUrl}`);
}

```

### Running a Browser‑Side Tool with Custom Arguments

```javascript
import { loadBrowserToolSource, wrapBrowserTool } from 'ego-browser';

async function executeSiteHelper(siteId, toolName, args) {
  const source = await loadBrowserToolSource(siteId, toolName);
  const executable = wrapBrowserTool(source, args);
  return await eval(executable);
}

// Invoke a site-specific button click handler
await executeSiteHelper('example-site', 'secureCheckout', {
  shippingMethod: 'express',
  giftWrap: false
});

```

## Key Files in the Learning Subsystem

| Path | Purpose |
|------|---------|
| [`src/learning/check-domain-learning.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/check-domain-learning.ts) | Directory scanning, manifest loading, domain matching, and `LearningEntry` construction |
| [`src/learning/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/index.ts) | Public API surface: `loadLearnedContext`, `runNodeSiteTool`, `loadBrowserToolSource`, `wrapBrowserTool` |
| [`src/learning/validate-learning-format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/validate-learning-format.ts) | JSON schema validation for [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) integrity |
| `skills/ego-browser/learnings/<site>/manifest.json` | Site skill declaration: `id`, `domains`, `notes`, `nodeTools`, `browserTools` |
| `skills/ego-browser/learnings/<site>/notes/*.md` | Human-readable knowledge files consumed as `LearnedKnowledgeNote` objects |

## Summary

- **Discovery**: `iterLearningDirs` scans the learnings root for folders with valid [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) files
- **Matching**: `siteSkillsForUrl` and `learningEntry` connect URLs to relevant site skills via domain patterns
- **Aggregation**: `loadLearnedContext` compiles notes and tool signatures into a unified `LearnedContext` object
- **Validation**: `validateLearning` ensures manifest schema compliance before runtime use
- **Execution**: `runNodeSiteTool` and `wrapBrowserTool` provide dynamic, environment‑appropriate invocation for Node and browser contexts

## Frequently Asked Questions

### What format must a site skill manifest follow?

A valid [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) must declare `id` (unique identifier), `name` (human-readable label), `domains` (array of patterns to match against URLs), `notes` (array of Markdown file paths), and tool definitions split into `nodeTools` and `browserTools`. The `validateLearning` function enforces this schema and rejects malformed manifests before they enter the runtime.

### How does the subsystem handle tool hot-reloading during development?

When `runNodeSiteTool` dynamically imports Node modules, it appends a cache‑busting query parameter to the module path. This forces Node to bypass the require cache and load the latest version, enabling rapid iteration on site-specific tool implementations without restarting the agent.

### Can multiple site skills match the same URL?

Yes. The `siteSkillsForUrl` function returns all skills whose domain patterns match the input URL. The agent runtime can then aggregate knowledge from multiple sources or apply precedence rules based on match specificity. The `learningEntry` helper normalizes each match into a consistent `LearningEntry` structure regardless of source.

### What distinguishes browser-side from Node-side tools?

**Browser‑side tools** are JavaScript functions wrapped in IIFEs that execute in the page's JavaScript context—useful for DOM manipulation, event triggering, or extracting data from rendered content. **Node‑side tools** are CommonJS/ES modules that run in the agent's Node.js process, enabling API calls, database access, or complex computation unavailable in browser sandboxes.