npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@axiom-lattice/core

v2.1.79

Published

Core library for agent-based applications

Readme

@axiom-lattice/core

Core agent framework providing lattice managers for models, tools, agents, memory, stores, and more.

Store Configuration

configureStores

Unified store registration that replaces the manual new → initialize → remove → register boilerplate. Handles StoreLatticeManager, ScheduleLatticeManager, and MemoryLatticeManager through a single call.

import { configureStores } from "@axiom-lattice/core";
import { createPgStoreConfig } from "@axiom-lattice/pg-stores";
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";

// One connection string creates all PG store instances
const stores = createPgStoreConfig(process.env.DATABASE_URL);

// One call registers all stores across three managers
await configureStores({
  ...stores,
  checkpoint: PostgresSaver.fromConnString(process.env.DATABASE_URL),
}, { autoDisposeStores: true });

Supported store keys

| Key | Registered via | |-----|---------------| | thread, assistant, workspace, project, database, metrics, mcp, user, tenant, userTenantLink, threadMessageQueue, workflowTracking, eval, channelInstallation, channelBinding, skill | StoreLatticeManager | | schedule | ScheduleLatticeManager (auto-configured as POSTGRES) | | checkpoint | MemoryLatticeManager (accepts any CheckpointSaver) |

Behavior

For each store entry:

  1. Calls store.initialize() if the method exists (skipped if it requires arguments)
  2. Removes any existing "default" registration
  3. Registers the new store under "default"
  4. Tracks store.dispose for cleanup

Options

interface ConfigureStoresOptions {
  autoDisposeStores?: boolean;     // Register SIGINT/SIGTERM handlers for cleanup
  customStores?: Record<string, object>;  // Register custom store types
}

Returns a dispose function for manual cleanup regardless of autoDisposeStores.

Custom stores

Register custom store implementations that follow the same initialize() / dispose() pattern:

interface MyCustomStore {
  getData(): Promise<string>;
}

class PostgreSQLMyCustomStore implements MyCustomStore {
  async initialize() { /* run migrations */ }
  async dispose() { /* close pool */ }
  async getData() { return "from pg"; }
}
await configureStores({
  thread: new PostgreSQLThreadStore(opts),
}, {
  customStores: {
    myCustom: new PostgreSQLMyCustomStore({ poolConfig }),
  },
});

// Consume via StoreLatticeManager
const { store } = getStoreLattice("default", "myCustom");
const customStore = store as MyCustomStore;

Consumer pattern

Components that need stores read them directly from StoreLatticeManager — no setConfigStore required:

// SqlDatabaseManager reads database configs lazily from the store lattice
const db = await sqlDatabaseManager.getDatabase(tenantId, "main-db");

// MetricsServerManager loads configs on first access
const client = await metricsServerManager.getClient(tenantId, "prometheus");
const servers = await metricsServerManager.getServerKeys(tenantId);

Other Lattice Managers

Model Lattice

import { registerModelLattice } from "@axiom-lattice/core";

registerModelLattice("default", {
  model: "gpt-4o",
  provider: "openai",
  streaming: true,
  apiKeyEnvName: "OPENAI_API_KEY",
});

ChunkBuffer

import { InMemoryChunkBuffer, registerChunkBuffer } from "@axiom-lattice/core";

const buffer = new InMemoryChunkBuffer({ ttl: 30 * 60 * 1000 });
registerChunkBuffer("default", buffer);

Directory Structure

  • src/store_lattice/ — Store lattice manager + configureStores
  • src/model_lattice/ — LLM provider abstractions
  • src/tool_lattice/ — Tool implementations (SQL, metrics, etc.)
  • src/agent_lattice/ — Agent definitions and builders
  • src/memory_lattice/ — Context/memory management
  • src/schedule_lattice/ — Scheduled task management
  • src/sandbox_lattice/ — Code execution sandbox providers

Custom Middleware Registry

Applications can write their own middleware and register it at runtime without modifying core source code. The registered middleware can then be enabled via agent config stored in the database.

Quick start

import { createMiddleware, CustomMiddlewareRegistry } from "@axiom-lattice/core";

CustomMiddlewareRegistry.register("my-logger", (config) =>
  createMiddleware({
    name: "MyLogger",
    wrapModelCall: async (request, handler) => {
      console.log(`[${config.logLevel}] Model call`);
      return handler(request);
    },
  })
);

Database config format

In your agent's graphDefinition.middleware array, add a "custom" type entry:

{
  "id": "mw-001",
  "type": "custom",
  "name": "Audit Logger",
  "description": "Logs model calls for audit",
  "enabled": true,
  "config": {
    "key": "my-logger",
    "logLevel": "debug"
  }
}

The config.key must match the key passed to register(). All other config fields are forwarded to your factory function.

Full example — permission-check middleware

import { createMiddleware, CustomMiddlewareRegistry } from "@axiom-lattice/core";

CustomMiddlewareRegistry.register("permission-check", (config) =>
  createMiddleware({
    name: "PermissionCheck",
    wrapToolCall: async (request, handler) => {
      const toolName = request.toolCall?.name;
      if (toolName && !config.allowedTools.includes(toolName)) {
        return { content: `Tool "${toolName}" is not permitted.` };
      }
      return handler(request);
    },
  })
);

Database config:

{
  "id": "perm-001",
  "type": "custom",
  "name": "Tool Permissions",
  "description": "Restrict tool access",
  "enabled": true,
  "config": {
    "key": "permission-check",
    "allowedTools": ["read_file", "write_file"]
  }
}

API reference

