How do you get observability out of a hosted agent harness when the agent loop runs in a vendor service rather than your process?
Short answer: you can't eBPF-instrument it, because there is no local process to attach to — the agent loop, model calls, and tool executions run inside the vendor's hosted service, not on your machines. What you can observe from your side is the HTTP boundary: generic client-side instrumentation (eBPF or a userspace client tracer) still sees every outbound POST /v1/agents/runs request and the streaming response it returns, so request/response size, latency, and status survive. To reconstruct the inner span tree (turns, model responses, tool calls), you rely on the vendor's own export path: OpenAI's Agents API records each session's steps as OpenTelemetry spans ("the trace groups model responses and tool calls under the root agent or the subagent that performed them. Each recorded step is called a span") and lets you pull those traces as OTLP JSON from a public endpoint. That is the load-bearing boundary: the trace is not something you emit into your pipeline; it is something you fetch from the vendor, on demand, under an organization-level gate.
Why eBPF can't reach the inner loop
eBPF attaches to the kernel execution of a process on your host. A hosted agent harness is the opposite case: your code issues one HTTP request to the vendor's service, and everything interesting after that — planning, each model response, each tool invocation, subagent delegation — executes on the vendor's side. There is no local kernel trace of it. This is exactly why it is a class distinct from the other recurring threads: the Claude / OpenAI SDK case is instrumentable, because that SDK's agent loop runs on your machine and its eBPF or client instrumentation can capture the inner chat / execute_tool crossings locally; the hosted service case is not, because the loop lives behind an HTTP boundary you can only cross, not instrument. The boundary is process locality, and it decides what you can and cannot reconstruct without the vendor exporting the structure for you.
The three things that cross the boundary
Only two data paths cross the HTTP boundary, and both are visible to ordinary client-side tracing:
- The outbound request. The agent run is a request your process sends; eBPF (or a userspace client tracer) sees its size, duration, and status. This is the whole of the local signal.
- The live event stream. Every session exposes an event stream that shows the agent's progress in real time while the run is in flight. Useful for liveness and for a lightweight UI, but it is a progress feed, not a finished trace: it is not a set of structured spans you can query, correlate, or ingest, and an agent's answer can appear before its trace and token usage are ready.
- The recorded trace. After the fact, the vendor stores the session's spans. This is the only path that carries the inner structure, and it is pull-based:
GET /v1/agents/sessions/{session_id}/tracesreturns a page of traces as OTLP JSON. The guide is explicit that "Trace export must be enabled for your organization" and that the key needsapi.traces.reador the broaderapi.agents.readpermission.
The decisive asymmetry is that the inner tree exists only as far as the vendor chooses to export it. A managed harness is a black box unless it hands you a span structure; and the handoff is a pull of a vendor-hosted artifact under a vendor-set permission and org gate, not an exporter you configure and point at your collector.
How to verify what you can and cannot reconstruct
- Confirm the loop is remote before assuming you lost nothing. Identify whether the agent runs in your process (SDK) or a managed service (hosted API). If it is a managed service, the inner span tree is not on your host by construction.
- Instrument the crossing, not the interior. Add client-side tracing on the outbound request; from there you get the ordinary HTTP signals (latency, status, payload size) that any eBPF or userspace client tracer produces. Do not expect the model/tool span tree to appear from this layer.
- Pull the trace where the vendor offers it. For OpenAI's Agents API, fetch the OTLP JSON from the session-trace endpoint with an org-enabled export and a key that has the read permission, then import the OTLP into any OpenTelemetry-compatible backend. Treat it as a recovered trace, not a natively emitted one: its span vocabulary is the vendor's, its timing is the vendor's, and it only appears for sessions the vendor recorded.
- Watch for the missing-exporter case. Where a vendor's beta exposes no tracing configuration and no external trace exporter, the event stream is all you get live and the recorded trace is all you get after the fact — both inside the vendor. The generalization to other cloud-managed agent services holds: the moment the loop leaves your process boundary, only the HTTP crossing and whatever the vendor chooses to export are observable.
The limitation that decides it
The answer is bounded by locality. eBPF (and any userspace client instrumentation) reconstructs the trace graph only at the crossings that leave your process; it cannot manufacture the interior of a loop that runs in a remote service. The inner span structure is recoverable only when the harness exposes exporters or a trace-export path — and that exposure is the vendor's decision, gated by its permissions and enabled per organization. So the practical rule is: for a hosted harness, plan on (a) the outbound HTTP crossing as your reliable local signal, (b) the vendor's event stream for live progress, and (c) the vendor's recorded-trace export for the finished span tree — and treat the absence of an external exporter as a hard boundary, not a gap to bridge locally.
References
- OpenAI Agents API — Observability (every session exposes an event stream; session logs and token usage viewable in the dashboard; recorded traces inspectable there or exported as OTLP JSON)
- OpenAI Agents API — Tracing (tracing enabled by default; each recorded step is a span;
GET /v1/agents/sessions/{session_id}/tracesreturns OTLP JSON; export must be enabled for the organization and the key needs the traces-read permission) - OpenAI Agents API — Export session traces (the OTLP JSON export endpoint in detail)
- OpenAI Agents API — Live session events (the in-flight event stream)
- W3C — Context Propagation (the
traceparent/tracestatecontract that makes a trace portable across process and vendor boundaries) - OpenTelemetry — Traces concept (spans, the signal a trace export would carry)
Community discussion today
The monitored window is a rolling week across two allowlisted Slack archives (both OpenTelemetry instrumentation channels); the two visible-browser chat workspaces and the public mailing-list and subreddit surfaces were not reviewed in this run, and that gap is noted rather than treated as quiet. Several themes recur from the prior days' windows; the one with a new, source-confirmed boundary is the hosted-agent observability question above.
Hosted agent harness and the observability boundary (the question above). A user, after seeing the hosted Agents API announced, asked how to get observability data out of it and worried that this approach, if it becomes popular, would lock that data away the way cloud vendors already do with managed services. The working answer is the locality boundary: eBPF instrumentation is local-to-process, so a loop that runs in a vendor service cannot be instrumented in the way the SDK case can; only the HTTP crossings that leave the process are visible. The decisive, source-grounded detail is the vendor's own export path — OpenAI records each step as an OpenTelemetry span and offers a pull-based OTLP JSON export gated per organization and per permission — which is a "fetch the trace from the vendor" model, not a "configure an external exporter into your collector" model. The unresolved boundary is what happens for a vendor whose beta exposes no tracing configuration and no external exporter: the event stream plus a recorded trace are all that exist, both inside the vendor's custody.
GenAI agent span shape: sibling vs. nested (recurring). A maintainer confirmed that one invoke_agent span per turn with chat and execute_tool as direct children, glued by the tool-call identifier, is the intended shape; nesting each tool call under the chat that requested it is the causal-tree idea in the semantic-conventions work, useful only if the context can be carried across a sidecar, and not a requirement yet. The sibling-plus-call-id shape is not a miss.
OBI config v1→v2 migration and the Helm chart gap (recurring). A user still hitting "fields are outside the supported v1-to-v2 migration contract" wanted a middle ground; the maintainer's position is unchanged: the all-or-nothing migrate is intentional, and the proposed escape is an explicit partial/best-effort mode that migrates what it can and reports the omitted fields, not a silent default. The related OBI Helm-chart v2 gap — a v1 field still injected into the v2 config schema by the chart's helpers — was directed to be filed as its own issue in the helm-charts repo, with chart/OBI versions, the relevant values, the rendered config, and the validation error.
Node.js sentinel cost and agent lifecycle (recurring). The Node.js cost thread continues: the eBPF probes are cheap; the cost is the injected Node agent's per-callback sentinel. The maintainer-confirmed read is that client-span parenting comes from the fd-pair map while the per-callback sentinel keeps the trace-context map aligned with the active request, and a third consumer (external trace/profile correlation) also reads that pinned map — so gating the sentinel only on manual spans and log enrichment could silently break that integration. A related lifecycle report: a user excluding a service from tracing had to roll out a pod restart to see latency drop, because the injected agent kept running until the pod was recycled.
Continue exploring
Back to index
eBPF Q&A
Daily, source-grounded answers to recurring questions about eBPF, Linux observability, profiling, runtime extension, and security.
Previous
Why does the OBI config migration from v1 to v2 refuse to write a file when some fields have no mapping, and what is the middle ground?
Short answer: because obi config migrate is a behavior-preserving conversion, not a translation pass.
Next
For a Node.js service instrumented by OBI, why can't I just turn off the per-callback trace-context sentinel because the service doesn't use manual spans or log enrichment?
Short answer: because the sentinel is not a feature of OBI's; it is the write side of a shared, pinned kernel map whose consumer set extends beyond OBI's own configuration.
- Last updated
- Sep 20, 2026
- First published
- Sep 20, 2026
- Contributors
- Yunwei 123
Was this page helpful?