Skip to content

[Bug]: Tool execution intercepts cannot access the managed tool_call_id #1006

Description

@ericevans-nv

Affected area

  • Rust core runtime
  • Language bindings
  • Middleware
  • Plugins

Current behavior

Managed tool execution accepts an optional tool_call_id. Relay preserves this
value on the managed tool handle and lifecycle events, but does not pass it to
tool execution intercepts.

The current execution-intercept callback receives only:

tool_name, arguments, next_call

The runtime already has the tool_call_id when it constructs the execution
chain, but the value is omitted from the callback invocation.

As a result, a plugin cannot inspect or preserve the originating tool-call
identity when it returns a result without invoking next_call. The value cannot
be recovered through the existing callback arguments.

Relevant source:

  • crates/core/src/api/tool.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/runtime/callbacks.rs

Expected behavior

Tool execution intercepts should receive the optional tool_call_id supplied
to managed execution.

Impact

Plugins do not have access to the complete identity of the tool execution they
are intercepting.

This prevents a plugin from reliably associating a result with the originating
tool call when it completes the execution itself instead of invoking the
remaining execution chain.

There is no equivalent value available through the existing callback arguments,
and invoking next_call is not a valid way to recover information that should
already be part of the interception context.

API design options

Both viable approaches change the existing tool execution-intercept callback
contract and are breaking API changes.

Option A: Introduce ToolExecutionContext

Replace:

async def intercept(tool_name, arguments, next_call):
    ...

with:

async def intercept(
    context: ToolExecutionContext,
    next_call,
) -> ToolExecutionInterceptOutcome:
    ...

The initial context contains:

  • tool_name
  • arguments
  • optional tool_call_id

This requires existing callbacks to migrate, but future optional context fields
can be added without changing the callback signature again.

Option B: Add tool_call_id as a callback parameter

Replace:

async def intercept(tool_name, arguments, next_call):
    ...

with:

async def intercept(
    tool_name,
    arguments,
    tool_call_id,
    next_call,
) -> ToolExecutionInterceptOutcome:
    ...

This is the smaller change, but another required execution value would require
another callback-signature migration.

Recommended direction

Prefer Option A because Relay maintains this callback contract across multiple
language bindings and plugin transports. A typed execution context requires a
larger initial migration but establishes a stable interface for future optional
execution data.

Whichever option is selected:

  • Document the breaking callback migration.
  • Update primary bindings together.
  • Version native-plugin ABI changes instead of modifying an existing ABI table
    in place.
  • Update worker protocol and SDK compatibility requirements.
  • Reject incompatible plugin/runtime combinations with a clear version error.

Acceptance criteria

  • An execution intercept receives the exact tool_call_id supplied to managed
    tool execution.
  • An omitted tool_call_id remains absent.
  • An intercept can return a result without invoking next_call while retaining
    the originating tool-call identity.
  • The selected callback contract is implemented consistently across Rust,
    Python, and Node.js.
  • Experimental and dynamic-plugin surfaces preserve their documented
    compatibility boundaries.
  • Native and worker plugin boundaries reject incompatible versions safely.
  • Documentation explains the selected API and migration path.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Bugissue describes bug; PR fixes bugImprovementimprovement to existing functionality

Type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions