Skip to content

fix(claude-agent-sdk): return the SDK Query object from the instrumented query() - #3776

Open
iaj6 wants to merge 1 commit into
Arize-ai:mainfrom
iaj6:fix/claude-agent-sdk-preserve-query-object
Open

iaj6 wants to merge 1 commit into
Arize-ai:mainfrom
iaj6:fix/claude-agent-sdk-preserve-query-object

Conversation

@iaj6

@iaj6 iaj6 commented Sep 17, 2026

Copy link
Copy Markdown

Fixes #3774

What changed

wrapQuery() in openinference-instrumentation-claude-agent-sdk returned a new { [Symbol.asyncIterator]() {...} } object instead of the SDK's Query. Every control method on Query (interrupt(), setPermissionMode(), setModel(), setMaxThinkingTokens(), applyFlagSettings(), reinitialize(), initializationResult()) and the AsyncGenerator methods (next()/return()/throw()) were undefined on the instrumented result. Code that stops a running query with q.interrupt() silently stopped working when tracing was enabled.

The wrapper now:

  • calls the SDK at query() time, as the unwrapped SDK does (it spawns the Claude Code process on call), and starts the AGENT span at that point so the injected PreToolUse/PostToolUse hooks have their parent span before the SDK sees them;
  • returns a Proxy over the real Query: [Symbol.asyncIterator], next, return and throw route through one shared traced iterator (created on first use, so the span ends exactly once whichever entry point the caller uses); every other member forwards to the SDK object, bound to it so implementations that rely on this keep working.

The traced iterator body is unchanged from before; it just moved out of [Symbol.asyncIterator]() so it can be shared. wrapQuery became generic over the return type so manuallyInstrument() keeps returning the same type the module exposes.

Why the suite did not catch it

Every test in test/v1Query.test.ts mocks query() as a plain { [Symbol.asyncIterator] }, so there were no methods to lose. This PR adds three tests:

  • a mock shaped like the real Query (async iterator plus interrupt() and setPermissionMode()), asserting the methods survive instrumentation, are callable, and that iteration still yields one OK AGENT span;
  • iteration driven by hand through next() and return() on the returned object is traced and ends the span;
  • the SDK query() is invoked when the wrapper is called, not when iteration begins.

All three fail on main and pass here. The rest of the suite is unchanged (45/45).

Behaviour notes for review

  • Span start time moves from first iteration to the query() call. In practice callers iterate immediately, and this is what the unwrapped SDK's process lifetime already looks like.
  • A caller who creates a query and never iterates now has an AGENT span that never ends (it was previously never created). That matches every other span for an abandoned operation.
  • Reproduced against the real @anthropic-ai/claude-agent-sdk@0.3.218 before the fix: Reflect.ownKeys(wrapped.query(...)) is [Symbol(Symbol.asyncIterator)], all eleven methods undefined.

Changeset included (patch).

🤖 Generated with Claude Code

https://claude.ai/code/session_01MXxV7qtX4GyTfo7QWaREnL

…ted query()

The wrapped query() returned a fresh { [Symbol.asyncIterator] } object, so
every method the SDK declares on Query (interrupt(), setPermissionMode(),
setModel(), next()/return()/throw(), ...) was undefined once tracing was on.
It also deferred the SDK call until iteration began, whereas the unwrapped
SDK spawns the Claude Code process at query() time.

Call the SDK at query() time, start the AGENT span there so the injected tool
hooks have their parent, and return a Proxy over the real Query: the async
iterator and next()/return()/throw() route through one shared traced iterator
(so the span ends exactly once), everything else forwards bound to the SDK
object.

Fixes Arize-ai#3774

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MXxV7qtX4GyTfo7QWaREnL
@iaj6

iaj6 commented Sep 17, 2026

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@iaj6
iaj6 requested a review from a team as a code owner September 17, 2026 17:35
@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅

github-actions Bot added a commit that referenced this pull request Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[bug] claude-agent-sdk (JS): instrumented query() drops the SDK Query object, so interrupt() and other control methods are undefined

1 participant