§02 · Concepts
Five ideas.
The model behind useAdapter — what a
category is, what an adapter is, how the loader resolves them, and
why your import never crashes for a missing env var.
-
01 Categories
A category is a small TypeScript interface in @openagentry/core. host, db, auth, llm, wiki, repo, storage, email, domain are the v0 categories.
// @openagentry/core — categories are TypeScript interfaces. export interface DatabaseAdapter { readonly category: 'db'; readonly id: string; createInstance(opts): Promise<DatabaseInstance>; destroyInstance(i, confirm): Promise<void>; connectionString(i, env): Promise<string>; exportState(i): Promise<DatabaseStateBundle>; importState(b): Promise<DatabaseInstance>; }Methods are total. Every failure throws a structured AgentryError with a stable code, a phase, and a one-line suggestedFix.
-
02 Adapters
An adapter is an npm package whose default export satisfies a category interface. Adapter packages also declare an openagentry manifest block (type, category, id, capabilities, envVars, protocolVersion).
// @openagentry/adapter-db-neon — an adapter is an npm package whose // default export satisfies the category interface. import db from '@openagentry/adapter-db-neon'; // → DatabaseAdapter — category: 'db', id: 'neon' await db.createInstance({ name: 'app-prod', region: 'aws-us-east-1', engine: 'postgres' });The CLI reads the manifest at registration time. It never imports anything from the package beyond the default export.
-
03 The plugin loader
The CLI reads .agentry/workspace.json, resolves each listed package from the workspace node_modules, validates its manifest, and registers its default export. Plugins are loaded in declaration order.
// .agentry/workspace.json { "plugins": [ "@openagentry/adapter-llm-cli", "@openagentry/adapter-llm-aigateway", "@openagentry/adapter-wiki-fs" ], "defaultEnv": "dev" }Failures during load are collected, not thrown. agentry capabilities reports them under loadErrors so you can register what works and fix what doesn't.
-
04 The lazy-default convention
Most adapters use a lazy-default pattern: the default export is always a frozen object with category and id, but env reads happen on first method call. The package imports cleanly without env set; calls fail with a structured error at use-site.
// adapter-db-neon: the default export is a frozen shape. // Calling a method reads NEON_API_KEY on first use. import db from '@openagentry/adapter-db-neon'; // → DatabaseAdapter — works to import without env set. await db.createInstance({ name: 'app', ... }); // → throws AgentryError({ code: 'E_NEON_AUTH', ... }) if NEON_API_KEY is missing.Three adapters (auth-nextauth, llm-cli, wiki-fs) use eager-but-never-null because their env defaults always resolve to something. The construct-or-null pattern is gone.
-
05 Resolution
useAdapter(category) returns the last-registered adapter for that category. Multiple adapters in the same category are fine — change the default by reordering workspace.json. No app-code change.
// app.ts — useAdapter returns whichever adapter was registered last // for the category. With three llm adapters declared, the last one wins. import { useAdapter } from '@openagentry/core'; const llm = useAdapter('llm'); // → llm-aigateway, the last entry in workspace.json's plugins array // Reorder workspace.json to switch defaults — no app code change.agentry capabilities --json surfaces both the default (categories[cat]) and the full registered list (adaptersByCategory[cat]).