Observability

TypeScript SDK Observability

Trace policy decisions, model calls, and tool work with the ArmorIQ TypeScript SDK. Set the export, capture ceiling, and per-session switch.

This page is for TypeScript and Node.js developers. You learn how tracing works in the @armoriq/sdk package, which options and environment variables control it, and which session calls are optional. The SDK builds one OpenTelemetry trace for each session, and sends it to ArmorIQ without telemetry code in your agent.

This page describes @armoriq/sdk 0.8.23, the current release. Older versions used a different recorder. See Migrate from the legacy recorder.

Install

You need an organization-scoped ArmorIQ API key. The SDK supports Node.js 18.19, and 20.6 or later. The LangChain and Strands adapters need Node.js 20 or later.

npm install @armoriq/sdk@0.8.23

Default setup

Tracing is on by default. With a framework adapter you add no tracing code. See Set up tracing for a complete example and Framework support for tested versions.

import { ArmorIQClient } from '@armoriq/sdk';
import { ArmorIQLangChain } from '@armoriq/sdk/dist/integrations/langchain';

const armoriq = new ArmorIQLangChain({
  client: new ArmorIQClient({ apiKey: process.env.ARMORIQ_API_KEY! }),
  mode: 'sdk',
});

const handler = await armoriq.forUser('alice@example.com', { goal: userMessage });
await agentExecutor.invoke({ input: userMessage }, { callbacks: [handler] });

Creating an ArmorIQClient also patches the openai and @anthropic-ai/sdk packages, so that direct calls are traced while a session is active. An ES module application must call instrumentOpenAI(OpenAI) itself. See Framework support.

Use a session without an adapter

If you call the session API yourself, the session is also the trace. Close it when it ends. The SDK does not close a session for you, and an open session does not export its root span.

const session = client.startSession({ mode: 'sdk' });
try {
  // startPlan, check, and report calls
} finally {
  await session.close();
}
  • close(status?, taskOutcome?) ends the plan span and the root span, then sends the rest. The status can be ok (the default), error, denied, cancelled, timeout, or disconnected.
  • dispose() is an alias for close(). Both are safe to call more than once and never throw.
  • Close the session before the process stops. The SDK drains ended spans when the process exits normally, but it does not handle SIGINT, SIGTERM, or process.exit().

Optional session calls

CallWhat it does
session.flushObservability()Ends the active plan span and sends queued spans. Use it at a request boundary in a long-lived process. Most agents do not need it.
session.refreshObservabilityPolicy()Waits until the session holds a current organization policy. Call it once at start-up in a short script. Do not call it inside a request hook.
session.otelSessionThe native session object that adapters use. It is null when tracing is off.

None of these calls changes a policy decision. They never throw into your code.

Options

Pass observability to the client, to startSession, or to both. A session value overrides the same field from the client.

const client = new ArmorIQClient({
  apiKey: process.env.ARMORIQ_API_KEY!,
  observability: { captureMode: 'metadata' },
});

const quiet = client.startSession({ observability: { enabled: false } });
OptionDefaultEffect
enabledtrueTurns tracing off when false
exporter'armoriq''armoriq', 'external', or 'provider'. If you set no exporter and an endpoint setting exists, the SDK chooses 'external'. See Choose who exports your traces.
endpointnoneThe full OTLP traces URL for external export
baseEndpointnoneAn OTLP base URL. The SDK adds /v1/traces.
headersnoneExtra headers for external export
tracerProvidernoneYour OpenTelemetry tracer provider, for provider export
captureModenone (no local limit)The most data that you allow locally: off, metadata, enhanced, or debug. The organization policy can lower it more.
serviceNamearmoriq-sdkThe OpenTelemetry service.name
resourceAttributesnoneExtra resource attributes. Only service.version, service.namespace, service.instance.id, deployment.environment.name, telemetry.sdk.name, and telemetry.sdk.version are kept.
userIdnoneA user ID that you choose to send to an external destination
streamUsagetrueAsk OpenAI streaming chat requests for token usage. See below.
protocolhttp/protobufThe only supported value
timeoutMillis10,000Export time limit. It must be a positive whole number.

