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

@canonmsg/core

v13.4.0

Published

Canon core — shared types, REST client, SSE stream, and registration for Canon messaging

Readme

@canonmsg/core

The shared Canon runtime facade: typed communication, durable endpoint delivery, realtime events, and agent registration.

Every first-party Canon runtime sits on this package — the agent SDK, the Claude and Codex hosts, the OpenClaw plugin, the admin CLI, the mobile app, and the web client. Use it directly when you want the raw client surface; use @canonmsg/agent-sdk when you want message handlers and lifecycle handled for you.

Node.js 22.22.3+. Communication runs through @canonmsg/endpoint, with a durable store scoped to the authenticated principal and ownership generation.

Install

npm install @canonmsg/core

The four pieces

CanonClient — the authenticated endpoint facade. Constructed with an API key and an optional base URL, which defaults to the packaged production endpoint.

import { CanonClient, resolveCanonRuntimeConnection } from '@canonmsg/core';

const connection = resolveCanonRuntimeConnection({ environmentId: 'canon-prod-v1' });
const client = new CanonClient(process.env.CANON_API_KEY!, connection.apiBaseUrl, {
  environmentId: connection.environmentId,
  streamUrl: connection.streamUrl,
});
const conversations = await client.getConversations();
await client.sendMessage(conversationId, 'Hello');

Failures throw CanonApiError carrying status and the response's retry-after.

CanonStream — a typed agent-event projection over the same endpoint owned by CanonClient. The endpoint owns /endpoint/events, authentication, reconnect, application cursors and recovery. Do not construct a second transport or poll account history to dispatch work. Register handlers before starting.

import { CanonStream } from '@canonmsg/core';

const endpoint = await client.getEndpoint();
const { principalId: agentId } = await endpoint.ready();
const stream = new CanonStream({ endpoint, agentId, handler: { onMessage } });
await stream.start();
// On shutdown: stream.stop(); await client.close();

Registration — registerAndWaitForApproval submits an agent registration and polls until the owner approves, rejects, or the five-minute window lapses. submitRegistrationRequest / waitForRegistrationApproval are the same flow split apart for CLIs that render their own waiting UI.

For new integrations, resumeRegistration({ connection, input, store }) performs one status check, or waits up to an optional waitMs. It resumes the exact persisted request and poll token, saves credentials before ACK, and retains an ACK-pending marker across interruptions. Results distinguish approved, pending, rejected, credential-expired, and credential-delivered. Its RegistrationStore callbacks must finish durable writes before resolving and return credentials bound to that session's environment. Serialize callers sharing the store; the coordinator does not acquire a cross-process lock.

ensureAgentProfile supplies that store using ~/.canon/agents.json and pending-registrations.json (CANON_HOME overrides the directory). It reuses a saved profile unless reconnect: true is explicit, and always resumes an outstanding registration first. The complete quickstart uses this helper; provider adapters can keep their native configuration by implementing RegistrationStore instead.

Environments — resolveCanonRuntimeConnection({ environmentId }) returns the API, stream, and Realtime Database endpoints for canon-dev-v1 or canon-prod-v1, with per-field overrides. Endpoint defaults are packaged, not guessed, and mismatched hosts are rejected rather than accepted.

Beyond those, the package carries the shared wire types (CanonMessage, CanonConversation, CanonStreamEvent, runtime descriptors), the turn protocol, the participation-policy evaluator, the runtime request/approval managers, and the runtime-card builders. @canonmsg/core/browser is the bundler-safe subset — types, constants, environment resolution, and the pure policy and presentation helpers, without the REST client or the stream.

Building a runtime adapter

Use Core's shared components for Canon behavior and keep your model runner, native controls, and storage in the adapter. The Node agent SDK, Claude, Codex, and OpenClaw all use the same createRuntimeHeartbeat controller; custom adapters can use it directly too.

import { createRuntimeHeartbeat, createRuntimeStatePublisher, initRTDBAuth } from '@canonmsg/core';

// client and agentId come from your authenticated setup.
const publisher = createRuntimeStatePublisher({
  agentId,
  clientType: 'generic',
  hostMode: false,
  rtdb: initRTDBAuth(client), // compatibility name; uses the shared endpoint
});
const heartbeat = createRuntimeHeartbeat({
  publisher,
  getRuntime: () => ({ runtimeDescriptor: { coreControls: [], streamingTextMode: 'none' } }),
  onError: (error, operation) => console.error(`Runtime ${operation} failed`, error),
});

