Skip to main content

v3.5.0

Released: October 11, 2026

Highlights​

  • A stop takes effect at once: an aborted signal now ends the request in the middle of a provider call, a rate limit wait, or a tool, not only between tool rounds
  • stream() says when it was stopped: a stopped stream ends quietly with a last chunk whose finish_reason is 'aborted'
  • generate() rejects with an AbortError: the same convention as fetch
  • Tools respect the stop: tools that have not started are skipped, and tools receive the signal as ctx.signal
  • Providers cancel the real HTTP call: Vertex AI, Gemini, Anthropic on Vertex and Ollama generate() now pass the signal on
  • New exports: AbortedError, raceAbort, untilAborted
  • metadata.aborted on agent results: a stopped streaming agent run returns the text that had arrived and says it was stopped

@toolpack-sdk/knowledge has no changes in this release; it is bumped to 3.5.0 to keep the packages in step.


toolpack-sdk​

New features​

Stopping a request takes effect at once​

Pass an AbortSignal as signal on a request, as before. What is new is when the stop is seen. Previously it was only checked between tool rounds, so a stop during a slow provider call waited for that call to end. Now the caller is released right away, also in the middle of a provider call, a rate limit wait, a tool, or a conversation summary.

const controller = new AbortController();
stopButton.onclick = () => controller.abort();

for await (const chunk of toolpack.stream({ model, messages, signal: controller.signal })) {
if (chunk.finish_reason === 'aborted') break; // stopped: what arrived before is kept
process.stdout.write(chunk.delta);
}

What each call does on a stop:

CallOn a stop
stream()Ends without throwing. The last chunk is { delta: '', finish_reason: 'aborted' }
generate()Rejects with an AbortedError (name is 'AbortError', code is 'ABORTED')
import { AbortedError } from 'toolpack-sdk';

try {
const response = await toolpack.generate({ model, messages, signal: controller.signal });
} catch (err) {
if (err instanceof AbortedError) return; // stopped by the caller, not a failure
throw err;
}

Tools and the stop​

  • A tool that has not started when the stop arrives is not run. This also covers tool calls that had already arrived in a stream
  • The SDK stops waiting for a tool that is running, and for a pending human confirmation (onToolConfirm)
  • Tools receive the signal as ctx.signal, so a long-running tool can end its own work:
execute: async (args, ctx) => {
const res = await fetch(args.url, { signal: ctx.signal });
return await res.text();
}

A tool that does not read ctx.signal keeps running in the background after a stop; its result is ignored. Tools that run in the same parallel batch all start together, so a stop only skips the ones that have not started.

Providers cancel the HTTP call​

The signal is now passed to the provider's own HTTP request, so the call is cancelled and not left running in the background:

Providergenerate()stream()
Anthropic, OpenAI, OpenRouteralready cancelledalready cancelled
Vertex AInewnew
Gemininewnew
Anthropic on Vertexnewnew
Ollamanewalready cancelled

Custom providers that ignore request.signal still work: the caller is released at once, but their request runs to its end in the background.

New exports​

ExportWhat it is
AbortedErrorThe error generate() rejects with on a stop. Extends SDKError
raceAbort(promise, signal?)Settles like promise, unless the signal is aborted first: then it rejects with an AbortedError
untilAborted(source, signal?)Yields what an async iterable yields and ends quietly as soon as the signal is aborted
ToolContext.signalAbortSignal | undefined, aborted when the run was stopped
RetryOptions.signalFor withRetry: no further attempt is started, and a wait for one ends at once
CompletionChunk.finish_reasonNew value 'aborted'

Behaviour changes​

  • generate() with a signal now rejects with an AbortedError on a stop. Before, it waited for the provider call to end and returned the last response. Code that aborts a generate() call needs a try/catch (check err.name === 'AbortError' or err instanceof AbortedError)
  • stream() with a signal now ends with one extra chunk, { delta: '', finish_reason: 'aborted' }. Code that handles every finish_reason value should handle 'aborted'
  • After a stop, pending tools are skipped and the result of a running tool is not sent to the model
  • Requests without a signal behave exactly as before

Improvements and fixes​

  • A stop during the final text-only round at the tool-round cap is no longer swallowed: generate() rejects and stream() ends as stopped, like any other stop
  • A stop during the conversation summary (context window strategy summarize) is seen at once, and is no longer treated as a failed summary that falls back to pruning
  • A rate limit wait that is stopped clears its timer, so it no longer keeps the process alive for up to 60 seconds
  • A stop is never reported as a provider error


@toolpack-sdk/agents​

New features​

A stopped run reports that it was stopped​

Passing signal to invokeAgent() or run() works as before, and the stop now takes effect at once (see the toolpack-sdk notes). What the caller gets back depends on whether the run streams:

RunOn a stop
Streaming (onChunk set, or the mode has streaming)Resolves with the text that had arrived, and result.metadata.aborted is true
Not streamingRejects with an error whose name is 'AbortError'. onError and agent:error fire as for any failed run
const controller = new AbortController();

const result = await agent.invokeAgent({
message: 'Write a long report',
signal: controller.signal,
onChunk: (delta) => process.stdout.write(delta),
});

if (result.metadata?.aborted) {
// result.output is the part that arrived before the stop
}

Agent Mind on a stopped run​

A stopped streaming run is saved like a failed one: beliefs and reflections from the run are kept, and its goal writes are dropped. A run that was cut off should not record goals as done.


Behaviour changes​

  • A run that does not stream now rejects with an AbortError when its signal is aborted. Before, it returned the last response
  • A stop is seen in the middle of a model call or a tool, not only between tool rounds. Sub-agents reached through delegate_to_agent receive the same signal


Compatibility​

  • Node.js >= 20
  • No new required dependencies
  • @toolpack-sdk/agents 3.5.0 needs toolpack-sdk ^3.5.0
  • Code that does not pass a signal needs no changes. Code that aborts a generate() call, or an agent run that does not stream, should catch the AbortError (see Behaviour changes)

Install​

npm install toolpack-sdk@3.5.0
npm install @toolpack-sdk/knowledge@3.5.0
npm install @toolpack-sdk/agents@3.5.0