@raindrop-ai/openai-managed-agents
v0.0.5
Published
Raindrop tracing for the OpenAI Agents API
Maintainers
Keywords
Readme
Raindrop for the OpenAI Agents API
Capture the managed Agents API (openai.beta.agents.sessions) in Raindrop.
This package uses openai, the OpenAI TypeScript SDK. For the separate,
in-process @openai/agents framework, use @raindrop-ai/openai-agents.
Requires Node.js 22+ and OpenAI SDK 7.15.x–7.x.
pnpm add @raindrop-ai/openai-managed-agents openaiQuick start
import OpenAI from "openai";
import { createRaindropOpenAIManagedAgents } from "@raindrop-ai/openai-managed-agents";
const raindrop = createRaindropOpenAIManagedAgents({
writeKey: process.env.RAINDROP_WRITE_KEY,
userId: "user_123",
properties: { feature: "world_builder" },
});
const client = raindrop.wrap(new OpenAI());
try {
const events = await client.beta.agents.sessions.create({
agent: { model: "gpt-6-astra", instructions: "Help build a simulation World." },
environment: { type: "openai_hosted" },
input: "Describe the files needed for a simulation World.",
stream: true,
});
try {
for await (const event of events) {
if (event.type === "agent.session.turn.output_text.delta") process.stdout.write(event.delta);
if (event.type === "agent.session.idle") break;
}
} finally {
events.controller.abort();
}
} finally {
await raindrop.shutdown();
}Use an OpenAI application key with the Agents API permissions.
No OpenTelemetry setup is required. Missing or blank Raindrop keys disable cloud
shipping. Set localWorkshopUrl: false to disable local Workshop mirroring.
Capture
- One partial event per turn, completed when the native turn reaches a terminal state.
- Session model, user input, completed final answer, per-turn usage, and provider attribution.
- A root turn span and child command, function, MCP, search, and subagent-control tool spans.
- Session and turn IDs in properties. By default, the session ID is the conversation ID.
- Original OpenAI results, events, request options, abort controls, and response helpers.
The wrapper observes sessions.create, sessions.retrieve,
sessions.events.create, sessions.events.stream, and sessions.stream.
Use raindrop.wrap(client, { userId, convoId, eventName, properties }) for context
on sessions created or retrieved through that wrapper. Use a single wrapper for
each native client. Retrieve an existing session before following its events to
capture its model. Reading raw bodies with asResponse() bypasses capture.
Reconnecting runners and stored history
For a runner that reconnects streams, use the public typed handler with an unwrapped native client. Keep one Raindrop client for the runner's lifetime:
import type { Turn } from "openai/resources/beta/agents/sessions/turns";
raindrop.handler.onSession(session);
raindrop.handler.onInput(session.id, userInput); // after successful submission
// Snapshot turn statuses before recovering their items.
const recoveredTurns: Turn[] = [];
for await (const turn of client.beta.agents.sessions.turns.list(session.id, { order: "asc" })) {
recoveredTurns.push(turn);
}
for await (const item of client.beta.agents.sessions.items.list(session.id, { order: "asc" })) {
raindrop.handler.onItem(session.id, item);
}
for (const turn of recoveredTurns) raindrop.handler.onTurn(turn);
for await (const event of events) raindrop.handler.onEvent(event);
await raindrop.flush();
// After all streams/recovery work are stopped:
raindrop.handler.forgetSession(session.id);
await raindrop.shutdown();Open the stream before recovery so it can buffer live updates. Read and buffer turn statuses first, recover items next, then pass the buffered turns to the handler. A turn completed after the snapshot closes from its buffered live terminal event. Process callbacks serially during recovery and consumption.
onInputEvents(sessionId, events) records accepted message/tool-result submissions.
Repeated terminal turns and tool receipts are deduplicated within this client.
Turn deduplication retains the latest 10,000 completed turns. Across process
restarts, provide a stable eventId: (turnId) => ... and replay items before turns.
No session or item listing is intercepted automatically.
The wrapper closes unfinished captures when the last iterator for a session
exits, including early exit and transport failure. This marks capture interruption; it does not
cancel the backend turn. Session model and context remain available for later
streams; the previous input is cleared. Metadata retains the latest 10,000 idle
sessions plus sessions with active turns or streams. Call forgetSession when
the session is no longer needed. Use the callback path when an unfinished turn
must survive a reconnection.
Options and enrichment
Options include writeKey, endpoint, projectId, userId, convoId,
eventName, properties, appGit, debug, localWorkshopUrl,
maxTextFieldChars, eventId(turnId), and redact(text).
Text uses the shared core bounds. redact applies to captured prompts, answers,
tool payloads, and errors. Caller-supplied properties/attachments are the caller's
responsibility.
events.patch, events.finish, events.addAttachments, events.setProperties,
users.identify, and signals.track expose the shared core enrichment API.
flush() ships current buffers; shutdown() also closes unfinished captures.
Shared client options
createRaindropOpenAIManagedAgents(...) also takes the options every Raindrop JS integration shares:
| Option | Type | Default | Description |
|---|---|---|---|
| redactPii | boolean | false | Redact PII (emails, phone numbers, card numbers, addresses, secrets) from event input/output before it leaves the process |
| redactPiiFields | PiiFieldMap | - | More wire field paths to redact when redactPii is on, e.g. { event: ["properties.customer.email"] }; add properties["error.message"] to also redact error messages |
| flushTimeoutMs | number | 10000 | Deadline for flush() / shutdown(). Both resolve to a result (delivered, dropped, queued, ...) and never reject |
flush() and shutdown() resolve to { delivered, timedOut, dropped, queued, inFlight } and never reject; ignoring the result is fine.
Tool catalog capture (ai.prompt.tools)
Agent configuration, not exact per-call capture. Model calls run inside
OpenAI's managed runtime, so the wrapper never sees individual requests. It
does see the resolved agent definition on the session (session.agent.tools)
and records it as ai.prompt.tools on every coordinator openai.agent.turn
span (one per model turn), together with
ai.prompt.tools.source = "agent.config" so consumers can tell this
configuration snapshot from exact capture. Function tools keep their JSON
Schema parameters; mcp, web_search, tool_search and
programmatic_tool_calling entries are provider-owned and recorded as
{"type":"provider-defined",…} without an invented schema. Subagent turns run
their own tool sets, so they only receive an explicit override. Absent
attribute = catalog unknown; [] = the agent has no tools. This package has no
separate prompt-content switch, so the attribute is recorded whenever tracing
is enabled.
| Option | Description |
| --- | --- |
| tools (on createRaindropOpenAIManagedAgents({ tools }) or wrap(client, { tools })) | Overrides the tool catalog recorded as ai.prompt.tools on every model span. Use when the integration cannot see the tools the model was given. Replaces the agent definition entirely (tools: [] records an empty catalog) and carries no source attribute. |
Your properties win
Properties you pass (as defaults, per call, or through events.setProperties / events.patch / events.finish) are merged into the event. If one of your keys has the same name as a key the integration sets itself, such as ai.usage.*, ai.provider, error.type / error.message, or the integration's own prefixed keys, your value is kept, whatever order the updates are sent in. With debug on, the integration logs each key you override.
Event ids
Set each turn's id with the eventId: (turnId) => string option; the event and every span of the turn carry it. After a turn finishes, raindrop.lastEventId holds its id, for attaching a signal or feedback:
await raindrop.signals.track({ eventId: raindrop.lastEventId!, name: "thumbs_up", type: "feedback", sentiment: "POSITIVE" });Finishes you make through raindrop.events.* don't change lastEventId. It holds the last turn finished anywhere in the process, so on a client shared by concurrent turns, use the id you passed instead.
Limits
The service does not expose individual inference requests. Captures represent
turns, rather than each hidden model call. Usage comes only from Turn.usage;
missing usage stays missing. Session-wide cumulative counters are never used as
turn usage. Usage is best effort and later revisions after the first terminal
receipt are not applied.
Span timing reflects observed lifecycle events, including recovery; native turn
timestamps are recorded as openai.turn.* attributes. Command duration is available
in the native item. Image contents, reasoning text, intermediate commentary,
agent-to-agent messages, and streaming text deltas are not automatically
captured. Final text is read from completed final-answer items. Subagent turns
are grouped by session and identified by subagent ID; no cross-turn parent
hierarchy is inferred. Coordinator model identity is available from the session
configuration. Subagent model and inherited coordinator input stay absent unless
their own input is recorded.
See RELEASING.md for first-publication and verification steps. See the integration docs for configuration details.
