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

@agent-os-lab/agent-sdk

v0.1.24

Published

TypeScript SDK for the Hermes Memory Lab Agent Service `/api/v1` API.

Readme

Agent Service SDK

TypeScript SDK for the Hermes Memory Lab Agent Service /api/v1 API.

The SDK is intentionally HTTP-only. It does not import UI code, Next.js route handlers, server adapters, or AgentCore. Use the server entrypoint for trusted backend code and the browser entrypoint for frontend conversation flows.

For the full integration guide, see USAGE.md.

Install

After publishing, install the SDK package:

npm install @agent-os-lab/agent-sdk

Use the published entrypoints:

import { AgentServiceServerClient } from "@agent-os-lab/agent-sdk/server";
import { AgentServiceBrowserClient } from "@agent-os-lab/agent-sdk/browser";
import type { AgentRunEvent } from "@agent-os-lab/agent-sdk/types";

Local source imports inside this repository use:

import { AgentServiceServerClient } from "./src/server";
import { AgentServiceBrowserClient } from "./src/browser";

Publish

Publishing is controlled from this SDK package directory. The script bumps the SDK package version, builds the package, runs npm pack --dry-run, and then publishes to npm:

bun run sdk:publish

The default release is patch. Use --minor or --major when needed:

bun run sdk:publish -- --minor

Use --dry-run to validate the next version and package contents without publishing. The script restores the previous version after a dry run.

If npm requires two-factor authentication, pass the one-time password with --otp:

bun run sdk:publish -- --otp 123456

Runtime

  • Node.js 20+ or a modern browser runtime with fetch, Headers, Response, and ReadableStream.
  • Runtime dependency: @ag-ui/client for AG-UI chat adapters.
  • API keys must never be logged or sent to browsers.

Server Authentication

const client = new AgentServiceServerClient({
  baseUrl: "https://agent-service.example.com",
  apiKey: process.env.AGENT_SERVICE_API_KEY!,
  requestId: () => crypto.randomUUID(),
});

const { tenant } = await client.getCurrentTenant();

In production, tenant identity comes from the persisted API key row. tenantId is optional in the SDK and should only be supplied for development tenant switching or trusted internal tools:

const localClient = new AgentServiceServerClient({
  baseUrl: "http://localhost:3000",
  apiKey: "dev-service-key",
  tenantId: "tenant-demo",
});

Browser Authentication

Browser code must not receive AGENT_SERVICE_API_KEY. Use a same-origin business-project BFF/proxy or a short-lived scoped token minted by the business backend:

const client = new AgentServiceBrowserClient({
  baseUrl: "/api/agent-service",
});
const client = new AgentServiceBrowserClient({
  baseUrl: "https://agent-service.example.com",
  accessToken: async () => getScopedAgentToken(),
});

accessToken is a business-application user token for the BFF/proxy, not an Agent Service API key. Do not pass tenant API keys, platform admin tokens, or any long-lived service credential to the browser client.

Caller-provided authorization and x-hermes-tenant-id headers are stripped by the browser client. Only accessToken may set a browser bearer token, and only when the business backend expects that browser token.

AG-UI Chat

Use createAgUiChat when building a production chat surface with AG-UI compatible clients such as assistant-ui. It wraps the public AG-UI chat contract for session list/create/read/delete, active-run recovery, and AG-UI agent creation. threadId is the AgentOS session ID.

const chat = browserClient.createAgUiChat({
  agentId: "support-agent",
  profileId: "business-user-123",
  getAttachmentIds: () => pendingAttachmentIds,
});

const { session } = await chat.createSession();
const history = await chat.getSession(session.sessionId);
const active = await chat.getActiveRun(session.sessionId);
const agent = chat.createAgent({
  threadId: session.sessionId,
});

Use active.run?.runId as resumeRunId in the AG-UI runtime custom run config when reconnecting to an existing run.

Use the lower-level createAgUiAgent when you already own session/history management and only need the AG-UI transport.

const agent = browserClient.createAgUiAgent({
  agentId: "support-agent",
  threadId: session.sessionId,
  profileId: "business-user-123",
  getAttachmentIds: () => pendingAttachmentIds,
});

await agent.runAgent({
  toolContext: { businessUserId: "user-123" },
});

Trusted backend or BFF code can create the same AG-UI agent from the server client. The server client signs requests with the service API key and tenant scope configured on the client.

const agent = serverClient.createAgUiAgent({
  agentId: "support-agent",
  threadId: session.sessionId,
  profileId: "business-user-123",
});

To recover a running stream after refresh without the facade, call getAgUiActiveRun(agentId, sessionId, profileId) and pass the returned run ID through the AG-UI runtime's custom run config as resumeRunId.

Create Profile, Agent, And Stream

const client = new AgentServiceServerClient({
  baseUrl: "https://agent-service.example.com",
  apiKey: process.env.AGENT_SERVICE_API_KEY!,
});

await client.createProfile({
  profileId: "business-user-123",
  displayName: "Business User 123",
  metadata: { source: "billing-app" },
});

await client.createAgent({
  agentId: "support-agent",
  displayName: "Support Agent",
  billingSubjectId: "company-a/user-123",
  systemPrompt: "You are a support assistant.",
  model: "openai/gpt-5.2",
  memoryProvider: "none",
  memoryScopeMode: "both",
  compressionEnabled: false,
  memoryReviewEnabled: false,
});

const { bot } = await client.createBot({
  displayName: "WeChat Support Bot",
  agentId: "support-agent",
});

const { channelAccount } = await client.createBotChannelAccount(bot.botId, {
  channelType: "wechat",
  displayName: "Primary WeChat",
});

await client.refreshBotChannelQrCode(bot.botId, channelAccount.channelAccountId);

const { session } = await client.createSession("support-agent", {
  profileId: "business-user-123",
});

for await (const event of client.streamMessage("support-agent", session.sessionId, {
  profileId: "business-user-123",
  message: "Help me understand my invoice.",
})) {
  if (event.type === "message.delta") {
    process.stdout.write(event.delta);
  }
  if (event.type === "tool.started") {
    console.log("tool started", event.name);
  }
}

Billing Attribution And Export

Set billingSubjectId on Agents or Wikis when a tenant application needs cost attribution for one of its own business users, teams, or accounts. Per-request APIs that accept billingSubjectId can override the resource default for that request.

await client.createAgent({
  agentId: "support-agent",
  displayName: "Support Agent",
  billingSubjectId: "company-a/user-123",
  systemPrompt: "You are a support assistant.",
  model: "openai/gpt-5.2",
  memoryProvider: "none",
  memoryScopeMode: "both",
  compressionEnabled: false,
  memoryReviewEnabled: false,
});

for await (const event of client.streamMessage("support-agent", session.sessionId, {
  profileId: "business-user-123",
  message: "Help me understand my invoice.",
  billingSubjectId: "company-a/user-456",
})) {
  // ...
}

Pull billing events from a trusted backend with listBillingEvents. The cursor is afterSequence; poll until hasMore is false, then store nextSequence for the next polling cycle.

let afterSequence = loadLastBillingSequence();

while (true) {
  const page = await client.listBillingEvents({
    afterSequence,
    limit: 1000,
  });

  await saveBillingEvents(page.events);
  afterSequence = page.nextSequence;

  if (!page.hasMore) {
    await saveLastBillingSequence(afterSequence);
    break;
  }
}

Use billingSubjectId to fetch one subject's ledger:

const page = await client.listBillingEvents({
  billingSubjectId: "company-a/user-123",
  limit: 1000,
});

Create A Bot-Owned Agent And Wiki

For tenant product setup flows, use createBotBundle to create a Bot with its own Agent, optional Wiki, and optional channel account in one call. The SDK creates the Wiki first, passes the generated wikiId into Agent creation, creates the Bot bound to that Agent, then creates the channel account bound to the Bot.