A local captureMode can only lower what the organization allows. It can never raise it. See Organization controls.

Environment variables

VariableEffect
ARMORIQ_API_KEYThe API key
ARMORIQ_BACKEND_URLThe ArmorIQ backend. The SDK sends traces to <backend>/v1/traces and reads the policy from <backend>/observability/policy/lease.
ARMORIQ_OBSERVABILITY0, false, off, no, or disabled turns tracing off for the process
OTEL_SDK_DISABLEDtrue turns tracing off
OTEL_TRACES_EXPORTERnone turns tracing off
ARMORIQ_OTEL_EXPORTERarmoriq, external, or provider, if you set no exporter option
ARMORIQ_OTEL_CAPTURE_MODEThe local capture limit, if you set no captureMode option
ARMORIQ_OTEL_STREAM_USAGE0, false, off, no, or disabled stops the SDK from asking for usage in OpenAI streams
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, OTEL_EXPORTER_OTLP_ENDPOINT, ARMORIQ_OTEL_ENDPOINTThe endpoint for external export
OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TRACES_HEADERSHeaders for external export
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL, OTEL_EXPORTER_OTLP_PROTOCOLMust be http/protobuf
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT, OTEL_EXPORTER_OTLP_TIMEOUTThe export time limit
OTEL_SERVICE_NAME, OTEL_RESOURCE_ATTRIBUTESService name and safe resource attributes

The TypeScript SDK has no setting for a custom certificate authority, a client certificate, a proxy, compression, or gRPC. Use a Collector for those. The full rules for endpoints and headers are in Choose who exports your traces.

Streaming usage

For OpenAI streaming chat requests, the SDK adds stream_options: { include_usage: true } so that token counts arrive in the last chunk. It hides that chunk from your code. If your request already sets stream_options, the SDK does not change it. Set streamUsage: false or ARMORIQ_OTEL_STREAM_USAGE=0 if your gateway rejects the option or you must keep the raw stream.

Transcript token usage

Token usage for a transcript is stored for each session, model, UTC date, and UTC hour. To add these tokens as model spans on the session trace, pass the native session:

await client.captureTranscriptTokens({
  transcriptPath: '/tmp/transcript.jsonl',
  product: 'armorclaude',
  sessionId: session.sessionId,
  otel: session.otelSession,
});

What the SDK records

The SDK adds these spans without code from you:

Span nameWhen
armoriq.agent.runThe root span of the session
armoriq.intent.planThe plan
armoriq.policy.evaluateEach policy decision
gen_ai.chatEach model call
armoriq.tool, mcp.execute, bash.execute, cli.execute, command.executeTool and command work. Group operations by the armoriq.operation.category attribute, not by span name.

See What data is captured for fields and Data model for the attributes.

How it behaves

  • The SDK reads your organization policy first. It fetches the capture policy with your API key. With armoriq and provider export, the SDK does not wait for the first answer, so spans that start before it arrives are not recorded. With external export, it waits up to 1.4 seconds for the first answer. If the policy is not available, the SDK records nothing.
  • Failures are fail-open. A tracing error never changes an allow, hold, or block result, and never throws into your code.
  • Closing is bounded. close() waits at most 1.5 seconds to send the rest.
  • Export retries are bounded. The SDK retries 429, 502, 503, 504, and common network errors up to 5 times, then drops the batch. It respects Retry-After.
  • No silent fallback. If an external setting is missing or invalid, the SDK exports nothing and logs nothing. It does not use localhost or ArmorIQ instead.
  • Your trace context stays out of ArmorIQ spans. With armoriq and external export, the root span starts from an empty context. With provider export, it becomes a child of your active span.

Next steps

On this page