Skip to content

Observability · Guide

JavaScript and TypeScript

What the npm package actually ships: a dependency-free core, one subpath per framework, optional peers that stay optional, and a CLI for n8n. Everything here is about the package — the wiring for each framework has its own guide.

bash
npm install @cognipeer/observability
Package@cognipeer/observability
RuntimeNode.js 18 or newer
FormatsESM and CommonJS, with TypeScript declarations for both
DependenciesNone
Side effectsNone declared, so bundlers can tree-shake it
LicenceMIT

Subpath exports

Framework code lives behind its own subpath. Nothing loads until you import it, which is what keeps the install free of the framework you are not using.

SubpathWhat it exportsGuide
@cognipeer/observabilityinit, getClient, observe, trace, TraceSession, flush, shutdownAPI reference
@cognipeer/observability/langchainCognipeerCallbackHandler, cognipeerConfig, installLangChainTracingLangChain
@cognipeer/observability/langgraphlanggraphConfig, withCognipeerTracingLangGraph
@cognipeer/observability/openai-agentsCognipeerTracingProcessor, installOpenAIAgentsTracingOpenAI Agents SDK
@cognipeer/observability/claude-agent-sdkClaudeMessageTracer, traceQueryClaude Agent SDK
@cognipeer/observability/vercel-aiwithCognipeerTracing, cognipeerMiddleware, cognipeerTelemetry, installVercelAITracingVercel AI SDK
@cognipeer/observability/otelCognipeerSpanExporterOpenTelemetry
@cognipeer/observability/n8nthe execution bridge, also exposed as the cognipeer-n8n binaryn8n

The core exports are enumerated in the API reference; the framework symbols are documented where their wiring is, on the Console side.

Optional peer dependencies

Each framework is an optional peer, so npm will not install it and will not complain when it is missing. Install the one you already use at whatever version you already have.

PeerRangeNeeded for
@langchain/core>=0.1.0/langchain, /langgraph
@openai/agents>=0.0.1/openai-agents
@anthropic-ai/claude-agent-sdk>=0.1.0/claude-agent-sdk
ai>=3.0.0/vercel-ai
@opentelemetry/sdk-trace-base>=1.0.0/otel

The LangChain handler is written against the whole @langchain/core 0.1 to 1.x range rather than a pinned major, because an observability package that forces a framework upgrade is not deployable.

Async context and nesting

observe() nests automatically: a wrapped function called from inside another becomes its child span, and when no session is open the outermost call opens one and closes it when it settles. That relies on knowing which run the current execution belongs to.

On Node that is AsyncLocalStorage, which survives await boundaries and keeps concurrent runs apart. On runtimes without it — edge workers, browsers — the package falls back to a single module-level frame, which is correct for the one-request-per-isolate model those runtimes use.

One wrinkle worth knowing on Node 18 and 20: the package resolves AsyncLocalStorage synchronously where the runtime allows it (process.getBuiltinModule, Node 22.3+), and asynchronously otherwise. In the async case, work traced in the very same tick as init() can land on the fallback frame. trace() waits for the context to be ready before it opens its session, so the ordinary shape — init(), then trace(...) — is covered. If you instrument with bare observe() calls at startup on Node 18 or 20, await contextReady after init() first.

Flushing and process exit

Exports run on a background promise chain, off the framework's hot path, so a long-running service never needs to do anything. A short-lived process must wait for delivery before it exits, or the tail of the session is lost:

ts
import { flush, shutdown } from '@cognipeer/observability';

export async function handler(event) {
  await trace({ name: 'lambda-agent' }, async () => { /* … */ });
  await flush();      // or shutdown() to also close still-open sessions
}

A beforeExit hook is installed automatically and covers ordinary shutdown, but a runtime that freezes the process between invocations — as serverless platforms do — can cut it off. Call flush explicitly there. Both flush and shutdown are safe to call more than once.

There is one subtlety the package handles for you: session.end() returns before the network settles, deliberately, so a framework callback is never blocked on an export. The client keeps a settling session tracked anyway, so await flush() still covers a session whose last request is in flight.

The cognipeer-n8n CLI

The package ships a binary that mirrors n8n workflow executions into Console by polling the public REST API — no files on the n8n instance, no restart, and it works on n8n Cloud as well as self-hosted:

bash
npx --package=@cognipeer/observability cognipeer-n8n \
  --n8n-url https://n8n.acme.com \
  --n8n-api-key "$N8N_API_KEY" \
  --api-key "$COGNIPEER_API_KEY" \
  --once

--once mirrors the current page and exits, which is the fastest way to verify the wiring end to end; drop it to poll continuously. Every flag also reads an environment variable, so a container needs no arguments at all. The full flag table, the push-based external-hook alternative, and what n8n can and cannot report are in the n8n guide.

Building an integration yourself

TraceSession is the primitive every shipped integration is built on, and it is exported. When a framework gives you paired start and end callbacks, openSpan(key, init) and closeSpan(key, close) turn one pair into exactly one Console event carrying both sides, with the duration measured for you. See the session API for a worked example and Contributing for the rules an integration has to follow.

Next

Studio · Pulse · Console · Agent SDK and more — the Cognipeer documentation hub