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.
npm install @cognipeer/observability| Package | @cognipeer/observability |
| Runtime | Node.js 18 or newer |
| Formats | ESM and CommonJS, with TypeScript declarations for both |
| Dependencies | None |
| Side effects | None declared, so bundlers can tree-shake it |
| Licence | MIT |
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.
| Subpath | What it exports | Guide |
|---|---|---|
@cognipeer/observability | init, getClient, observe, trace, TraceSession, flush, shutdown | API reference |
@cognipeer/observability/langchain | CognipeerCallbackHandler, cognipeerConfig, installLangChainTracing | LangChain |
@cognipeer/observability/langgraph | langgraphConfig, withCognipeerTracing | LangGraph |
@cognipeer/observability/openai-agents | CognipeerTracingProcessor, installOpenAIAgentsTracing | OpenAI Agents SDK |
@cognipeer/observability/claude-agent-sdk | ClaudeMessageTracer, traceQuery | Claude Agent SDK |
@cognipeer/observability/vercel-ai | withCognipeerTracing, cognipeerMiddleware, cognipeerTelemetry, installVercelAITracing | Vercel AI SDK |
@cognipeer/observability/otel | CognipeerSpanExporter | OpenTelemetry |
@cognipeer/observability/n8n | the execution bridge, also exposed as the cognipeer-n8n binary | n8n |
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.
| Peer | Range | Needed 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:
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:
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
- API reference — every exported symbol and option
- Examples — runnable TypeScript programs
- Python — the other half of the library
- Troubleshooting

