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

@jsm-mit/agent-platform-canister-package

v0.4.0

Published

Wrapper TypeScript package for the agent-platform-canister canister.

Readme

Agent Platform Canister Package

A TypeScript library for interacting with the agent-platform-canister canister on the Internet Computer — agent configs (base fields + freeform custom JSON) and batched chat history for the agent platform. The wrapper is the only sanctioned way to talk to the canister: it hides HttpAgent/idlFactory wiring, unwraps the canister's Framework.Result<T> into "resolves with T or throws a known Error", and converts candid opt/variant/Principal shapes into idiomatic TypeScript.

Part of workspace-agent-platform; the canister itself lives in the sibling ../agent-platform-canister. Concept/architecture design lives in ~/workspace-jsm-company/jsm-ideas/agent-platform/.

Installation

npm install @jsm-mit/agent-platform-canister-package

Usage

Initialization

import {
    AgentConfigsActor,
    ChatHistoryActor,
    AdminActor,
} from "@jsm-mit/agent-platform-canister-package";

const canisterId = "your-canister-id";
const agentConfigsActor = new AgentConfigsActor(canisterId, identity); // identity optional
const chatHistoryActor = new ChatHistoryActor(canisterId, identity);
const adminActor = new AdminActor(canisterId, identity);

Permissions model

