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

@northbound-run/matrix-agent-sdk

v0.7.0

Published

Core TypeScript SDK for building Matrix agents. Provides a high-level, strongly-typed client that wraps `matrix-js-sdk` with normalized event types, credential management, and multiple storage backends.

Readme

@northbound-run/matrix-agent-sdk

Core TypeScript SDK for building Matrix agents. Provides a high-level, strongly-typed client that wraps matrix-js-sdk with normalized event types, credential management, and multiple storage backends.

Overview

@northbound-run/matrix-agent-sdk is a thin, opinionated layer over matrix-js-sdk designed for agent use cases. It normalizes Matrix event types into common message, reaction, redaction, and membership events; manages persistent credentials and sync state; and provides a typed event emitter for real-time updates.

The client exposes eight sub-APIs: messages (send, edit, redact, react, mark read), rooms (list, create, join, leave, invite, get info, set topic/name), members (list, kick, ban, unban, set power level), state (read and write room state events), media (upload, download), typing (send typing indicators), receipts (read receipts), and account (profile, presence).

Quick Start

Login with password:

import { AgentChannelsMatrixClient } from "@northbound-run/matrix-agent-sdk";

const client = await AgentChannelsMatrixClient.login({
  username: "mybot",
  password: "secret",
  homeserverUrl: "https://matrix.org",
});

await client.start();

Connect with existing credentials:

const client = new AgentChannelsMatrixClient({
  accessToken: "syt_...",
  userId: "@mybot:matrix.org",
  deviceId: "MYBOT_DEVICE",
  homeserverUrl: "https://matrix.org",
});

await client.start();

Load stored credentials:

import { loadFirstCredential } from "@northbound-run/matrix-agent-sdk";

const creds = await loadFirstCredential({
  storePath: "~/.matrix-agent",
});

const client = new AgentChannelsMatrixClient(creds);
await client.start();

Listen for events:

client.on("message", (event) => {
  console.log(`${event.sender}: ${event.body}`);
});

client.on("reaction", (event) => {
  console.log(`${event.sender} reacted with ${event.content.shortcode}`);
});

client.on("room.join", (event) => {
  console.log(`Joined room: ${event.roomId}`);
});

Send a message:

await client.messages.send({
  roomId: "!abc:matrix.org",
  body: "Hello, world!",
});

Installation

npm install @northbound-run/matrix-agent-sdk
# or
bun install @northbound-run/matrix-agent-sdk

Requires matrix-js-sdk ^35.0.0 (installed automatically).

Configuration

AgentChannelsMatrixClient Constructor

| Parameter | Type | Default | Description | |---|---|---|---| | accessToken | string | — | Access token for the Matrix account | | userId | string | — | Full Matrix user ID (e.g. @bot:matrix.org) | | deviceId | string | — | Device ID for the session | | homeserverUrl | string | https://matrix.agentchannels.dev | Matrix homeserver URL | | store | string or IAgentStore | "memory" | Storage backend: "memory", "file", "sqlite", or custom store | | storePath | string | ~/.matrix-agent | Path for file-based storage |

Login Configuration

