openagentry

§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.


  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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]).