Skip to main content
GitHub

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);
  }
}

Next Steps