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

@graphmind-ai/client

v0.5.1

Published

Adapter-agnostic runtime for the GraphMind live agent debugger: session, event transport, and the cooperative gate engine (pause / step / breakpoints / resume).

Readme

@graphmind-ai/client

Adapter-agnostic runtime for the GraphMind live agent debugger. This package owns the session (WebSocket transport + event buffer), the run context, and the gate engine — the cooperative pause points that let a viewer hold, step, inject into, retry, or abort a running agent.

It has no dependency on any AI SDK. Adapters (e.g. for Vercel's ai package) are separate packages that translate SDK callbacks/middleware into session.emit(...) and await session.gate(...).

Publishing note: private until the npm scope question is settled (the @graphmind scope is taken); see @graphmind-ai/schema's README.

Quick start (what an adapter does)

import { createSession } from '@graphmind-ai/client';

const session = createSession({
  appName: 'trip-planner',
  sdk: { name: 'ai', version: '7.0.79' },
});

await session.run('book-trip', async (ctx) => {
  session.emit('node.started', {
    nodeId: 'tool:searchFlights', kind: 'tool', name: 'searchFlights',
    instanceId: 'call-1', input: { from: 'VIE', to: 'LIS' },
  });

  const decision = await session.gate('before', {
    nodeId: 'tool:searchFlights', kind: 'tool', name: 'searchFlights',
  });
  if (decision.action === 'inject') return decision.output;   // skip execution
  if (decision.action === 'abort') throw ctx.signal.reason;   // see "Abort"

  // ... run the real tool, passing ctx.signal into SDK calls ...
});

await session.dispose();

Behavior guarantees

  • Never throws into the host. Every public method catches internal errors, degrades to a no-op, and logs one rate-limited console.warn (default: at most one line per failure kind per minute). Errors thrown by your function inside session.run are yours and propagate untouched.
  • Fail-open. If the viewer disconnects (or the session is disposed), every held gate resolves {action:'continue'} immediately — measured under 100ms in tests — and breakpoints/mode are forgotten until the next hello.ack re-arms them.
  • Free when detached. gate() short-circuits to a shared resolved promise when no viewer is attached or nothing matches. The test suite asserts average awaited-gate cost < 1ms (typical is microseconds; the spike measured 0.03ms worst-case).
  • Lazy, resilient transport. Nothing touches the network until the first run/emit/gate (or an explicit ready() — see the attach guarantee below). Connects get 300ms (connectTimeoutMs), the handshake 1s, then the session stays detached and retries in the background every 10s (retryIntervalMs). Losing an established attachment is treated as the urgent case it is: the first reconnects happen after 200ms, 400ms and 800ms before the steady-state interval takes over, because every millisecond dark is events being pushed through a finite buffer. (Measured with the soak harness: a blip costs 206ms dark, where the flat 10s interval cost 9.99s.) All timers are unref'd — the session never keeps your process alive.
  • Replay-on-attach. Events are kept in a bounded ring buffer (default 5000 frames or 8 MiB, whichever binds first, drop-oldest; bufferSize / maxBufferBytes). On attach the whole buffer is replayed oldest-first with original seq numbers, so a viewer that attaches mid-run still renders history (and deduplicates by seq on reconnects).
  • Loss is never silent. If the debugger is unreachable for longer than the buffer holds, the events that never made it are counted with their seq range and announced two ways: a gap marker on the next attach — a real graph.hint envelope carrying payload.gap = {droppedCount, fromSeq, toSeq, reason}, which the server stores and the viewer can render — and a rate-limited warning in your own logs. session.stats().lost is the honest count (dropped also counts frames that were delivered and then aged out of the replay buffer, which is not loss).

Attach guarantee: session.ready()

The transport is lazy, so a run that starts immediately after createSession can fail-open past its first gates before the handshake lands. When you need pause guarantees from the very first event:

const attached = await session.ready();            // default timeout 2000ms
const attached = await session.ready({ timeoutMs: 500 });

ready() force-starts the connection immediately (even before any emit) and resolves true once the handshake completes — breakpoints/mode from the hello.ack are armed before it resolves. It resolves false on timeout, and immediately when the session is disabled or disposed. It never throws (and never rejects): false means "still detached — carry on", keeping the fail-open contract. Concurrent calls share one connection attempt; a call after attachment resolves true instantly; after a disconnect a new call re-arms (it kicks an immediate reconnect instead of waiting out retryIntervalMs).

Kill switches

| Condition | Effect | |---|---| | GRAPHMIND_DISABLED=1 | Disabled. Beats everything, including enabled: true. | | enabled option set | As given (unless the above). | | NODE_ENV=production | Disabled unless GRAPHMIND=1. | | otherwise | Enabled. |

Disabled sessions no-op everything: no sockets, no buffering, no warnings — but session.run still executes your function and still hands it a working RunContext (ids + abort signal), so adapter code never needs to branch.

GRAPHMIND_URL overrides the default endpoint ws://127.0.0.1:4747/ingest (or pass url).

Recording less: the redaction switches (0.5.0)

Four whole-field kill switches stop values being recorded at all. Set them in the environment of the instrumented app (not the server), or pass them as session options on any adapter (graphmind({ hideInputs: true, … })); either source turning a switch on turns it on — the environment is a floor code cannot lower.

| Switch | Replaces with "__REDACTED__" | |---|---| | GRAPHMIND_HIDE_INPUTS / hideInputs | every node's input (prompts, messages, tool arguments, MCP params); streamed tool-argument deltas are emptied | | GRAPHMIND_HIDE_OUTPUTS / hideOutputs | every node's output; every streamed token delta is emptied (v: "", a chars length survives) | | GRAPHMIND_HIDE_TOOL_ARGS / hideToolArgs | only tool nodes' input | | GRAPHMIND_HIDE_TOOL_RESULTS / hideToolResults | only tool nodes' output |

What the tool-only switches do not cover. The tool-only switches hide the tool node's own input / output. In an agent loop the same values also travel through the model: its tool_use blocks (LLM output) and the tool_result messages of the next request (LLM input). To keep tool arguments and results out of the recording entirely, set GRAPHMIND_HIDE_INPUTS (and GRAPHMIND_HIDE_OUTPUTS). Error messages are never redacted.

Values 1 or true, case-insensitive (as options: true, 1, "1" or "true" — a privacy switch fails closed). Redaction runs inside the session before the ring buffer, so late-attaching debuggers, SQLite, exports, graphmind mcp-proxy recordings and the read-only MCP tools only ever see the placeholder; run names, node names, kinds, ids, timings and token counts are always recorded (graphmind mcp-proxy additionally drops the server's command-line arguments from the run's label and metadata under GRAPHMIND_HIDE_INPUTS). Affected events carry redaction: {count, keys}. node.error is never redacted — an error message can echo data, so HIDE_OUTPUTS is not a guarantee against error text. The Python SDK and the Ruby gem implement the same switches, env names, placeholder and wire fields (0.5), held to the same conformance fixtures.

Gating model

session.gate(point, node) — point is before | after | error, node is {nodeId, kind: agent|llm|tool|custom, name}. The decision is:

| decision | adapter's obligation | |---|---| | {action:'continue'} | proceed normally | | {action:'inject', output} | skip execution (or replace the failed result) and use output | | {action:'retry'} | re-run the node's execution (typically after an error gate) | | {action:'abort'} | stop the run — see below |

Pauses happen when a viewer is attached and a breakpoint matcher hits (kind?/name?/point?, point defaults to before) — or on every before/error gate in step mode (mode.set: step). While held, the session emits exec.paused; on release (viewer resume, pauseTimeoutMs auto-continue, disconnect fail-open, dispose) it emits exec.resumed.

Parallel gates are independent: two concurrent tool calls hold two pauses, each resumable on its own (spike assertions b.1–b.4).

Abort (why there is an AbortController)

Spike RESULTS.md, risk #4: throwing a plain Error out of SDK middleware lands in the SDK's retry logic — an "abort" would be retried maxRetries times before surfacing. So the abort path is cooperative cancellation instead:

  • Every session.run context carries an AbortController; ctx.signal must be passed into SDK calls by the adapter.
  • When a gate resolves {action:'abort'}, the session aborts that run's controller with an AbortError-named reason before the gate promise resolves. The adapter then throws ctx.signal.reason (or simply lets the SDK observe the signal). AI SDKs treat AbortError as terminal — no retries — and session.run records the run as status: 'aborted'.

Options reference

createSession({
  url,                 // default GRAPHMIND_URL ?? ws://127.0.0.1:4747/ingest
  appName, sdk, meta,  // reported in hello / run.started
  enabled,             // override kill-switch logic (GRAPHMIND_DISABLED still wins)
  connectTimeoutMs,    // 300
  handshakeTimeoutMs,  // 1000
  retryIntervalMs,     // 10_000 steady state (200/400/800ms burst after a blip)
  bufferSize,          // 5000 events, drop-oldest
  maxBufferBytes,      // 8 MiB — second, byte-wise bound on the same buffer
  pauseTimeoutMs,      // auto-continue held gates after N ms (default: hold forever)
  webSocket,           // WebSocket constructor override (default: global WebSocket, Node >= 22)
  logger, warnIntervalMs, env, // testing / embedding hooks
})

session.stats() returns {enabled, attached, buffered, dropped, lost, pendingGaps, heldGates, seq} for diagnostics. lost counts events that were evicted before ever reaching the debugger — real holes in the recorded run; dropped is the blunter lifetime eviction count and includes frames that were delivered first.

Scripts

  • pnpm typecheck — tsc over src + tests (schema resolved from source)
  • pnpm test — vitest: gate hold/resume/inject/retry/abort, parallel independence, disconnect fail-open < 100ms, detached overhead < 1ms, ring-buffer replay + overflow, gap markers + loss accounting + fast reconnect, handshake + version-mismatch detachment, kill switches, host-crash immunity
  • pnpm build — emit dist/ (ESM + .d.ts; requires @graphmind-ai/schema built first, which pnpm's topological ordering does for you)