const { bot, agent, wiki, channelAccount } = await client.createBotBundle({
  wiki: {
    displayName: "Support Knowledge",
    description: "Knowledge used by the support Bot.",
  },
  agent: {
    displayName: "Support Agent",
    systemPrompt: "You are a support assistant.",
    model: "openai/gpt-5.2",
    memoryProvider: "none",
    memoryScopeMode: "both",
    compressionEnabled: false,
    memoryReviewEnabled: false,
  },
  bot: {
    displayName: "WeChat Support Bot",
    context: { channel: "wechat" },
  },
  channelAccount: {
    channelType: "wechat",
    displayName: "Primary WeChat",
  },
});

Delete the same owned bundle with:

await client.deleteBotBundle(bot.botId);

deleteBotBundle deletes Bot, Agent, and the Agent-bound Wiki. Use it only when those resources are owned by that Bot; for shared Agents or Wikis, call the lower-level delete APIs explicitly.

Scheduled Agent Tasks

Use schedules from a trusted backend to run an Agent on a one-time, interval, or cron schedule. Scheduled runs use a fresh execution session by default.

const { schedule } = await client.createSchedule("support-agent", {
  profileId: "business-user-123",
  name: "Daily support summary",
  prompt: "Summarize yesterday's urgent support issues.",
  schedule: "0 9 * * *",
  timezone: "Asia/Shanghai",
  sessionId: "session-a",
});

await client.pauseSchedule("support-agent", schedule.id);
await client.resumeSchedule("support-agent", schedule.id);
await client.runScheduleNow("support-agent", schedule.id);

const { fires } = await client.listScheduleFires("support-agent", schedule.id);
const { deliveries } = await client.listScheduleDeliveries("support-agent", schedule.id);

Skills

Skills are human-authored SKILL.md procedures plus optional linked files under references/, templates/, scripts/, or assets/. The SDK manages Skills and Agent bindings; runtime execution reads Skills through AgentOS.

const { draft, validation } = await client.createSkillDraft({
  intent: "Create a TypeScript backend code review skill.",
  category: "engineering",
  tags: ["review"],
});

if (!validation.ok) {
  throw new Error(validation.errors.join("\n"));
}

const { skill } = await client.createSkill({
  displayName: draft.displayName,
  markdown: draft.markdown,
  files: draft.files,
});

Draft generation does not save anything. You can also create a Skill directly from a reviewed SKILL.md:

const { skill } = await client.createSkill({
  displayName: "Code Review",
  markdown: `---
name: code-review
description: Review code changes.
version: 1.0.0
metadata:
  agentos:
    category: engineering
    tags: [review]
---

# Code Review
`,
  files: [{
    path: "references/checklist.md",
    contentType: "text/markdown",
    contentText: "# Checklist",
  }],
});

await client.setSkillAgentBindings(skill.skill.skillId, {
  agentIds: ["support-agent"],
});

const { skills: agentSkills } = await client.listAgentSkills("support-agent");
const { wiki } = await client.getAgentWiki("support-agent");

Agent Groups And A2A

Agent Groups are server-only control-plane APIs. Put Agents in the same group, then mark target Agents callable.

await client.createAgent({
  agentId: "crm-analyst",
  displayName: "CRM Analyst",
  systemPrompt: "Analyze CRM records.",
  model: "openai/gpt-5.2",
  memoryProvider: "none",
  memoryScopeMode: "both",
  compressionEnabled: false,
  memoryReviewEnabled: false,
  a2aCallable: true,
});

await client.createAgentGroup({
  groupId: "sales",
  displayName: "Sales",
});

await client.replaceAgentGroupAgents("sales", {
  agentIds: ["support-agent", "crm-analyst"],
});

await client.appendAgentGroupAgents("sales", {
  agentIds: ["quote-agent"],
});

await client.removeAgentGroupAgents("sales", {
  agentIds: ["quote-agent"],
});

const { card } = await client.getA2aAgentCard("crm-analyst");
const result = await client.sendA2aMessage("crm-analyst", {
  callerAgentId: "support-agent",
  profileId: "business-user-123",
  message: "Summarize this account.",
});

