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 onChunkstreaming 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
}
| Type | Fields |
|---|---|
ToolActor | id: string, kind?: string |
ToolContext.actor | ToolActor | 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:
| Provider | Image (image/*) | Other files (PDF, etc.) |
|---|---|---|
| Anthropic | image part by URL | document part by URL |
| Vertex AI | file part by URI | file part by URI |
| OpenAI | image_url part | placeholder text ("File content not available inline for this provider") |
| Gemini | no envelope support | no 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_signatureto 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 bothgenerate()andstream(). Vertex AI already did this forgenerate()and now does it forstream()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.searchnow retries without the category filter when a category name matches nothing (models sometimes guess names likefilesystem), 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
logToolshelper - 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