| Method | Signature | Description | |--------|-----------|-------------| | register | (key: string, factory: (config: Record<string, any>) => AgentMiddleware \| Promise<AgentMiddleware>) => void | Register a factory by key | | unregister | (key: string) => boolean | Remove a registration | | get | (key: string) => AgentMiddlewareFactory \| undefined | Look up a factory | | has | (key: string) => boolean | Check if key is registered | | list | () => string[] | Get all registered keys |

Important

  • Register factories before building agents (typically at app startup)
  • Duplicate keys overwrite previous registrations
  • Unregistered keys are skipped with a console warning at build time
  • Factory functions receive the raw config object (minus key) — validate it yourself

External Channel Integration

core provides the storage infrastructure and binding system that external channel adapters (Lark, Slack, Email, etc.) depend on. The actual HTTP routes and message dispatch live in packages/gateway, but all persistence and sender resolution are handled here.

What core provides

| Component | Location | Purpose | |-----------|----------|---------| | InMemoryChannelInstallationStore | store_lattice/ | In-memory ChannelInstallationStore implementation | | InMemoryBindingStore | store_lattice/ | In-memory BindingRegistry implementation | | BindingRegistryHolder | bindings_lattice/ | Global accessor for the active BindingRegistry | | manage_binding tool | tool_lattice/manage_binding/ | Agent tool for CRUD on sender-to-agent bindings |

Architecture overview

External Platform (Lark, Slack, Email)
  → Gateway HTTP route (packages/gateway)
  → ChannelAdapter.receive(rawPayload) → InboundMessage
  → MessageRouter.dispatch() (packages/gateway)
    → BindingRegistry.resolve() ←── core
    → Thread creation / reuse ←── core
    → Agent.addMessage() ←── core
  → ChannelAdapter.sendReply() (packages/gateway)

Agents remain channel-agnostic — they only see standard thread/message execution.

Setup for custom channel development

1. Register channel stores

import { configureStores } from "@axiom-lattice/core";
import { PostgreSQLChannelInstallationStore, PostgreSQLBindingStore } from "@axiom-lattice/pg-stores";

await configureStores({
  channelInstallation: new PostgreSQLChannelInstallationStore({ pool }),
  channelBinding: new PostgreSQLBindingStore({ pool }),
  // ... other stores
});

2. Set the global BindingRegistry

import { setBindingRegistry } from "@axiom-lattice/core";

const bindingStore = getStoreLattice("default", "channelBinding").store;
setBindingRegistry(bindingStore);

3. Implement ChannelAdapter (in gateway)

import type { ChannelAdapter, InboundMessage, OutboundMessage, ReplyTarget } from "@axiom-lattice/protocols";
import { z } from "zod";

const slackConfigSchema = z.object({
  botToken: z.string(),
  signingSecret: z.string(),
});

export const slackAdapter: ChannelAdapter = {
  channel: "slack",
  configSchema: slackConfigSchema,

  async receive(rawPayload, installation): Promise<InboundMessage | null> {
    // 1. Parse Slack-specific event
    // 2. Return normalized InboundMessage or null to ignore
    return {
      channel: "slack",
      channelInstallationId: installation.id,
      tenantId: installation.tenantId,
      sender: { id: userId, displayName: userName },
      content: { text: messageText },
      replyTarget: {
        adapterChannel: "slack",
        channelInstallationId: installation.id,
        rawTarget: { channelId, threadTs },
      },
    };
  },

  async sendReply(replyTarget, message, installation): Promise<void> {
    // Use Slack API to send reply
    // replyTarget.rawTarget contains channel-specific context
  },
};

4. Register routes and adapter (in gateway)

// packages/gateway/src/channels/registry.ts
adapterRegistry.register(slackAdapter);

// packages/gateway/src/channels/routes.ts
app.post("/api/channels/slack/installations/:id/events", async (req, reply) => {
  const installation = await installationStore.getInstallationById(req.params.id);
  const message = await slackAdapter.receive(req.body, installation);
  if (message) await messageRouter.dispatch(message);
});

Binding resolution flow

When MessageRouter.dispatch() receives an InboundMessage:

  1. Calls BindingRegistry.resolve({ channel, senderId, channelInstallationId, tenantId })
  2. If no binding found:
    • If installation.rejectWhenNoBinding → throw BindingNotFoundError
    • If installation.fallbackAgentId → create temporary fallback binding
  3. If binding disabled → reject
  4. Thread resolution (based on binding.threadMode):
    • "fixed" → reuse binding.threadId
    • "per_conversation" → always create new thread
  5. Execute agent via agent.addMessage()

Agent-side binding management

Agents can manage bindings dynamically via the manage_binding tool:

// Agent can call this tool to:
// - list_installations: List available channel installations
// - create: Bind a sender to an agent (channel, senderId, agentId)
// - update: Change agent or threadMode for a binding
// - delete: Remove a binding
// - list: List all bindings with optional filters

Key types (from @axiom-lattice/protocols)

interface ChannelAdapter<TConfig = unknown> {
  readonly channel: string;
  readonly configSchema: z.ZodSchema<TConfig>;
  receive(rawPayload: unknown, installation: ChannelInstallation): Promise<InboundMessage | null>;
  sendReply(replyTarget: ReplyTarget, message: OutboundMessage, installation: ChannelInstallation): Promise<void>;
}

interface Binding {
  id: string;
  channel: string;
  channelInstallationId: string;
  tenantId: string;
  senderId: string;
  agentId: string;
  threadMode: "fixed" | "per_conversation";
  enabled: boolean;
}

Reference implementation

See packages/gateway/src/channels/lark/ for a complete production channel adapter including:

  • Webhook verification and decryption
  • Event parsing and normalization
  • Thread mapping (user / group / hybrid modes)
  • Reply delivery via official SDK