JS Context
Trace context propagation in JavaScript/TypeScript.
The JavaScript SDK uses AsyncLocalStorage for automatic context propagation across async operations.
Getting Context
getCurrentContext()
Get the current context state:
import { getCurrentContext } from 'risicare';
const context = getCurrentContext();
if (context) {
console.log('Session:', context.session);
console.log('Agent:', context.agent);
console.log('Span:', context.span);
console.log('Phase:', context.phase);
}getCurrentContext() returns a plain object snapshot of the current context. Its session and agent fields are either null (when not set) or objects with these keys:
{
session: { sessionId: string; userId?: string } | null;
agent: {
agentId: string;
agentName?: string;
agentRole?: string;
agentType?: string;
} | null;
span: { spanId: string; traceId: string } | null;
phase: SemanticPhase | null;
}Individual Getters
import {
getCurrentTraceId,
getCurrentSpanId,
getCurrentAgentId,
getCurrentSessionId,
} from 'risicare';
const traceId = getCurrentTraceId();
const spanId = getCurrentSpanId();
const agentId = getCurrentAgentId();
const sessionId = getCurrentSessionId();W3C Trace Context
Propagate context across service boundaries using W3C headers:
Inject Context
import { injectTraceContext } from 'risicare';
// Inject into HTTP headers
const headers: Record<string, string> = {};
injectTraceContext(headers);
// Now headers contains:
// {
// 'traceparent': '00-abc123...-def456...-01',
// 'tracestate': 'risicare=...'
// }
await fetch('https://api.example.com', { headers });Extract Context
extractTraceContext always returns a Record<string, string | undefined> (it is never undefined). When the incoming headers carry a Risicare tracestate, the record includes traceId, parentSpanId, flags, and any propagated sessionId / agentId. Use those values to re-enter the corresponding context scope — for example, restoring the upstream session with withSession:
import { extractTraceContext, withSession } from 'risicare';
// In your HTTP handler
app.post('/api/endpoint', async (req, res) => {
const context = extractTraceContext(req.headers);
if (context.sessionId) {
await withSession({ sessionId: context.sessionId }, async () => {
// Spans created here are grouped under the propagated session
await processRequest(req.body);
});
} else {
await processRequest(req.body);
}
res.json({ success: true });
});Manual Context Management
Use the exported scope wrappers to run code within an explicit session, agent, or phase context. Each takes the scope's options (or value) and a callback, runs the callback inside that scope, and returns its result. They nest naturally:
import { withSession, withAgent, withPhase } from 'risicare';
import { SemanticPhase } from 'risicare';
await withSession({ sessionId: 'sess-123', userId: 'user-456' }, async () => {
await withAgent({ name: 'my-agent', role: 'worker' }, async () => {
// Code here runs with the session + agent context applied
await doWork();
});
});
// A single phase scope:
await withPhase(SemanticPhase.THINK, async () => {
await analyze();
});Prefer the higher-level wrappers (session(), agent(), and the phase functions in JS Decorators) for instrumenting whole functions; the withX() forms are for ad-hoc scoping inside existing code.
Span Registry
Access spans by ID for use in async generators and streaming:
import {
registerSpan,
getSpanById,
unregisterSpan,
} from 'risicare';
// Register a span for later lookup by ID
registerSpan(span, 30000); // optional TTL in ms
// Get span by its ID
const span = getSpanById('abc123def456');
// Clean up when done
unregisterSpan('abc123def456');When to Use the Span Registry
The span registry is designed for async generators and streaming scenarios where contextvars context may be lost after yield. Store the span ID before yielding, then retrieve it by ID in subsequent iterations.
Cross-Process Propagation
HTTP Client
import { injectTraceContext } from 'risicare';
async function fetchWithTracing(url: string, options: RequestInit = {}) {
const headers = new Headers(options.headers);
// Inject trace context
const traceHeaders: Record<string, string> = {};
injectTraceContext(traceHeaders);
Object.entries(traceHeaders).forEach(([key, value]) => {
headers.set(key, value);
});
return fetch(url, { ...options, headers });
}Message Queues
import { injectTraceContext, extractTraceContext, withSession } from 'risicare';
// Producer
async function publishMessage(queue: Queue, payload: unknown) {
const headers: Record<string, string> = {};
injectTraceContext(headers);
await queue.publish({
payload,
headers,
});
}
// Consumer
async function handleMessage(message: Message) {
const context = extractTraceContext(message.headers);
if (context.sessionId) {
await withSession({ sessionId: context.sessionId }, async () => {
await processMessage(message.payload);
});
} else {
await processMessage(message.payload);
}
}