Skip to content

Expose stable tool invocation context for idempotent side effects #5749

Description

@xiexiahao

Problem / Motivation

AgentRunner already has a stable provider tool-call ID when dispatching a tool, and runtime checkpoints use tool-call IDs to distinguish pending and completed work. However, the tool implementation does not receive the identity of the logical tool invocation. RequestContext exposes session_key and turn_id, but there is no per-tool invocation context carrying tool_call_id. This matters for tools with external side effects such as sending messages or email, creating remote records, invoking API mutations, or submitting work to another service. If execution is retried or resumed, a tool supporting downstream idempotency has no canonical nanobot invocation identity it can reuse as an idempotency key.

Proposed Solution

Expose the current logical tool invocation identity through a small runtime-context primitive:

@dataclass(frozen=True)
class ToolInvocationContext:
    tool_call_id: str
    invocation_key: str | None = None

AgentRunner would bind this context only while executing the corresponding tool call, and reset it afterward. A helper such as current_tool_invocation_context() would let context-aware tools or extensions inspect the identity without changing tool schemas or exposing internal metadata to the model. A deterministic invocation_key could be derived from session_key + tool_call_id, with an appropriate fallback without a session key. The same logical tool call should have the same key; nanobot need not persist results or enforce exactly-once execution.

Alternatives Considered

Mutating request-level metadata would be unsafe when multiple tool calls execute concurrently. A nested ContextVar scoped to one invocation preserves the existing request context and follows asyncio task isolation. This proposal does not add automatic retries, a durable idempotency database, exactly-once guarantees, a scheduler or task ledger, checkpoint changes, automatic replay, tool-schema changes, or new user-facing configuration. Downstream systems remain responsible for enforcing idempotency.

Related Component

Other

Additional Context

Suggested tests: single-call identity; parallel calls see their own IDs and keys; context cleanup; stable keys for the same (session_key, tool_call_id); distinct keys for different calls; and a defined SDK or ephemeral no-session fallback. Nanobot already has RequestContext identities, ToolCallRequest.id, and ContextVar-based runtime context.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions