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

@zkstash/sdk

v0.12.1

Published

TypeScript SDK for zkstash - Memory layer for AI agents on EVM and Solana

Readme

ZKStash SDK

TypeScript SDK for https://zkstash.ai - Long Term Memory Management for AI agents.

Installation

npm install @zkstash/sdk

Quick start

If you just need a ready-to-go client and are fine using the same private key for both ZKStash auth and x402 payments, use fromPrivateKey:

import { fromPrivateKey } from "@zkstash/sdk/rest";

async function main() {
  const client = await fromPrivateKey(process.env.PRIVATE_KEY as `0x${string}`);

  await client.createMemory({
    userId: "user_demo",
    agentId: "agent_demo",
    conversation: [
      { role: "user", content: "Met Alex for coffee in SF." },
      { role: "assistant", content: "Great, logging it." },
    ],
  });
}

MCP Client

The SDK includes a Model Context Protocol (MCP) client.

import { fromPrivateKey } from "@zkstash/sdk/mcp";

async function main() {
  const client = await fromPrivateKey(process.env.PRIVATE_KEY!, {
    agentId: "agent_demo",
    payment: {
      maxValue: 10000n, // Optional payment cap
    },
  });

  // List available tools
  const tools = await client.listTools();
  console.log(tools);

  // Call a tool
  const result = await client.callTool({
    name: "memory_search",
    arguments: {
      query: "test query",
      agentId: "agent_demo",
    },
  });
  console.log(result);
}

Advanced usage (custom wallets)

To use different keys for auth and payments (recommended), build your own signers via x402-fetch and pass them to the ZkStash constructor.

EVM

import { createSigner } from "x402-fetch";
import { ZkStash } from "@zkstash/sdk/rest";

const authSigner = await createSigner("base-sepolia", process.env.AGENT_KEY!);
const paymentSigner = await createSigner("base-sepolia", process.env.X402_KEY!);

const client = new ZkStash({
  signer: authSigner,
  payment: {
    signer: paymentSigner,
    maxValue: 5_000n,
  },
});

Solana

import { createSigner } from "x402-fetch";
import { ZkStash } from "@zkstash/sdk/rest";

const authSigner = await createSigner("solana-devnet", process.env.AGENT_KEY!);
const paymentSigner = await createSigner(
  "solana-devnet",
  process.env.X402_KEY!
);

const client = new ZkStash({
  signer: authSigner,
  payment: {
    signer: paymentSigner,
  },
});

Once the client is created you can call any endpoint:

const memories = await client.searchMemories({
  query: "coffee",
  filters: { agentId: "agent_demo" },
});

console.log(memories);

Memory TTL (Time-to-Live)

Memories can have an optional expiry time. Expired memories are automatically cleaned up.

Creating Memories with TTL

// Per-memory TTL
await client.storeMemories("agent-1", [
  { kind: "SessionContext", data: { task: "booking" }, ttl: "24h" },
  { kind: "Reminder", data: { text: "Follow up" }, ttl: "1h" },
]);

// Default TTL for all memories in request
await client.storeMemories(
  "agent-1",
  [
    { kind: "SessionContext", data: { task: "booking" } },
    { kind: "Preference", data: { color: "blue" } },
  ],
  { ttl: "7d" }
);

// Using explicit timestamp
await client.storeMemories("agent-1", [
  {
    kind: "Meeting",
    data: { title: "Sync" },
    expiresAt: Date.now() + 24 * 60 * 60 * 1000, // 24 hours
  },
]);

Updating Memory Expiry

// Set new expiry
await client.updateMemory("mem_abc123", {
  expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000, // 7 days from now
});

// Remove expiry (make permanent)
await client.updateMemory("mem_abc123", {
  expiresAt: null,
});

Batch Operations

For better performance with multiple memories, use batch operations.

Batch Search

const results = await client.batchSearchMemories([
  { query: "user preferences", filters: { agentId: "assistant" } },
  { query: "recent tasks", filters: { agentId: "assistant" } },
  { query: "meeting notes", filters: { agentId: "assistant" } },
]);

// results.results[0].memories - first query results
// results.results[1].memories - second query results

Batch Delete

const result = await client.batchDeleteMemories([
  "mem_abc123",
  "mem_def456",
  "mem_ghi789",
]);
console.log(`Deleted ${result.deleted} memories`);

Batch Update

// Tag multiple memories
await client.batchUpdateMemories(
  ["mem_abc123", "mem_def456"],
  { tags: ["archived", "q4-2024"] }
);

// Set expiry on multiple memories
await client.batchUpdateMemories(
  ["mem_abc123", "mem_def456"],
  { expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000 }
);

Memory Sharing with Grants

ZKStash supports permissionless memory sharing between agents using cryptographic grants. A grant is a signed message that allows one agent to access another agent's memories.

Creating a Grant

import { fromPrivateKey } from "@zkstash/sdk/rest";

// Agent A creates a grant for Agent B
const agentA = await fromPrivateKey(process.env.AGENT_A_KEY!);

const { grant, shareCode } = await agentA.createGrant({
  grantee: "0x...", // Agent B's wallet address (or Clerk userId for API key users)
  agentId: "researcher", // Optional: limit to specific agent
  duration: "7d", // Grant lasts 7 days (e.g., "1h", "24h", "30d")
});

// Share the code with Agent B (via any channel)
console.log("Share this code:", shareCode);

Note: For API key users, use their Clerk userId (e.g., user_2abc123...) as the grantee. This is displayed in the zkStash dashboard.

Using a Grant

import { fromPrivateKey } from "@zkstash/sdk/rest";

// Agent B receives the share code and adds it
const agentB = await fromPrivateKey(process.env.AGENT_B_KEY!);

// Add grant for automatic inclusion in all searches
agentB.addGrant(shareCode);

// Now searches will include Agent A's shared memories
const results = await agentB.searchMemories({
  query: "research findings",
  filters: { agentId: "researcher" },
});

// Results include source annotations
results.memories.forEach((m) => {
  if (m.source === "shared") {
    console.log(`From ${m.grantor}:`, m.data);
  }
});

Search Scopes

Control which memories to search using the scope option:

// Search only your own memories (ignores grants)
await client.searchMemories(
  { query: "preferences", filters: { agentId: "my-agent" } },
  { scope: "own" }
);

// Search only shared memories (from grants)
await client.searchMemories(
  { query: "findings", filters: { agentId: "researcher" } },
  { grants: [grantFromResearcher], scope: "shared" }
);

// Search both own + shared (default)
await client.searchMemories(
  { query: "everything", filters: { agentId: "any" } },
  { grants: [grantFromA] } // scope defaults to "all"
);

Grant Management

// Add a grant (accepts SignedGrant object or share code string)
client.addGrant(shareCode);
client.addGrant(grantObject);

// Remove a grant
client.removeGrant(grantObject);

// Get all instance grants
const grants = client.getInstanceGrants();

// Pass grants per-request without storing them
await client.searchMemories(
  { query: "...", filters: { agentId: "..." } },
  { grants: [oneTimeGrant] }
);

Development

Testing

To run the E2E tests, you need to set the TEST_PRIVATE_KEY environment variable.

# Run REST client E2E test
TEST_PRIVATE_KEY=your_private_key npm run e2e

# Run MCP client E2E test (requires local MCP server)
TEST_PRIVATE_KEY=your_private_key npm run mcp-e2e