An Agent can call another Agent only when both share at least one Agent Group and the target Agent has a2aCallable: true.

WeChat Bot Binding

Bot management is a server-only API. Create the Agent first, then bind a WeChat channel to a Bot:

import {
  AgentServiceServerClient,
  SERVICE_BOT_LOGIN_STATUS,
} from "@agent-os-lab/agent-sdk/server";

const client = new AgentServiceServerClient({
  baseUrl: "https://agent-service.example.com",
  apiKey: process.env.AGENT_SERVICE_API_KEY!,
});

const { bot, channelAccount } = await client.createWechatBotBinding({
  displayName: "WeChat Support Bot",
  agentId: "support-agent",
  channelDisplayName: "Primary WeChat",
});

await client.refreshBotChannelQrCode(bot.botId, channelAccount.channelAccountId);

const { qrCodeText } = await client.waitForBotChannelQrCode(bot.botId, channelAccount.channelAccountId, {
  timeoutMs: 30000,
  intervalMs: 1000,
});

console.log("Open this WeChat login QR URL:", qrCodeText);

const { channelAccounts } = await client.listBotChannelAccounts(bot.botId);
const binding = channelAccounts.find((account) => account.channelAccountId === channelAccount.channelAccountId);

if (binding?.runtimeState?.loginStatus === SERVICE_BOT_LOGIN_STATUS.loggedIn) {
  console.log("WeChat binding is logged in.");
}

createWechatBotBinding does not accept a botId; Agent Service generates it. The QR code is returned as a URL string in qrCodeText, so render it as a link or open it in a new page.

Tenant HTTP Tools

Tool provider registration, tool definition, Agent binding, and invocation reads are server-only APIs:

const { provider } = await client.createToolProvider({
  name: "crm",
  baseUrl: "https://crm.example.com/agentos",
  auth: { type: "bearer", token: process.env.CRM_AGENTOS_TOKEN! },
});

await client.upsertTool(provider.providerId, "profile_completion.update", {
  description: "Update profile completion percentage.",
  inputSchema: {
    type: "object",
    required: ["percent"],
    properties: {
      percent: { type: "number", minimum: 0, maximum: 100 },
    },
    additionalProperties: false,
  },
  executor: { type: "http", path: "/tools/profile-completion/update", timeoutMs: 5000 },
  contextPolicy: { includeMessageContext: true, requireMessageContext: true },
  resultPolicy: "hidden",
});

await client.setAgentTools("support-agent", {
  tools: ["crm.profile_completion.update"],
});

Send opaque business context per message when a tool needs to identify the business-side user:

await client.sendMessage("support-agent", session.sessionId, {
  profileId: "business-user-123",
  message: "Update this user's profile completion.",
  toolContext: { businessUserId: "user-123" },
});

Agent Service does not interpret the tool context object. It stores it with the user message and forwards it to tools according to each tool's contextPolicy.

Production tool setup also requires AGENTOS_USER_DATA_ENCRYPTION_KEY in Agent Service so provider credentials can be encrypted at rest. The full tools guide covers schema support, HTTP request/response envelopes, error codes, token rotation, and business-side handler examples in USAGE.md.

Frontend chat surfaces should use the browser client against a BFF/proxy:

const browserClient = new AgentServiceBrowserClient({
  baseUrl: "/api/agent-service",
});

const { session } = await browserClient.createSession("support-agent", {
  profileId: "business-user-123",
});

for await (const event of browserClient.streamMessage("support-agent", session.sessionId, {
  profileId: "business-user-123",
  message: "Help me understand my invoice.",
})) {
  if (event.type === "message.delta") {
    appendAssistantDelta(event.delta);
  }
}

Attachments

Attachments are uploaded directly from the browser to the Agent Service configured object store. When the upload is confirmed, Agent Service reads the object, calls file2md, and stores the converted Markdown during the confirm request. Send only converted attachment IDs with a message or queued run.

const created = await browserClient.createAttachmentUpload("support-agent", session.sessionId, {
  profileId: "business-user-123",
  filename: file.name,
  contentType: file.type || "application/octet-stream",
  sizeBytes: file.size,
});

await fetch(created.upload.url, {
  method: created.upload.method,
  headers: created.upload.headers,
  body: file,
});

const { attachment } = (
  await browserClient.confirmAttachmentUpload("support-agent", session.sessionId, created.attachment.id, {
    profileId: "business-user-123",
  })
);

if (attachment.status !== "converted") {
  throw new Error(attachment.conversionError ?? "Attachment conversion failed.");
}

await browserClient.sendMessage("support-agent", session.sessionId, {
  profileId: "business-user-123",
  message: "Analyze this file.",
  attachmentIds: [attachment.id],
});

Available methods:

  • createAttachmentUpload
  • confirmAttachmentUpload
  • listSessionAttachments
  • getSessionAttachment
  • retrySessionAttachment

Converted Markdown is complete. Agent Service rejects oversized attachment prompts instead of truncating them.

Wiki File Uploads

Use uploadConsoleFile when you need the converted Markdown. Use uploadAndCreateWikiSource when you want to upload a file, convert it, and attach it to a Wiki in one SDK call.

import { uploadAndCreateWikiSource, uploadConsoleFile } from "@agent-os-lab/agent-sdk";

const converted = await uploadConsoleFile({
  client,
  file,
});

console.log(converted.markdown);

const { source } = await uploadAndCreateWikiSource({
  client,
  wikiId: "wiki-a",
  file,
});

Lower-level file methods are also available:

  • createConsoleFileUpload
  • confirmConsoleFileUpload
  • getConsoleFile
  • retryConsoleFile

Async Runs

Async runs require a runtime worker process in the Agent Service environment.

const created = await client.createRun("support-agent", {
  profileId: "business-user-123",
  sessionId: session.sessionId,
  message: "Summarize my open invoices.",
  toolContext: { businessUserId: "user-123" },
});

let run = created.run;
while (run.status === "queued" || run.status === "running") {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  run = (await client.getRun("support-agent", run.runId)).run;
}

const replay = await client.listRunEvents("support-agent", run.runId);

Webhooks

const { webhook, secret } = await client.createWebhook({
  url: "https://billing.example.com/hermes-webhooks",
  eventTypes: ["run.completed", "run.failed", "run.cancelled"],
});

The raw webhook secret is returned once. Store it securely and verify x-hermes-webhook-signature in the receiver. Do not log the secret.

Error Handling

import { AgentServiceError } from "@agent-os-lab/agent-sdk/server";

try {
  await client.getAgent("missing-agent");
} catch (error) {
  if (error instanceof AgentServiceError) {
    console.error({
      status: error.status,
      code: error.code,
      requestId: error.requestId,
      details: error.details,
    });
  }
  throw error;
}

Request Options

Every SDK method accepts optional request options as the final parameter:

const controller = new AbortController();

await client.sendMessage("support-agent", "session-id", {
  profileId: "business-user-123",
  message: "Hello",
}, {
  requestId: "business-request-123",
  signal: controller.signal,
  headers: {
    "x-business-workflow-id": "workflow-123",
  },
});

The browser client accepts the same requestId, signal, and safe custom headers, but strips caller-provided authorization and x-hermes-tenant-id.

Delete Semantics

Delete operations archive or deactivate control-plane resources. deleteAgent and deleteProfile remove resources from normal SDK reads and writes, but historical sessions, runs, messages, memory audit data, and cost ledger records remain available to the platform for audit and retention. API key deletion revokes the key instead of removing its audit record.

Coverage

The server SDK covers tenant-scoped agent registry, profile registry, sessions, streaming, async runs, memory, session search, lineage, billing event export, and webhooks.

The browser SDK covers the current-user conversation surface: sessions, sync messages, streaming messages, async runs, run polling, cancellation, and run event replay.