| Parameter | Type | Required | Description | |---|---|---|---| | username | string | Yes | Local part of the user ID (no @ or server) | | password | string | Yes | Account password | | homeserverUrl | string | No | Homeserver URL (defaults to https://matrix.agentchannels.dev) | | store | string or IAgentStore | No | Storage backend (defaults to "memory") | | storePath | string | No | Path for file-based storage |

API Reference

AgentEventEmitter

Typed event emitter with the following events:

| Event | Payload | Description | |---|---|---| | message | NormalizedMessage | Text or formatted message received | | message.edit | NormalizedMessage | Message edited by sender | | message.redact | NormalizedRedaction | Message redacted (deleted) | | reaction | NormalizedReaction | Emoji reaction added to a message | | reaction.redact | NormalizedReaction | Reaction removed | | room.join | RoomInfo | Client joined a room | | room.leave | RoomInfo | Client left a room | | room.invite | RoomInfo | Client invited to a room (not yet joined) | | room.update | RoomInfo | Room name, topic, or encryption status changed | | room.encrypted | RoomInfo | Room encryption toggled | | member.join | AgentMembership | Another user joined a room | | member.leave | AgentMembership | Another user left a room | | member.invite | AgentMembership | Another user invited to a room | | typing | TypingEvent | User is typing indicator | | receipt | ReceiptEvent | Read receipt from user | | connection | ConnectionState | Sync connection opened or closed | | sync | AgentSyncState | Sync completed (initial or incremental) |

Usage:

client.on("message", (msg) => {
  console.log(msg.body);
});

client.once("room.join", (room) => {
  console.log(`Joined ${room.name}`);
});

Messages API

Send a message:

await client.messages.send({
  roomId: "!abc:matrix.org",
  body: "Hello",
  format: "plain", // or "html"
  msgtype: "m.text", // or "m.notice", "m.emote"
});

Edit a message:

await client.messages.edit({
  roomId: "!abc:matrix.org",
  eventId: "$old_event",
  body: "Updated text",
});

Redact a message:

await client.messages.redact({
  roomId: "!abc:matrix.org",
  eventId: "$event",
  reason: "Spam",
});

React to a message:

await client.messages.react({
  roomId: "!abc:matrix.org",
  eventId: "$event",
  emoji: "👍",
});

Mark as read:

await client.messages.markRead({
  roomId: "!abc:matrix.org",
  eventId: "$event",
});

Get recent messages:

const messages = await client.messages.getMessages({
  roomId: "!abc:matrix.org",
  limit: 50,
  direction: "b", // "b" for backwards, "f" for forwards
});

Rooms API

List rooms:

const rooms = await client.rooms.list();

Create a room:

await client.rooms.create({
  name: "My Room",
  topic: "Discussion",
  preset: "private_chat", // or "public_chat", "trusted_private_chat"
  invites: ["@user:matrix.org"],
});

Join a room:

await client.rooms.join({
  roomId: "!abc:matrix.org", // or use room alias "#name:server"
});

Leave a room:

await client.rooms.leave({
  roomId: "!abc:matrix.org",
});

Invite a user:

await client.rooms.invite({
  roomId: "!abc:matrix.org",
  userId: "@user:matrix.org",
});

Get room info:

const info = await client.rooms.getInfo({
  roomId: "!abc:matrix.org",
});
// info.name, info.topic, info.memberCount, info.encrypted

Set room topic:

await client.rooms.setTopic({
  roomId: "!abc:matrix.org",
  topic: "New topic",
});

Set room name:

await client.rooms.setName({
  roomId: "!abc:matrix.org",
  name: "New name",
});

Members API

List members:

const members = await client.members.list({
  roomId: "!abc:matrix.org",
});

Kick a member:

await client.members.kick({
  roomId: "!abc:matrix.org",
  userId: "@user:matrix.org",
  reason: "Spam",
});

Ban a member:

await client.members.ban({
  roomId: "!abc:matrix.org",
  userId: "@user:matrix.org",
  reason: "Spam",
});

Unban a member:

await client.members.unban({
  roomId: "!abc:matrix.org",
  userId: "@user:matrix.org",
});

Set power level:

await client.members.setPowerLevel({
  roomId: "!abc:matrix.org",
  userId: "@user:matrix.org",
  powerLevel: 50, // 0-100, default is 0, 50 is moderator, 100 is admin
});

State API

Get a state event:

const event = await client.state.get({
  roomId: "!abc:matrix.org",
  type: "m.room.topic",
  stateKey: "", // optional state key
});

Set a state event:

await client.state.set({
  roomId: "!abc:matrix.org",
  type: "m.room.custom_event",
  content: { custom: "data" },
  stateKey: "custom_key", // optional
});

Get all state:

const allState = await client.state.getAll({
  roomId: "!abc:matrix.org",
});

Media API

Upload media:

const url = await client.media.upload({
  data: Buffer.from("..."), // or Uint8Array
  mimeType: "image/png",
  filename: "image.png",
});
// url is an MXC URL: "mxc://server/media_id"

Download media:

const data = await client.media.download({
  mxcUrl: "mxc://matrix.org/abc123",
});
// returns Uint8Array

Get HTTPS URL for media:

const httpsUrl = await client.media.getUrl({
  mxcUrl: "mxc://matrix.org/abc123",
});
// returns "https://matrix.org/_matrix/media/r0/download/matrix.org/abc123"

Typing API

Send typing indicator:

await client.typing.set({
  roomId: "!abc:matrix.org",
  typing: true,
  timeout: 5000, // milliseconds
});

Receipts API

Get read receipts:

const receipts = await client.receipts.get({
  roomId: "!abc:matrix.org",
  eventId: "$event",
});

Account API

Get profile:

const profile = await client.account.getProfile({
  userId: "@user:matrix.org", // optional, defaults to current user
});
// profile.displayName, profile.avatarUrl

Set display name:

await client.account.setDisplayName({
  displayName: "My Bot",
});

Set avatar:

await client.account.setAvatar({
  mxcUrl: "mxc://matrix.org/abc123",
});

Set presence:

await client.account.setPresence({
  presence: "online", // "online", "offline", "unavailable"
  statusMessage: "In a meeting",
});

Credential Management

loadCredentials

Load an array of credentials (multi-account support). Credentials are sourced from (in order):

  1. Credentials file at the specified path
  2. MATRIX_* environment variables (single account)
  3. FileStore at the default location
import { loadCredentials } from "@northbound-run/matrix-agent-sdk";

const creds = await loadCredentials({
  storePath: "~/.matrix-agent",
});
// returns StoredCredentials[] (may be empty)

loadFirstCredential

Load the first available credential. Throws if no credentials found.

import { loadFirstCredential } from "@northbound-run/matrix-agent-sdk";

const cred = await loadFirstCredential({
  storePath: "~/.matrix-agent",
});

saveCredentials

Persist credentials to the file store.

import { saveCredentials } from "@northbound-run/matrix-agent-sdk";

await saveCredentials("~/.matrix-agent", [
  {
    userId: "@bot:matrix.org",
    accessToken: "syt_...",
    deviceId: "BOT_DEVICE",
    homeserverUrl: "https://matrix.org",
  },
]);

listStoreAccounts

List user IDs that have stored credentials.

import { listStoreAccounts } from "@northbound-run/matrix-agent-sdk";

const userIds = await listStoreAccounts("~/.matrix-agent");

validateCredentials

Type guard to validate an object as StoredCredentials.

import { validateCredentials } from "@northbound-run/matrix-agent-sdk";

if (validateCredentials(obj)) {
  // obj is StoredCredentials
}

resolveStorePath

Resolve a path with ~ to absolute path.

import { resolveStorePath } from "@northbound-run/matrix-agent-sdk";

const absolute = resolveStorePath("~/.matrix-agent");

Storage Backends

MemoryStore (default)

In-memory store for ephemeral sessions. Data is lost on shutdown.

const client = new AgentChannelsMatrixClient({
  accessToken: "token",
  userId: "@bot:matrix.org",
  deviceId: "BOT",
  store: "memory",
});

FileStore

File-based JSON store for persistent sessions. Stores credentials, sync token, and sync cache.

const client = new AgentChannelsMatrixClient({
  accessToken: "token",
  userId: "@bot:matrix.org",
  deviceId: "BOT",
  store: "file",
  storePath: "~/.matrix-agent",
});

Custom Store

Implement IAgentStore to use a custom storage backend (e.g. database, encrypted storage).

import type { IAgentStore } from "@northbound-run/matrix-agent-sdk";

class CustomStore implements IAgentStore {
  async getCredentials(): Promise<StoredCredentials | null> { /* ... */ }
  async setCredentials(creds: StoredCredentials): Promise<void> { /* ... */ }
  async getSyncToken(): Promise<string | null> { /* ... */ }
  async setSyncToken(token: string): Promise<void> { /* ... */ }
  async getSyncCache(): Promise<Record<string, unknown> | null> { /* ... */ }
  async setSyncCache(cache: Record<string, unknown>): Promise<void> { /* ... */ }
}

const client = new AgentChannelsMatrixClient({
  accessToken: "token",
  userId: "@bot:matrix.org",
  deviceId: "BOT",
  store: new CustomStore(),
});

Error Handling

The SDK exports a typed error hierarchy. Catch specific errors to handle them appropriately:

import {
  AgentChannelsMatrixError,
  AuthError,
  RoomNotFoundError,
  TokenExpiredError,
  RateLimitError,
  NetworkError,
} from "@northbound-run/matrix-agent-sdk";

try {
  await client.messages.send({
    roomId: "!abc:matrix.org",
    body: "Hello",
  });
} catch (err) {
  if (err instanceof TokenExpiredError) {
    console.error("Re-authenticate required");
  } else if (err instanceof RateLimitError) {
    console.error(`Rate limited, retry after ${err.retryAfter}ms`);
  } else if (err instanceof RoomNotFoundError) {
    console.error(`Room not found: ${err.roomId}`);
  } else if (err instanceof AuthError) {
    console.error("Authentication failed");
  } else if (err instanceof NetworkError) {
    console.error("Network error, will retry");
  } else if (err instanceof AgentChannelsMatrixError) {
    console.error(`Matrix error: ${err.message}`);
  }
}

Error Classes

  • AgentChannelsMatrixError — Base error class
  • AuthError — Authentication failed
  • InvalidCredentialsError — Credentials invalid or missing
  • TokenExpiredError — Access token expired
  • RateLimitError — Rate limited by homeserver (includes retryAfter)
  • NetworkError — Network connection failed
  • TimeoutError — Request timed out
  • HomeserverError — Homeserver returned an error
  • RoomNotFoundError — Room does not exist
  • NotInRoomError — Client not a member of the room
  • InsufficientPowerLevelError — Insufficient permissions for operation
  • EventNotFoundError — Event does not exist
  • InvalidMessageError — Message body or format invalid
  • StoreError — Storage operation failed
  • SyncError — Sync connection failed
  • NotReadyError — Client not connected (call start() first)

Constants

import {
  DEFAULT_HOMESERVER_URL,
  DEFAULT_STORE_PATH,
} from "@northbound-run/matrix-agent-sdk";

console.log(DEFAULT_HOMESERVER_URL); // "https://matrix.agentchannels.dev"
console.log(DEFAULT_STORE_PATH); // "~/.matrix-agent"

Runtime

Supports Node.js and Bun. Requires no runtime-specific dependencies beyond matrix-js-sdk.

License

MIT