Everything is admin-gated. A fresh canister has no admins — the first caller of adminActor.registerAsAdminAsyncUnsafe() claims the role (e.g. agents-village's own service identity); further admins via addAdminAsyncUnsafe.

Agent configs

const config = await agentConfigsActor.upsertAgentConfigAsyncUnsafe({
    id: "kasia",
    name: "Kasia",
    persona: "Jesteś Kasią, sprzedawczynią mebli...",
    disclosesAsAi: false,
    // tools defaults to [] when omitted (a tool-less agent); customConfigJson too is
    // an optional escape hatch, JSON-encoded.
    tools: [
        { tool: "check_availability", paramsJson: '{"serviceId":"sultana"}' },
    ],
});
// Same call again with the same id updates in place — createdAt is preserved.

const fetched = await agentConfigsActor.getAgentConfigAsyncUnsafe("kasia");
const all = await agentConfigsActor.listAgentConfigsAsyncUnsafe(500);
await agentConfigsActor.deleteAgentConfigAsyncUnsafe("kasia");

Chat history — batched, mixed conversations/agents per call

A single flush from agents-village can mix entries from many conversations and agents — that's the whole point of flushing a buffer as one call rather than one call per message (AGENT-PLATFORM decision 2026-08-18). Any entry naming an unknown agentId refuses the whole batch.

await chatHistoryActor.appendMessageBatchAsyncUnsafe([
    {
        conversationId: "conv-1",
        agentId: "kasia",
        message: { role: "user", content: "hi", timestamp: 0n },
    },
    {
        conversationId: "conv-2",
        agentId: "lilly",
        message: { role: "user", content: "hey", timestamp: 0n },
    },
]);

const history = await chatHistoryActor.getConversationHistoryAsyncUnsafe(
    "conv-1",
    100,
);

Config change notifications (0.3.0)

The canister can report every real create, change or delete of an agent config as one task on a rabbit-motoko channel, so agents-village reloads only that agent. commonId is the agent id, the payload is {"agentId":"<id>","change":"upserted"|"deleted"}. Writing identical values and refused writes report nothing; a failed report is only logged on the canister, the write stands. The agent-platform canister must be allowed to add tasks on the channel in rabbit. Admin only.

await adminActor.setConfigChangeNotificationsAsyncUnsafe({
    rabbitCanisterId: "6aigz-vyaaa-aaaag-ayjqa-cai",
    channel: "agent-config-changes",
});
const settings = await adminActor.getConfigChangeNotificationsAsyncUnsafe(); // or null
await adminActor.setConfigChangeNotificationsAsyncUnsafe(null); // switch off

Agent placement: village and tags (0.4.0)

Every agent lives in a village — the agents-village process that runs it, "Development" unless placed elsewhere — and wears tags, labels the dashboard filters by (the canister attaches no meaning to them). Both come back on every AgentConfigView. On upsert they are optional: left out, a create lands in "Development" with no tags and an update keeps what is stored, so a caller that never heard of villages cannot move an agent. setAgentTagsAsyncUnsafe replaces the tags alone (the creating app and admins); a different set counts as a config change and is reported like any other.

const placed = await agentConfigsActor.upsertAgentConfigAsyncUnsafe({
    id: "kasia",
    name: "Kasia",
    persona: "...",
    disclosesAsAi: true,
    village: "SultanaProd",
    tags: ["salon", "vip"],
});
const retagged = await agentConfigsActor.setAgentTagsAsyncUnsafe("kasia", [
    "vip",
]);
const mine = (await agentConfigsActor.listAgentConfigsAsyncUnsafe(500)).filter(
    (c) => c.village === "Development",
);

Village: 1–50 chars, letters, digits, - and _, starting with a letter or digit. Tag: 1–30 chars, lowercase letters, digits and -, starting with a letter or digit; at most 20 per agent, no duplicates — InvalidData otherwise.

Error model

Every ...AsyncUnsafe method throws one of exactly three errors — switch on error.message, details are always on error.cause:

  • CanisterError — the canister returned a business #err; cause = { errorKey, errorMessage, logs } (e.g. errorKey: "NotAuthorized", "NotFound", "InvalidData").
  • CriticalCanisterError — transport failure or a canister trap; cause = { logs, rawError }.
  • CallRefusedAtInspectionStage — the canister's inspect gate refused the call before execution (oversized payload or anonymous caller).

API

  • AgentConfigsActor — upsertAgentConfigAsyncUnsafe, setAgentPersonaAsyncUnsafe, setAgentTagsAsyncUnsafe, getAgentConfigAsyncUnsafe, listAgentConfigsAsyncUnsafe, deleteAgentConfigAsyncUnsafe.
  • ChatHistoryActor — appendMessageBatchAsyncUnsafe, getConversationHistoryAsyncUnsafe.
  • AdminActor — admin allowlist and diagnostics: registerAsAdminAsyncUnsafe, addAdminAsyncUnsafe, removeAdminAsyncUnsafe, getAdminsAsyncUnsafe, setLoggingEnabledAsyncUnsafe, whoAmIAsyncUnsafe, getLogsAsyncUnsafe, clearLogsAsyncUnsafe, setConfigChangeNotificationsAsyncUnsafe, getConfigChangeNotificationsAsyncUnsafe (ConfigChangeNotificationsSettings | null).
  • Configs/messages are returned as AgentConfigView/ChatMessageView (opts flattened to optionals, the message role variant as a "user" | "assistant" | "tool" string union); raw candid types remain available from . and ./declarations/* exports.

Development

See CLAUDE.md for the canister-change workflow (declarations sync, wrapper updates). The scripts below deploy to mainnet and spend cycles — the user runs them; Claude never does.

  • npm run test-suite — reinstalls the current canister build into short-living, the short-lived test canister of motoko-crafting-table, with a 3-minute lease (no prompt), then runs tests/test-suite.ts with CANISTER_ID set to short-living. No .env: every identity is generated per run. Tests never touch backend5.
  • npm run test-smoke — the same, with tests/test-smoke.ts: one write and one read per domain (~20 seconds). publish-public runs it too.
  • npm run redeploy -- [slot] [reinstall|upgrade] [--force] [--wipe] — deploys the canister build without tests through ../agent-platform-canister's craft:upgrade/craft:deploy. Defaults: slot backend5 (the live agents of agents-village), mode upgrade (state kept). A reinstall of any slot but short-living needs --wipe and prints the restore steps (registerAsAdmin, addAdmin the village, addApp Sultana core, setConfigChangeNotifications).
  • npm run publish-public — bump the version by hand (uncommitted), then run it from master. It checks both repos are on a clean master equal to origin/master and the version is unpublished, reinstalls the canister master into short-living (lease 2 minutes) and refuses unless the candid short-living serves equals the committed declarations/ and the smoke test passes against that deploy. Then it stamps the canister commit into package.json, builds, publishes and commits "Version bump to ".

Requirements

  • Node.js 18+
  • TypeScript 5+

License

ISC