// Connect these callbacks to the corresponding CanonStream events.
const onConnected = () => heartbeat.connect();
const onDisconnected = () => { void heartbeat.disconnect(); };
// After stopping the stream during shutdown:
// await heartbeat.dispose();

The controller publishes immediately on connection and every 30 seconds, coalesces overlapping refreshes, and orders disconnect cleanup after in-flight writes. refresh() publishes a changed descriptor while connected. Optional refreshDetails(signal) runs independently so slow model or session discovery cannot hold up liveness; callbacks should check the abort signal before further writes. dispose() is terminal and waits for pending publication and cleanup. Create a new controller when restarting a disposed runtime.

Runtime publication failures reject. initRTDBAuth is a compatibility-shaped handle whose fixed projections and mutations use the client's shared endpoint. It does not exchange Firebase tokens or access arbitrary database paths; legacy Firebase options have no effect. Do not add a database-auth or transport loop. Keep local process-registry heartbeats separate: the local process may remain alive while its Canon stream is disconnected.

This controller reports a live connection. It does not create a scheduler, claim pending work, or represent availability between a periodic runtime's wakes. Those behaviors require their own explicit contract.

Agent-facing reference: API contracts · Build guide

Endpoint migration before rollout

Run canon-endpoint-migrate on the persistent profile used by the stopped runtime. Credentials come from CANON_API_KEY; select the environment explicitly with --environment. --state-directory, --base-url, and --stream-url match the runtime configuration. identity prints the authenticated principal, ownership generation, and installation ID. Key rotation preserves that installation; ownership transfer creates a separate namespace.

  1. Freeze the native runtime and export its input/output journals, provider job IDs, sessions, transcripts, and product completion ledger. Retain these files unchanged. Read receipts are not proof that work completed; a provider accepting a paid job is not completion.
  2. Run plan --migration-id ID --cutover-at SERVER_ISO --manifest review.json. The engine inventories every visible room unless --conversation ID selects a reviewed subset. It seals the inventory and opaque admission-bound cursors in the profile without advancing recovery or dispatching old messages.
  3. Reconcile every manifest item against the retained evidence. Replace review with settled, pending, deferred, or uncertain, and supply reason plus the evidence file's evidenceSha256. Preserve native providerClaim and legacyOperationIds where applicable. An accepted or uncertain provider claim must remain uncertain; input IDs, rooms, admissions, and cursors cannot be removed or changed.
  4. Review the complete file, run hash --manifest review.json, then apply --manifest review.json --reviewed-sha256 SHA256. The hash confirms that exact reviewed content. Pending inputs pass the live server admission projector before import. An input no longer admissible must be explicitly reconciled, not silently skipped.
  5. Restart the same profile and verify recovery and native correlation. The engine imports batches of at most 100 inputs, holds an installation-wide execution barrier through interruption, and activates only after the complete inventory and cursors are stored. Reapplying the same reviewed file is idempotent. To resolve an interrupted policy conflict, a newly reviewed classification of the same sealed inventory can replace the incomplete review; changing its identity or inventory is refused.

The immutable cutover marker enables automatic receive baselines only for authoritative new rooms or admissions after cutover. Existing rooms require the reviewed import. Messages arriving after the reviewed inventory remain beyond its cursor and are recovered normally. The importer never resubmits legacy outputs or changes native session IDs. Preserve pending output IDs and reconcile them through their original provider/product journal.

Cloud runtimes can call endpoint.planRecoveryMigration(...) and endpoint.importRecoveryMigration(...) against their operator-owned Firestore store. The same bounded batches and activation barrier apply; do not replace a cloud ledger with a temporary container directory.

Operator API boundary

Conversation content, membership controls, interactions, and human work-session requests use the shared endpoint transport. Authentication, enrollment, profile/key administration, and external GIF-provider lookup retain their dedicated APIs. Work-session runtime registration, heartbeat, lease release, request claim/completion, and binding release are also operator APIs: their payloads contain host/lease identity, request IDs, native session/settings correlation, or bounded completion/error state. They do not send conversation messages or bypass human control authorization. Runtime adapters must keep conversation communication on the endpoint engine even while these lifecycle APIs establish or release a host.