Skip to content

Conversation

@karpetrosyan
Copy link
Contributor

@karpetrosyan karpetrosyan commented Jan 15, 2026

How the Tool Runner Works

Iteration Lifecycle

On each iteration, the tool runner performs three key operations:

  1. State update (only if unchanged): The tool runner appends the last message from the API response (the one yielded to the client) to its internal state only if the state wasn't modified during that iteration via pushMessages() or setMessagesParams(). If the state was mutated, it ignores that message and continues using the user-mutated state.

  2. Tool handling (always): The tool runner inspects the last message. If it contains any tool_use blocks, it handles them and appends an appropriate message containing the corresponding tool_result blocks — regardless of whether the state was mutated.

  3. Next request + repeat: It sends a new request to the API using the current internal state, yields the new message to the user, and repeats the loop.

generateToolResponse()

The generateToolResponse() method is a helper that reads the tool_use blocks, calls the tools, and generates a message containing the corresponding tool_result blocks. Note that:

  • It does not mutate state — calling generateToolResponse alone won’t prevent the loop from adding its message to state
  • It caches results to avoid redundant calls — if you pass the same state, it returns the cached result

If you push both the last message and the result of generateToolResponse() into the state, the tool runner will effectively do nothing except send the next request:

for await (const message of runner) {
  const defaultResponse = await runner.generateToolResponse();

  if (defaultResponse) {
    runner.pushMessages(
      {
        role: message.role,
        content: message.content,
      },
      defaultResponse,
    );
  }
}

Execution Flow Diagram

Note: If the Mermaid diagram below doesn't render in your environment, view it on GitHub: https://github.com/anthropics/anthropic-sdk-typescript/blob/main/helpers.md

sequenceDiagram
  autonumber
  participant U as User
  participant TR as ToolRunner
  participant API as Model API
  participant Tools as Tools

  loop Repeat until done
    TR->>API: Send request (using current state)
    API-->>TR: Message
    TR-->>U: Yield message

    note over U: User can read message<br/>and optionally change state via<br/>pushMessages or setMessagesParams
    U->>TR: Resume iteration

    alt User did not change state
      TR->>TR: Append message to history
    else User changed state
      TR->>TR: Keep user state (no auto-append)
    end

    alt Message contains tool request
      TR->>Tools: Run tools (with generateToolResponse)
      Tools-->>TR: Tool results
      TR->>TR: Append tool results
    else No tool request
      TR->>TR: Finish
    end
  end
Loading

@karpetrosyan karpetrosyan requested a review from a team as a code owner January 15, 2026 15:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant