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

@goplausible/ac2-sdk

v0.0.8

Published

AC2 — Agentic Communication and Control Protocol SDK

Readme

AC2 SDK

npm license

TypeScript SDK for the AC2 — Agentic Communication and Control Protocol. AC2 is a protocol for secure, identity-bound communication between autonomous agents and Controllers (wallets), built on Liquid Auth + WebRTC + DIDComm v2.

It is the protocol library: messages, envelopes, signaling, sessions, streaming, file transfer, replay defense, DID resolution. It does not include UI, wallet plumbing, or key management — those live in the Controller (the wallet that approves signing requests) and in agent-side plugins.

Companion projects in this monorepo — each maintains its own README. The SDK README focuses on the SDK and protocol; nothing else.

Status (v0)

| Component | Version | | --- | --- | | @goplausible/ac2-sdk | 0.0.4 | | @goplausible/ac2-plugin-openclaw | 0.0.31 | | regent (reference Controller) | 1.0.84 |

v0 ships the core protocol over WebRTC with a PlainEnvelope (no DIDComm wrapping yet). DIDComm v2 wrapping is planned post-POC; see vendor/didcomm-ts/PORT_PLAN.md.

What's working end-to-end: bidirectional text chat, ed25519 sign/verify HITL, seamless reconnect across idle / app-suspend / network-blip / plugin-restart, biometric-gated approval, snake_case wire format aligned with DIDComm v2, the agent badge classifier (sticky liquid → agent mode upgrade on first AC2 envelope), Notice envelopes for agent-side status, and chat-limbo grace timeout for survival across user back-out.

Spec compliance and known v0 deviations (channel label, single-channel transport, to[] optional, ac2/Stream* and ac2/AttachmentBegin SDK profiles, Approval* not in spec) are tracked in .notes/copilot-pr-review-2026-05-02.md and the SDK source comments.

Install

npm install @goplausible/ac2-sdk
# or
pnpm add @goplausible/ac2-sdk
# or
yarn add @goplausible/ac2-sdk

Peer requirements:

  • Node >= 20 (the package is ESM-only).
  • A WebRTC implementation — browser-native RTCPeerConnection / RTCDataChannel in the browser, @roamhq/wrtc (or compatible) in Node.

The SDK does not bundle a WebRTC implementation; you bring your own and pass an RTCDataChannel (or pair of channels) into the session.

Quick start — agent side

Wire the SDK on the agent (Node) side once Liquid Auth has handed you a live RTCDataChannel. The pattern: build channels, build a session, send ac2/SigningRequest, await ac2/SigningResponse.

import {
  AC2Session,
  attachAC2Channels,
  PlainEnvelope,
  pubkeyToDidKey,
  type SigningRequest,
  type SigningResponse,
} from "@goplausible/ac2-sdk";

// 1. Wrap the existing Liquid Auth control channel into AC2's transport shape.
//    v0 is single-channel; pass the peer connection plus the control channel.
const channels = await attachAC2Channels(peerConnection, controlDataChannel);

// 2. Build the session. `from` is the agent's DID, `to` is the wallet's DID
//    (resolved via Liquid Auth attestation).
const agentDid = pubkeyToDidKey(agentEd25519PublicKey);

const session = new AC2Session({
  channels,
  identity: { from: agentDid, to: [walletDid] },
  envelope: new PlainEnvelope(), // default; replace once DIDComm v2 lands
});

// 3. Hook responses before sending the request.
const pending = new Promise<SigningResponse>((resolve, reject) => {
  const off = session.onMessage((msg) => {
    if (msg.type === "ac2/SigningResponse" && msg.thid === reqId) {
      off();
      resolve(msg);
    } else if (msg.type === "ac2/SigningRejected" && msg.thid === reqId) {
      off();
      reject(new Error((msg.body as { reason: string }).reason));
    }
  });
});

// 4. Send the SigningRequest. The Controller wallet (Regent) prompts the
//    user, gates with biometrics, signs with the right key, and emits the
//    SigningResponse over the same channel.
const reqId = crypto.randomUUID();
const request: SigningRequest = {
  id: reqId,
  type: "ac2/SigningRequest",
  from: agentDid,
  to: [walletDid],
  created_time: Math.floor(Date.now() / 1000),
  body: {
    description: "Sign this proposal",
    encoding: "base64",
    payload: btoa("hello world"),
    key_type: "identity",      // 'account' or 'identity'
    display_hint: "text",      // 'text' | 'json' | 'hex'
  },
};
await session.send(request);

