Skip to main content

v3.4.0

Released: October 5, 2026

Highlights​

  • Acting user for tools: hosts can now say who a run acts for; every tool receives it as ctx.actor, and the model can never see or set it
  • Tools registered mid-run are picked up: request tools added during a tool loop (for example by mcp.overview) are available in the next round
  • Graceful finish at the tool-round cap: when the cap is hit with tool calls still pending, the model gets one final text-only round instead of returning empty content
  • File envelopes in tool results: tool results shaped as { "type": "file", "mimeType", "url" } are passed to Anthropic and Vertex AI as real file parts, and to OpenAI as images
  • Gemini and Vertex AI thinking models: multi-step tool calls with thinking models (Gemini 3.x and later) no longer fail with a 400 on the second turn
  • disableToolGuidance: turn off the automatic tool usage guidance in the system prompt
  • Better tool logging: the tools sent with each request are logged, and debug logs show full message and response content
  • A failed memory save no longer loses the reply: the run succeeds and the failure is reported in result.metadata.mindSaveError
  • onChunk streaming callback: receive each text delta as it is generated, per run

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


toolpack-sdk​

New features​

actor for per-user tools​

A new actor init option tells the SDK who the run is for (for example the signed-in user of your application). It is passed to every tool as ctx.actor. It is set by the host only: it is not part of any tool schema, so the model cannot read it or change it.

const toolpack = await Toolpack.init({
provider: 'openai',
tools: true,
actor: { id: 'user_123', kind: 'user' },
});

When one instance serves several people, pass a function. It is read on every tool call:

import { AsyncLocalStorage } from 'node:async_hooks';

const current = new AsyncLocalStorage<{ id: string }>();

const toolpack = await Toolpack.init({
provider: 'openai',
tools: true,
actor: () => current.getStore() ?? null,
});

Inside a tool:

execute: async (args, ctx) => {
const userId = ctx.actor?.id; // undefined when the host set no actor
// load only this user's data
}
TypeFields
ToolActorid: string, kind?: string
ToolContext.actorToolActor | undefined

ToolActor is exported from the package root. The option is also available on AIClientConfig.

Request tools registered mid-run​

Request tools registered while a tool loop is running (for example by mcp.overview) are now merged into the following round, in both generate() and stream(), and can be called right away. Previously they only became available on the next request.

Graceful conclusion at the tool-round cap​

When maxToolRounds is reached and the model still wants to call tools, the SDK now makes one final round with no tools and asks the model to finish or explain what it could not do. Previously the run ended with empty content (or, when streaming, simply stopped). Aborted runs are not affected. If the final call fails, the last response is returned as before.

File envelopes in tool results​

A tool can return a file by URL without embedding its bytes:

{ "type": "file", "mimeType": "application/pdf", "url": "https://example.com/report.pdf" }

How each provider handles it:

ProviderImage (image/*)Other files (PDF, etc.)
Anthropicimage part by URLdocument part by URL
Vertex AIfile part by URIfile part by URI
OpenAIimage_url partplaceholder text ("File content not available inline for this provider")
Geminino envelope supportno envelope support

Existing data: URI results work as before. parseFileEnvelope and the FileEnvelope type are exported from the package root.

disableToolGuidance​

The SDK adds short guidance about using tools to the system prompt. Hosts that write their own tool instructions can turn it off:

const toolpack = await Toolpack.init({
provider: 'openai',
tools: true,
disableToolGuidance: true,
});

It is independent of disableBaseContext.


Improvements and fixes​

  • Gemini and Vertex AI thinking models: these models attach a thought_signature to tool calls and reject the next turn unless the exact original content is sent back. Gemini now keeps the raw response content for tool-call turns (up to 500 entries) and replays it, in both generate() and stream(). Vertex AI already did this for generate() and now does it for stream() too. When the cache has no match, the previous reconstruction is used
  • Vertex AI: image and file parts with empty data or an empty URL are dropped instead of being sent, which Vertex AI rejects
  • tool.search now retries without the category filter when a category name matches nothing (models sometimes guess names like filesystem), so the search still returns useful tools
  • The tools sent with each request are logged at info level (names) and debug level (description and schema), through the new logTools helper
  • Debug logs now show full message content and full model responses instead of 200 to 300 character previews


@toolpack-sdk/agents​

New features​

mindSaveError on AgentResult.metadata​

At the end of a clean run the agent commits what it learned to its mind. Before, if that commit threw, the whole run failed and the finished reply was discarded. Now the reply is kept, the error is logged, and the run returns normally with the message of the failure in metadata.mindSaveError:

const result = await agent.run('Remember that my wife is called Anna');

if (result.metadata?.mindSaveError) {
// The reply is fine, but nothing from this run was saved to memory.
console.warn('Memory not saved:', result.metadata.mindSaveError);
}

Use it to tell the user their memory was not saved while still showing the answer. agent:complete is still emitted.

onChunk streaming callback​

AgentInput and AgentRunOptions accept an onChunk callback. When it is set, the run uses streaming and calls it with every text delta. The final AgentResult is unchanged.

let text = '';
const result = await agent.run('Write a short summary', undefined, {
onChunk: (delta) => process.stdout.write(delta),
});

Runs without onChunk behave as before (streaming still turns on when the agent's mode has streaming set).



Compatibility​

  • Node.js >= 20
  • No new required dependencies
  • All changes are additive; existing code keeps working without changes

Install​

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