# OTLP

`otlp()` exports Agent Invocation telemetry to an OpenTelemetry receiver through OTLP/HTTP JSON.
It adds no model-facing tool and does not wrap a Server Primitive. It registers a telemetry exporter for the invocation trace.
The same Agent Definition works with any receiver that accepts ordinary OpenTelemetry logs and traces.

## Configure OTLP export

Pass the receiver's OTLP base endpoint. ViteHub appends the conventional `/v1/logs` and `/v1/traces` signal paths.

```ts [server/agents/support.ts]
import { defineAgent } from 'vite-hub/agent'
import { otlp } from 'vite-hub/agent/capabilities'

export default defineAgent({
  capabilities: [
    otlp({
      endpoint: process.env.OTLP_ENDPOINT!,
      headers: {
        authorization: `Bearer ${process.env.OTLP_TOKEN!}`,
      },
      resource: {
        'service.namespace': 'support',
      },
      live: true,
    }),
  ],
  driver: { model: 'openai/gpt-5.1-mini' },
  name: 'support',
})
```

## How OTLP export works

ViteHub always exports one completed trace. Its ordinary OTLP spans carry structural timing and `gen_ai.*` attributes. Without `live`, the root span also contains the invocation's Trace Events, including one `vitehub.agent.configured` event with sanitized Agent Definition metadata: Agent identity, Capability metadata, Driver and model identity when resolved, tool names, runtime, and Workspace name, mode, and Sources. The event's `vitehub.agent.configuration.fingerprint` attribute covers these facts and the final resolved instructions, excluding Capability inspection snapshots. Public configurations use a stable `sha256_...` digest. Configurations containing redacted credentials use an `hmac_sha256_...` digest with a private runtime key to prevent offline credential guessing. Receivers can compare secret-bearing context revisions within the same runtime; these fingerprints change when the runtime restarts.

With `live: true`, each Trace Event is exported once as a correlated OTLP LogRecord while the invocation runs. LogRecords use the same trace and span IDs as the completed trace and carry `agent.invocation.id` plus `vitehub.event.sequence` for deduplication. ViteHub batches for up to five seconds or 512 new Trace Events, whichever comes first, and flushes immediately when the invocation ends. After all records are delivered, the completed trace omits span events because those events were already sent as logs. If a live batch fails, the completed trace retains its span events so the terminal export does not lose that evidence.

This is append-only export, not polling: ViteHub never resends an evolving in-progress span snapshot. OTLP's HTTP and gRPC exports are request/response protocols, so ViteHub does not add a receiver-specific SSE or WebSocket channel.

## Custom Capability metadata

Every Capability contributes its public `metadata` automatically. A custom Capability can add invocation-resolved facts without knowing which exporter is installed:

```ts
import { defineCapability } from 'vite-hub/agent'

export const repository = defineCapability({
  id: 'repository',
  metadata: { provider: 'github' },
  async resolve(context) {
    const installation = await resolveInstallation(context)
    context.telemetry.metadata({ installation: installation.slug })
  },
})
```

This contribution belongs to the Capability's entry in the configuration event. It does not create another invocation stream or require a receiver-specific integration.

## Delivery behavior

The exporter retries transient HTTP failures and honors `Retry-After`. Export is best effort and runs through the host's `waitUntil()` boundary; an unavailable receiver does not change Agent output. Receiver failures produce a bounded structured local error with the Capability ID, invocation ID, run ID, and export phase. Receivers should deduplicate spans by trace and span ID, and live records by `agent.invocation.id` plus `vitehub.event.sequence`, because a retry can repeat a request.

Streaming command output remains trace activity, not a log drain. Provider command start, output deltas, and completion use the same tool-call ID so a session UI can group them without inspecting terminal escape sequences.

## Requirements

`otlp({ endpoint })` requires an absolute `http:` or `https:` base endpoint.
The receiver must accept OTLP/HTTP JSON at `/v1/traces`, and at `/v1/logs` when `live` is `true`.

## Security and approval

`otlp()` adds no model-facing tool, so the Agent cannot call it and there is no approval step.
It sends telemetry from your server to the configured receiver. By default, exported telemetry is metadata-only.

The configuration event is not a user message. User prompts and model or tool content remain governed by trace content policy and are metadata-only in this exporter.

When a tool came from a Capability, its start, finish, and error events carry `capability.id`. A configured but unused Capability does not add this attribute. Tool input and output remain excluded unless content export is enabled.

Agent instructions are prompt content, so they are excluded by default. Opt in explicitly when the receiver is trusted:

```ts
otlp({
  endpoint: process.env.OTLP_ENDPOINT!,
  content: {
    inputs: true,
    instructions: true,
    outputs: true,
  },
})
```

Secret-shaped Capability metadata keys are replaced with `[redacted]`. Keep receiver credentials in `headers`; `otlp()` does not expose its endpoint or headers in Agent metadata.

## Driver support

| Agent Driver | Support |
| --- | --- |
| Model-backed | Exports the invocation trace, including model and tool activity. |
| Provider-backed | Exports the invocation trace, including normalized provider tool and command activity. |
| Custom-run-backed | Exports the invocation trace that ViteHub records around `driver.run`. |

## Verify OTLP export

Run one invocation against a local OpenTelemetry Collector with a debug exporter.
Confirm that one trace arrives at `/v1/traces` with a `vitehub.agent.configured` event, or with LogRecords at `/v1/logs` when `live` is `true`.
Stop the receiver and run the invocation again. Confirm that the Agent output does not change and that a local export error appears.

## Options

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `endpoint` | `string` | Required | Absolute OTLP base endpoint. ViteHub appends `/v1/logs` and `/v1/traces`. |
| `headers` | `Record<string, string>` or resolver | None | Request headers resolved for each export. |
| `resource` | OTLP resource attributes or resolver | Agent and runtime defaults | Additional resource attributes. |
| `content.inputs` | `boolean` | `false` | Includes user and tool input content in exported telemetry. |
| `content.instructions` | `boolean` | `false` | Includes resolved Agent instructions in the configuration telemetry. |
| `content.outputs` | `boolean` | `false` | Includes assistant, tool, and result output content in exported telemetry. |
| `live` | `boolean` | `false` | Sends append-only Trace Events as correlated OTLP logs while the invocation runs. |

## Related pages

- [Agent invocations](https://vitehub.dev/docs/agents/invocations)
- [diagnostics()](https://vitehub.dev/docs/agents/capabilities/diagnostics)
- [Custom capabilities](https://vitehub.dev/docs/agents/capabilities/custom)
- [Official capabilities](https://vitehub.dev/docs/agents/capabilities/official)