const response = await pending;
console.log("signature:", response.body.signature);
console.log("public key:", response.body.public_key);

Quick start — Controller (wallet) side

Most consumers of the SDK on the wallet side won't construct a session manually — they'll either use Regent as-is or implement a WithAC2 extension. But for reference, the parsing path looks like this:

import { validateMessage, type AC2Message } from "@goplausible/ac2-sdk";

controlChannel.addEventListener("message", async (ev) => {
  if (typeof ev.data !== "string") return;
  let parsed: unknown;
  try {
    parsed = JSON.parse(ev.data);
  } catch {
    return; // not an AC2 envelope; treat as chat text
  }
  if (!validateMessage(parsed)) return;
  const msg: AC2Message = parsed;
  switch (msg.type) {
    case "ac2/SigningRequest":
      await handleSigningRequest(msg);
      break;
    case "ac2/Notice":
      surfaceNotice(msg);
      break;
    // ... other types
  }
});

For a complete reference implementation see Regent's useConnection hook which handles the full lifecycle: incoming envelope dispatch, sticky mode-upgrade, signing-modal flow, biometric gate, notice banners, and reconnect resilience.

Modules & exported API

The SDK is intentionally small and tree-shakeable. Top-level exports from @goplausible/ac2-sdk:

| Module | Key exports | Purpose | |---|---|---| | identity | pubkeyToDidKey, algorandAddressToDidKey, DidKeyResolver, DidWebResolver, Did, DidDocument, DidResolver | DID derivation & resolution (did:key, did:web). | | transport | attachAC2Channels, AC2Signaling, AC2Channels, CONTROL_CHANNEL_LABEL, DATA_CHANNEL_LABEL, FILE_CHUNK_SIZE | Wraps Liquid Auth signaling + DataChannels into AC2's AC2Channels shape. | | envelope | PlainEnvelope, Envelope, PackedEnvelope | Pluggable envelope layer. v0 uses PlainEnvelope; DIDComm v2 lands later as a drop-in. | | messages | AC2Message, SigningRequest, SigningResponse, SigningRejected, StreamRequest, StreamResponse, StreamChunk, AttachmentBegin, AttachmentRef, Notice, validateMessage | Wire-message types + Zod-free runtime validator. | | session | AC2Session, AC2SessionOptions, SessionIdentity, MessageHandler, StreamEventHandler | High-level session: send/receive AC2 messages, manage streams, attachments, replay dedupe. | | streaming | StreamReader, StreamWriter, StreamEvent, StreamUsage | Chunked streaming primitives used internally by AC2Session.writeStreamChunk/onStream. | | files | FileSender, FileReceiver, ReceivedFile | Attachment send/receive over the ac2-data binary channel (cold-path in v0). | | replay | ReplayDedupe | LRU dedupe keyed by (channel, message id). Used by default by AC2Session. |

Wire profile (v0 — single-channel)

v0 runs single-channel: AC2 messages travel as JSON text frames on the existing liquid DataChannel from liquid-auth-js. The optional second ac2-data binary channel for file transfer is not wired in v0 (machinery exists in src/files/ but is cold-path).

| Channel | Label on the wire | Type | Purpose | | --- | --- | --- | --- | | Control | liquid | text | All AC2 protocol messages (signing trio + lenient plain text), reliable + ordered. | | Data | ac2-data | binary | Reserved; not created in v0. File chunks when wired. |

Spec target label is ac2-v1 (see specs/ac2.md § WebRTC DataChannel Transport). Switching to ac2-v1 requires a coordinated SDK + plugin + Controller release; tracked as a v0 deviation.

Mode: lenient text on the control channel (Path B). Plain text from the wallet is treated as user chat; JSON frames are parsed as AC2 envelopes (ac2/SigningRequest / ac2/SigningResponse / ac2/SigningRejected / ac2/Notice). Live duplex audio and video are out of scope for v0.

SDK layout

src/
├── identity/    DID derivation + resolution (did:key, did:web)
├── transport/   AC2Signaling + AC2Channels (wraps Liquid Auth DataChannels)
├── envelope/    PlainEnvelope (default); DIDComm v2 later
├── messages/    Wire-message types + runtime validator
├── streaming/   StreamReader / StreamWriter
├── files/       FileSender / FileReceiver (cold-path in v0)
├── replay/      Per-channel replay dedupe
└── session/     AC2Session — high-level send/receive

For the OpenClaw plugin source, see packages/ac2-plugin-openclaw/. For the Controller reference, see vendor/regent/. Both have their own READMEs.

Build & test

npm install
npm run build       # tsc → dist/
npm test            # vitest run
npm run typecheck   # tsc --noEmit

Pack a fresh SDK tarball:

# 1. Bump version in package.json + package-lock.json.
# 2. Build, then pack.
npm run build
npm pack --pack-destination /tmp
# → /tmp/goplausible-ac2-sdk-<version>.tgz

Recently shipped

A short log of what landed in the SDK + ecosystem since v0 first ran end-to-end. Detailed history is in commit logs and the per-component changelogs.

  • Sticky liquid → agent mode upgrade on first structured envelope — wallets can classify the badge before the first AC2 message even arrives if the agent emits a session.opened Notice on bind.
  • ac2/Notice envelope wired end-to-end — agent emits curated error/warn/info; Controller surfaces banners + persists to per-connection logs with FIFO rotation.
  • agent_type self-identification lifted from any nesting (root / body / metadata, snake or camel) — free-form so new agent authors slot in without coordinating with the Controller.
  • Lenient text mode (Path B) on the control channel so non-AC2 chat from the wallet doesn't error out the AC2 parser.
  • Replay dedupe scoped per channel label (liquid / ac2-data) so future second-channel addition doesn't cross-pollinate.
  • OpenClaw plugin v0.0.31 — bundles SDK v0.0.4, proactive session.opened Notice on bind, agent_type: "openclaw" top-level on Notice + SigningRequest.

SDK / protocol TODO

Items in flight on the SDK and spec side. Regent-side and Controller-UX items live in the Regent README.

  • DIDComm v2 envelope — v0 ships PlainEnvelope; the spec target is full DIDComm v2 wrapping (from always present, to[] required, replay defense, audit, forward routing). The Envelope interface is the swap-in point. Plan parked at vendor/didcomm-ts/PORT_PLAN.md.
  • TypeScript DIDComm port from vendor/didcomm-rust (SICPA reference). No reliable pure-TS DIDComm SDK exists today; the port becomes @goplausible/didcomm and unblocks the DIDComm v2 envelope above.
  • ac2/AttachmentBegin + ac2-data binary channel — file-transfer machinery exists in src/files/ but is cold-path. Wire the second negotiated DataChannel on both peers, add the Regent-side recording UX, ship voice messages and file attachments for chats.
  • Discovery spec implementation — spec drafted at specs/ac2-ext-discovery.md; SDK + plugin code still to come.
  • A2A extension — spec drafted at specs/ac2-ext-a2a.md; agent-to-agent communication primitives.
  • AP2 mandates — generation + verification of agent payment mandates; envelope type and helper functions.
  • MPP session spec + matching SDK primitives — the Multi-Party Payment session work is gated on the spec landing first.
  • ERC-8004 support — agent-side, for AC2 sessions that involve EVM signing.
  • Claude plugin — sibling to OpenClaw under packages/. Top-level agent_type: "claude" is already wired into Regent's badge classifier; the plugin itself is open.
  • Codex plugin — same shape as Claude, for the Codex runtime. Third reference plugin alongside OpenClaw + Claude.
  • ac2 CLI — auto-injects the AC2 toolset into an existing agent runtime: detects the host framework, installs + configures the matching plugin, scaffolds the user-journey config, and provides the agent with signing-request / verification-request tooling. Goal: drop AC2 into an existing agent in one command.

SDK / protocol ROADMAP

Bigger arcs, not strictly ordered:

  • Channel-label cutover to ac2-v1. Coordinated SDK + plugin + Controller release that flips the control channel label from liquid to the spec-target ac2-v1. Tracked as a v0 deviation.
  • Second-channel (ac2-data) negotiation turned on by default once @roamhq/wrtc renegotiation interference is verified resolved — unblocks live file transfer and large attachments end-to-end.
  • Notice as the agent-side observability stream. Today four sinks (banner, log, activity, badge counter). Future: structured fields per code, per-tenant filtering, severity-aware retry/backoff hints carried in the envelope.
  • Pluggable session policies. Per-session signing-key policy ("this agent can only request key_type: identity"), per-agent rate limits, time-bound capability tokens — all enforced at the SDK boundary so Controllers don't have to reimplement the logic.
  • Test-vector parity with the Rust DIDComm reference — the TS port lands with a vector suite that round-trips against didcomm-rust outputs.

Contributing

This SDK is part of GoPlausible. Issues, PRs, and protocol-spec discussions welcome — see specs/ for the wire formats.

License

Apache-2.0