TypeScript and JavaScript SDK
Package: @progress/observability
Overview
The TypeScript/JavaScript SDK instruments AI agents and LLM applications running on Node.js. It supports both ESM and CommonJS module systems.
Use this SDK when your agent is built with TypeScript or JavaScript and uses frameworks like LangChain.js, Vercel AI SDK, OpenAI Node, Anthropic, or any of the other supported providers.
Core capabilities:
- Automatic instrumentation of LLM providers and agent frameworks
- Manual instrumentation with
@task,@workflow,@agent,@tooldecorators andwrapFunctionWithSpan() - Tag propagation with
propagateAttributes() - ESM loader hooks for transparent module patching
- Content tracing control
Installation
npm install @progress/observability
Initialization
Initialization depends on your module system.
ESM / TypeScript — Bootstrap File (Recommended)
Create a separate bootstrap file that initializes instrumentation before your app loads:
// bootstrap.ts — ESM instrumentation bootstrap
import '@progress/observability/register/hooks';
import dotenv from 'dotenv';
dotenv.config();
import { Observability } from '@progress/observability';
await Observability.instrument({
appName: 'my-ai-agent',
apiKey: process.env.OBSERVABILITY_API_KEY
});
// Load your main app after instrumentation is ready
await import('./src/app.ts');
Run with: tsx bootstrap.ts
ESM / TypeScript — Inline (No Bootstrap File)
Initialize with dynamic imports at the top of your main entry point:
// src/app.ts — ESM without bootstrap file
import '@progress/observability/register/hooks';
const { Observability } = await import('@progress/observability');
import dotenv from 'dotenv';
dotenv.config();
await Observability.instrument({
appName: 'my-ai-agent',
apiKey: process.env.OBSERVABILITY_API_KEY
});
// Your application code here
CommonJS / Synchronous CommonJS
// CommonJS / Synchronous initialization
import { Observability } from '@progress/observability';
import dotenv from 'dotenv';
dotenv.config();
await Observability.instrument({
appName: 'my-ai-agent',
apiKey: process.env.OBSERVABILITY_API_KEY,
debug: true
});
// Your application code here
When using ESM with LangChain, use dynamic imports for LangChain modules after calling
instrument().
Configuration
Full configuration example with all options:
import { Observability, ObservabilityInstruments } from '@progress/observability';
await Observability.instrument({
appName: 'my-ai-agent',
apiKey: process.env.OBSERVABILITY_API_KEY,
endpoint: 'https://collector.observability.progress.com:443', // default
debug: false,
traceContent: true, // set false to exclude prompts/responses
disableBatch: true, // send traces immediately (default)
instruments: new Set([ // only enable specific instruments
ObservabilityInstruments.OPENAI,
ObservabilityInstruments.LANGCHAIN
]),
additionalTags: ['production', 'release:2.4.1']
});
Parameters
| Parameter | Required | Default | Description |
|---|---|---|---|
| appName | No | process.argv[1] | Application name shown in platform |
| apiKey | Yes* | undefined | Your API key (* or set env var) |
| endpoint | No | https://collector.observability.progress.com:443 | Collector endpoint URL |
| debug | No | false | Enable debug logging |
| traceContent | No | true | Include prompts and completions in traces |
| disableBatch | No | true | Send traces immediately (no batching) |
| instruments | No | undefined (all) | Set of instruments to enable |
| blockInstruments | No | undefined | Set of instruments to disable |
| additionalTags | No | undefined | Global tags applied to all spans |
| resourceAttributes | No | undefined | Custom resource attributes |
| headers | No | undefined | Custom headers for exporter |
Environment Variables
# Set these environment variables instead of passing them to instrument()
export OBSERVABILITY_APP_NAME="my-ai-agent"
export OBSERVABILITY_API_KEY="<your-api-key>"
export OBSERVABILITY_ENDPOINT="https://collector.observability.progress.com:443"
export OBSERVABILITY_TRACE_CONTENT="true"
| Variable | Overrides |
|---|---|
| OBSERVABILITY_APP_NAME | appName |
| OBSERVABILITY_API_KEY | apiKey |
| OBSERVABILITY_ENDPOINT | endpoint |
| OBSERVABILITY_TRACE_CONTENT | traceContent (true or false) |
Decorators
The TypeScript SDK provides the same four decorators as the Python SDK. In TypeScript, decorators are applied to class methods:
| Decorator | Span Kind | Use For |
|---|---|---|
| @task | task | A discrete unit of work |
| @workflow | workflow | A multi-step orchestration |
| @agent | agent | An AI agent entry point |
| @tool | tool | A tool or function the agent can invoke |
Examples
@agent
import { agent } from '@progress/observability';
class SupportAgent {
@agent({ name: 'support-agent' })
async handleRequest(message: string) {
// Agent logic with LLM calls, tool usage, and so on
return response;
}
}
@workflow
import { workflow } from '@progress/observability';
class OnboardingService {
@workflow({ name: 'onboarding-flow', version: 2 })
async onboardCustomer(customerId: string) {
await this.verifyIdentity(customerId);
await this.createAccount(customerId);
await this.sendWelcomeEmail(customerId);
}
}
@tool
import { tool } from '@progress/observability';
class SearchTools {
@tool({ name: 'web-search' })
async searchWeb(query: string): Promise<string> {
// Tool implementation
return results;
}
}
Wrapping Standalone Functions
For functions that are not class methods, use wrapFunctionWithSpan():
import { wrapFunctionWithSpan, ObservabilitySpanKind } from '@progress/observability';
// Wrap a standalone function (not a class method)
const instrumentedFetch = wrapFunctionWithSpan(
fetchData,
'fetch-data',
{ spanKind: ObservabilitySpanKind.TOOL, tags: ['api-call'] }
);
// Call the wrapped function — a span is created automatically
const result = await instrumentedFetch(url);
Tags
Tags work the same way as in the Python SDK. Three levels:
1. Global Tags
await Observability.instrument({
appName: 'my-ai-agent',
apiKey: process.env.OBSERVABILITY_API_KEY,
additionalTags: ['production', 'release:2.4.1']
});
2. Scoped Tags
import { propagateAttributes } from '@progress/observability';
propagateAttributes(['tenant:acme', 'experiment-v2'], () => {
// All spans created here inherit these tags
myAgentFunction();
});
// Nesting is supported — tags accumulate
propagateAttributes(['outer-tag'], () => {
propagateAttributes(['inner-tag'], () => {
doWork(); // span gets both "outer-tag" and "inner-tag"
});
});
3. Decorator Tags
import { task } from '@progress/observability';
class MyService {
@task({ tags: ['cohort-a'] })
async myTask() {
// ...
}
}
Supported Instruments
Currently Instrumented
| Category | Instruments |
|---|---|
| LLM Providers | OPENAI, ANTHROPIC, COHERE, BEDROCK, AZURE_OPENAI |
| Agent Frameworks | LANGCHAIN, LLAMA_INDEX |
| Vector Databases | PINECONE, CHROMA |
Controlling Instruments
import { Observability, ObservabilityInstruments } from '@progress/observability';
// Enable everything except Redis
await Observability.instrument({
appName: 'my-ai-agent',
apiKey: process.env.OBSERVABILITY_API_KEY,
blockInstruments: new Set([ObservabilityInstruments.REDIS])
});
Shutdown
Call shutdown() before your process exits to flush any pending telemetry:
// Call shutdown before your process exits
try {
await runAgent();
} finally {
await Observability.shutdown();
}
Observability.shutdown() accepts an optional timeoutMs parameter. The default is 30000.