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.23Default 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 beok(the default),error,denied,cancelled,timeout, ordisconnected.dispose()is an alias forclose(). 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, orprocess.exit().
Optional session calls
| Call | What 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.otelSession | The 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 } });| Option | Default | Effect |
|---|---|---|
enabled | true | Turns 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. |
endpoint | none | The full OTLP traces URL for external export |
baseEndpoint | none | An OTLP base URL. The SDK adds /v1/traces. |
headers | none | Extra headers for external export |
tracerProvider | none | Your OpenTelemetry tracer provider, for provider export |
captureMode | none (no local limit) | The most data that you allow locally: off, metadata, enhanced, or debug. The organization policy can lower it more. |
serviceName | armoriq-sdk | The OpenTelemetry service.name |
resourceAttributes | none | Extra resource attributes. Only service.version, service.namespace, service.instance.id, deployment.environment.name, telemetry.sdk.name, and telemetry.sdk.version are kept. |
userId | none | A user ID that you choose to send to an external destination |
streamUsage | true | Ask OpenAI streaming chat requests for token usage. See below. |
protocol | http/protobuf | The only supported value |
timeoutMillis | 10,000 | Export 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
| Variable | Effect |
|---|---|
ARMORIQ_API_KEY | The API key |
ARMORIQ_BACKEND_URL | The ArmorIQ backend. The SDK sends traces to <backend>/v1/traces and reads the policy from <backend>/observability/policy/lease. |
ARMORIQ_OBSERVABILITY | 0, false, off, no, or disabled turns tracing off for the process |
OTEL_SDK_DISABLED | true turns tracing off |
OTEL_TRACES_EXPORTER | none turns tracing off |
ARMORIQ_OTEL_EXPORTER | armoriq, external, or provider, if you set no exporter option |
ARMORIQ_OTEL_CAPTURE_MODE | The local capture limit, if you set no captureMode option |
ARMORIQ_OTEL_STREAM_USAGE | 0, 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_ENDPOINT | The endpoint for external export |
OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_TRACES_HEADERS | Headers for external export |
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL, OTEL_EXPORTER_OTLP_PROTOCOL | Must be http/protobuf |
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT, OTEL_EXPORTER_OTLP_TIMEOUT | The export time limit |
OTEL_SERVICE_NAME, OTEL_RESOURCE_ATTRIBUTES | Service 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 name | When |
|---|---|
armoriq.agent.run | The root span of the session |
armoriq.intent.plan | The plan |
armoriq.policy.evaluate | Each policy decision |
gen_ai.chat | Each model call |
armoriq.tool, mcp.execute, bash.execute, cli.execute, command.execute | Tool 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
armoriqandproviderexport, the SDK does not wait for the first answer, so spans that start before it arrives are not recorded. Withexternalexport, 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 respectsRetry-After. - No silent fallback. If an
externalsetting is missing or invalid, the SDK exports nothing and logs nothing. It does not uselocalhostor ArmorIQ instead. - Your trace context stays out of ArmorIQ spans. With
armoriqandexternalexport, the root span starts from an empty context. Withproviderexport, it becomes a child of your active span.
Next steps
Set Up OpenTelemetry Tracing
Turn on ArmorIQ tracing for a Python or TypeScript agent with no tracing code, then confirm the first trace in the dashboard.
Python SDK Observability
Trace policy decisions, model calls, and tool work with the ArmorIQ Python SDK. Set the export, capture ceiling, and per-session switch.