You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: AUTO_LOADING_EXTENSIONS.md
+83-3Lines changed: 83 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -51,16 +51,96 @@ sequenceDiagram
51
51
A->>TO: initialize tool calling
52
52
```
53
53
54
-
## What “Lazy Loading” Means (Today)
54
+
## What "Lazy Loading" Means (Today)
55
55
56
-
You’ll see “lazy” used in a few different ways:
56
+
You'll see "lazy" used in a few different ways:
57
57
58
58
-**Lazy optional dependencies**: the curated registry uses dynamic `import()` to include only installed extension packages.
59
-
-**Lazy heavy deps inside tools**: a tool’s `execute()` can dynamically `import()` heavy libraries on first call.
59
+
-**Lazy heavy deps inside tools**: a tool's `execute()` can dynamically `import()` heavy libraries on first call.
60
+
-**Lazy heavy deps shared across descriptors**: `SharedServiceRegistry.getOrCreate()` registers a factory at activation and only constructs the resource on the first call that needs it. Subsequent callers (other tools, the streaming guardrail) share the same instance.
60
61
-**Lazy skills**: `SkillRegistry` from `@framers/agentos/skills` exposes `skills_list` / `skills_read` tools so the model can fetch `SKILL.md` content on demand. Curated skill content ships in `@framers/agentos-skills`.
61
62
62
63
What AgentOS does not do by default: wait for the model to request a tool and then reveal its schema. Tool calling requires schemas up front, so the host decides which tools to expose for a given session/turn.
63
64
65
+
## End-to-End Walkthrough: A Lazy-Loaded Guardrail Pack
66
+
67
+
Walking through what actually happens when a host installs the PII redaction guardrail extension and runs a request. This is the same shape every guardrail pack follows.
Nothing else from the 100+ extension catalog enters `node_modules`. The registry is a small SDK; the catalog of metadata it carries is independent of which packages are installed.
// → manifest.packs.length === 1 (only the installed pii-redaction pack).
92
+
```
93
+
94
+
`createCuratedManifest()` calls `import.meta.resolve()` on every catalog entry. The 99 entries that are not installed get dropped with no error. The one installed pack lands in `manifest.packs`.
`ExtensionManager.loadManifest()` runs `pack.onActivate(ctx)`, which registers a `pii:ner-model` factory in `ctx.services` (`SharedServiceRegistry`). The 110MB BERT NER model file does not enter the module graph yet. The factory is the only thing registered.
104
+
105
+
### Step 4. Register descriptors
106
+
107
+
The pack emits three descriptors:
108
+
109
+
-`pii_scan` (kind: `tool`)
110
+
-`pii_redact` (kind: `tool`)
111
+
- The PII redaction guardrail itself (kind: `guardrail`, with `config.canSanitize = true` and `config.evaluateStreamingChunks = true`)
112
+
113
+
`ExtensionManager` checks `requiredSecrets` per descriptor before activating. If `anthropic.apiKey` was not provided, the optional LLM-judge resolver descriptor is skipped, and the rest of the pack activates.
114
+
115
+
### Step 5. First request loads the model
116
+
117
+
A user message hits the input pipeline. The two-phase guardrail dispatcher runs:
118
+
119
+
1.**Phase 1 (sequential sanitizers).** The PII guardrail's `evaluateInput()` fires. On this first call, the NER pipeline reaches into `services.getOrCreate('pii:ner-model', ...)` and the model loads. The guardrail returns a `SANITIZE` result with redacted text.
120
+
2.**Phase 2 (parallel classifiers).** Any remaining classifier guardrails run concurrently against the sanitized text via `Promise.allSettled`, with worst-action aggregation (`BLOCK > FLAG > ALLOW`).
121
+
122
+
### Step 6. Streaming output reuses the same model
123
+
124
+
As the LLM streams tokens, each `TEXT_DELTA` chunk passes through the guardrail's `evaluateOutput()`. The NER model is already loaded and cached in `SharedServiceRegistry`, so chunk evaluation is fast. `SANITIZE` results returned in Phase 1 chain deterministically; Phase 2 `SANITIZE` is downgraded to `FLAG` to keep output deterministic when classifiers run in parallel.
125
+
126
+
### Step 7. Tear-down releases everything
127
+
128
+
`AgentOS.shutdown()` calls `pack.onDeactivate(ctx)` in reverse order. `SharedServiceRegistry` releases the NER model. The kind-specific registries clear. Future processes start cold.
129
+
130
+
### What changes for the other guardrail packs
131
+
132
+
The pattern is identical. Only the heavy resource changes:
133
+
134
+
| Pack | Heavy resource | Lazy via |
135
+
|------|---------------|----------|
136
+
|`agentos-ext-pii-redaction`| BERT NER model (~110MB) |`services.getOrCreate('pii:ner-model', ...)`|
|`agentos-ext-topicality`| Embedding model + drift tracker |`services.getOrCreate('topicality:embedder', ...)`|
140
+
|`agentos-ext-code-safety`| None (regex-only) | No lazy load needed |
141
+
142
+
For the dispatcher mechanics and configuration flags, see [Guardrails](https://docs.agentos.sh/features/guardrails). For the kind-specific registry internals, see [Extension Loading](https://docs.agentos.sh/architecture/extension-loading).
143
+
64
144
## What Is Not Automatic (Yet)
65
145
66
146
- No core auto-install (`npm install`) of missing extensions.
Copy file name to clipboardExpand all lines: README.md
+69Lines changed: 69 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -223,6 +223,75 @@ await agentos.initialize({
223
223
});
224
224
```
225
225
226
+
## How extensions stay optional and lazy
227
+
228
+
The extension surface is dependency-injected at four layers. Each layer is opt-in by default, so a host that installs five extensions does not pay the cost of the other 95, and an extension that needs a 110MB NER model does not load it until the first call.
229
+
230
+
### Layer 1: package install gates everything
231
+
232
+
Every extension is its own npm package. Nothing is imported until you `npm install` it. The `@framers/agentos-extensions-registry` SDK depends on the extension packages as `peerDependenciesMeta.optional`, so installing the registry alone gives you the catalog metadata without pulling any extension source. Add `@framers/agentos-ext-pii-redaction` to your `package.json` and the registry sees it; remove it and the registry drops it on the next manifest build.
`createCuratedManifest()` calls `import.meta.resolve()` on every catalog entry before adding it to the manifest. If the resolution throws, the entry is omitted. The runtime never sees it. There is no error, no warning, no missing-module crash. This is the difference between a catalog (metadata) and a registry that only emits packs you can actually run.
237
+
238
+
```typescript
239
+
// Even if you ask for `tools: 'all'`, only installed packages reach the runtime.
Inside a pack, each descriptor (a single tool, guardrail, channel, or workflow) can declare `requiredSecrets`. Before activation, `ExtensionManager` checks whether each non-optional secret is resolvable from the secret store, the pack options, or the environment. Missing a secret? The descriptor is skipped and the rest of the pack still activates. Optional secrets unlock optional features (like the PII redaction extension's LLM-judge tier) without making them required.
247
+
248
+
```typescript
249
+
// pii-redaction declares its LLM judge as an optional dependency.
// No ANTHROPIC_API_KEY in env? The judge is skipped, the rest of the pack works.
257
+
```
258
+
259
+
### Layer 4: `SharedServiceRegistry.getOrCreate()` defers heavy resources
260
+
261
+
ML models, embedding indexes, ONNX runtimes, NER pipelines, and database pools are not loaded at activation. They are registered as factories and only constructed on the first call that needs them. The same instance is shared across descriptors in the same pack and (with a namespaced key) across packs.
The `import('./NerModel.js')` is a dynamic import: the model file itself does not enter the module graph until something asks for the service. First call pays the load cost; subsequent calls hit the cache. Tear-down releases everything via the registry's lifecycle.
276
+
277
+
### What this looks like end-to-end for a guardrail
278
+
279
+
A host installing `@framers/agentos-ext-pii-redaction`:
2.**Manifest build.**`createCuratedManifest({ tools: 'all' })` resolves only the installed PII pack and emits a single-pack manifest.
283
+
3.**Activation.**`ExtensionManager.loadManifest()` runs `pack.onActivate(ctx)`, which registers a `pii:ner-model` factory in `SharedServiceRegistry`. The model file is not loaded.
284
+
4.**Descriptor registration.** Two tool descriptors (`pii_scan`, `pii_redact`) and one guardrail descriptor land in the kind-specific registries. The guardrail descriptor's `config.canSanitize = true` and `config.evaluateStreamingChunks = true` flag it for the two-phase dispatcher.
285
+
5.**First request.** A user message hits the input pipeline. The two-phase dispatcher runs Phase 1 sequentially: the PII guardrail's `evaluateInput()` fires, calls into the NER pipeline, and the model loads on this first call. Subsequent requests hit the cached model.
286
+
6.**Streaming output.** As the model generates a response, each `TEXT_DELTA` chunk passes through the guardrail's `evaluateOutput()`. The dispatcher returns `SANITIZE` results with redacted text deterministically (Phase 1 chains sequentially), then runs all Phase 2 classifiers in parallel for any remaining checks.
287
+
7.**Tear-down.**`pack.onDeactivate(ctx)` runs in reverse order on shutdown. The shared service registry releases the model.
288
+
289
+
Same pattern for the four other guardrail packs: `@framers/agentos-ext-ml-classifiers` lazy-loads ONNX BERT models, `@framers/agentos-ext-grounding-guard` lazy-loads the NLI pipeline, `@framers/agentos-ext-topicality` lazy-loads embeddings, `@framers/agentos-ext-code-safety` is regex-only and pays no load cost.
290
+
291
+
This is the same auto-discovery surface that runtime-forged tools join: an agent that invents a function in session N can promote it via `SkillExporter` into a `SKILL.md` that the registry picks up on the next process start. Forging grows the surface mid-run; auto-discovery ships it as a first-class capability.
292
+
293
+
For the dispatcher mechanics (Phase 1 sanitizers, Phase 2 parallel classifiers, worst-action aggregation, mid-stream override), see [Guardrails](https://docs.agentos.sh/features/guardrails). For lifecycle internals, see [Extension Loading](https://docs.agentos.sh/architecture/extension-loading).
0 commit comments