Repository navigation
@tanstack/ai@0.70.0
Minor Changes
-
#1723
1d0f8f1- Subagent limits, and provider keys for agents.subagents.limits:maxDepth,maxConcurrent,maxCalls, andtimeoutMsfor the whole tree of children the model starts through tools. The tree shares one budget (SubagentBudget), which a child'sctx.chat({ subagents })passes on, so a child cannot reset it. A refused start reaches the model as a tool error.subagents.keysandctx.keys(ProviderKeys:get,require, andadapter). Pass an object by provider id, for example{ openai: userKey }, or a function, for example(provider) => getByokKey(request, provider). An agent builds akeyedAdapterwithawait ctx.keys.adapter(adapter). A missing key falls back to the provider'senvnames. When there is no key at all, the call throws an error that names the env var. A child'sctx.chat({ subagents })passes the keys on to its own children. Keys that the child sets itself win for those providers.
-
#1723
1d0f8f1- A subagent can now return a plain value and call any activity.defineAgent'sruncan return a promise of any value, such as an image result. The value arrives onSUBAGENT_FINISHED.result, and the parent model gets it as the tool result. A very long string inside an object result (for example a base64 image) reaches the parent model as a short note (compactForModel). A string result also streams as the child's text. The result ofctx.chat({ stream: false })streams like the chat stream.rungets the activities onctx(ctx.chat,ctx.generateImage,ctx.generateVideo, and the rest), which fill in the thread id, a run id, and the abort signal.ctx.forwardholds the fields for a plainchat()call. The optionalproducesfield says what an agent makes.- A host can set
subagents.binding(SubagentBinding) to add chat and generation middleware to every activity call of a child. A child'sctx.chat({ subagents })passes it down to its own children. - Children inherit the prompt cache retention of the parent call.
- The chat middleware context has
subagentNameandparentSubagentRunId.chat()takes both as options. RunRecordgets optional fields for host sessions (kind,activity,agent,result,artifacts,principal,leaseOwner,leaseExpiresAt,checkpoint). The memory run stores keep them.
-
#1750
3170517- Check a raw JSON Schema tool input before the tool runs. This covers plain JSON Schema tools and every MCP tool.Behavior change:
- A tool with a JSON Schema
inputSchemanow gets a tool error (stateoutput-error) when the input does not fit the schema. The tool does not run. Before, any input ran. - The check runs at the same place as the Standard Schema check: after approval edits and middleware
onBeforeToolCall. - The draft comes from
$schema(draft-04, draft-07, 2019-09, 2020-12). With no$schema, the check uses 2020-12.formatis not checked. - OpenAPI 3.0 schemas work:
nullable: truelets the field benull, and a booleanexclusiveMinimumorexclusiveMaximumworks in every draft. - The error names each failed location (up to 10) and shows the received arguments:
Input validation failed for tool <name>: #/count: .... - A schema that the check cannot use, such as one with a remote
$ref, gives a tool error on each call. - The check uses
@cfworker/json-schema. It does not compile code, so it runs on Cloudflare Workers.
Migration: if a JSON Schema tool must accept input that its schema rejects, widen the schema.
- A tool with a JSON Schema
-
#1728
3292bad- Add theonModelErrormiddleware hook andctx.retry(). When a model call fails before it streams output,onModelErrorgets theRUN_ERROR. Callctx.retry()there to drop the failed call, runonConfigagain, and call the model again. A model call retries at most 2 times this way.ctx.retry()throws in any other hook. -
#1750
3170517- Keep the provider's raw tool argument string for replay.- Adapters now send the raw argument string as
argsonTOOL_CALL_END. - The tool call in the history keeps that exact string when it is the same JSON value as the parsed input, so the next request sends the provider's own bytes back (for example
1.50, or an id pastNumber.MAX_SAFE_INTEGER). When the parsed input differs, only the changed values are written again. - The
TOOL_CALL_ENDthat reaches the client carries the same string inmetadata.tanstack.args, so the client history matches the server history. It no longer also carriesmetadata.tanstack.input: in-processchat()andStreamProcessorparseinputfromargs. Run the client and the server on the same release: an older client ignoresargs, so it builds the tool input from the streamed deltas only, and a tool call with no deltas (for example an Anthropic server tool) gets no input. - History converters for Bedrock and the Gemini Interactions API send malformed tool arguments as
{}instead of failing the request. - Gemini Interactions: the initial
step.argumentssnapshot goes out as aTOOL_CALL_ARGSdelta only when no argument deltas follow, so the deltas always add up to the final arguments. - Malformed tool arguments are no longer turned into
{}by an adapter. The tool gets a "Failed to parse tool arguments as JSON" error that the model can repair. Mistral still ends the run with an error on malformed arguments, as before.
- Adapters now send the raw argument string as
-
#1723
1d0f8f1- Continue a child. WithwithPersistenceon the parentchat(), an agent tool result gives the model the child'ssubagentRunId, and a later call of the same agent tool withsessionIdcontinues that child from its stored transcript. With a host that setssubagents.binding.start,background: truestarts the child and returns at once. These fields sit next to the agent's own fields, and each agent tool has them only when the run can use them. When the run usessessionIdorbackground, that name is reserved:chat()throws when an agent'sinputSchemauses it. An agent withoutinputSchematakes the task as text inprompt, and the child gets it as the last user message. Agent tools also support thedetachfield of the tool context.The card keeps the checked input of an agent with
inputSchema. On the client,part.subagent.inputhas it, typed frominputSchemawhen the agent declaration goes touseChat({ subagents }).@tanstack/aiaddsLoadChildCapability,getLoadChild, andprovideLoadChild, so a persistence middleware can load a stored child. A provider listsLoadChildCapabilityinprovides.withPersistencein@tanstack/ai-persistenceprovides it: it stores the task of each child's first call and the thread that started it, and loads only the children of the calling thread. -
#1750
3170517- Repair common model mistakes in a tool input before the final input check.- When a JSON Schema tool's input (and every MCP tool's input) fails the check, it is repaired and checked again:
"5"as5(decimal text only),"true"astrue,5as"5"for a string, andnullas"",0, orfalsefor a required field. Anullfor an optional field that does not allownullis removed. The repair follows unions,$ref,if/then/else,prefixItems,patternProperties, andadditionalProperties. - A Standard Schema runs first as written. When it fails and the schema can export JSON Schema (Zod, ArkType, Valibot), the input is repaired and the schema runs again. A failure reports the issues of the first run.
- The received arguments do not change. The tool gets the repaired copy.
- New
chat({ repairToolInput })option.falseturns the repair off. A function replaces the built-in repair: it gets{ tool, toolCall, input, error }and returns the input to check again, orundefinedto send the validation error to the model.
Behavior change: a call that failed the check only because of these mistakes now runs with the repaired input.
- When a JSON Schema tool's input (and every MCP tool's input) fails the check, it is repaired and checked again:
-
#1750
3170517- Check a tool input at the last moment, just before the tool runs or the client gets the call.Behavior change:
- The tool's Standard Schema
inputSchemanow checks the input after approvaleditedArgsand after middlewareonBeforeToolCall. Before, atransformArgsvalue reachedexecuteor the client without a check. An input that does not fit the schema now becomes a tool error (stateoutput-error), and the tool does not run. An approval edit is also still checked when the run resumes. onBeforeToolCallgets the parsed input before the schema check. It has no schema defaults or transforms yet, and it can be invalid.- Async Standard Schema input validators are awaited. Before, every call to such a tool failed with an input validation error.
onBeforeToolCallnow also runs for a client tool before the server sends the call to the client. This includes a client request at an interrupt boundary. It does not run while a call waits for approval or for a denied call. When the client result comes back, the result is kept, with no second hook run and no second check.otelMiddlewarerecords noexecute_toolspan for a client tool, because the server does not run it.- A validation error after the hook runs
onAfterToolCallwithok: false. - The tool error that the model sees now also shows the arguments that the model sent. When an approval edit or middleware changed them, the error says so.
- While a call waits for approval, an approved client tool in the same batch is now sent to the client. Before, it waited for the next round trip.
- When the only call that waits for approval fails its input check, the other calls of the batch now run. Before, they got no result.
Migration:
- If a middleware
transformArgsor an approval edit sends a value that the schema rejects, make the value fit the schema, or widen the schema. - If
onBeforeToolCallreadshookCtx.args, check its shape first. Do not expect schema defaults there. - If a middleware counts or blocks calls in
onBeforeToolCalland must see only server tools, return early whenhookCtx.tool?.executeis missing.
- The tool's Standard Schema
Patch Changes
-
#1742
5f95dbd-decide()now reports token usage when the provider answered but the answers cannot be mapped, for example when an answer is missing, has the wrong type, or has no probability for its value. Before,decide()threw without reporting the usage that the provider already billed. TheonUsagemiddleware hook and theevaluate:usageevent now get that usage once, beforeonErrorruns.onFinishstill does not run for a failed call, anddecide()throws the mapping error. If anonUsagehook throws, that error is thrown instead, as it is for a successful call. The mapping error is still logged. -
#1789
4298480- Keep custom metadata fromREASONING_MESSAGE_STARTon the assistantUIMessage. The thinking text was already kept. Metadata that arrived only on the start event was dropped. -
#1776
f68da51- File each model call on its own assistant message. Reasoning that arrives before the call's text id, a tool call whose parent id is new, and a signature that names an earlier step stay on the message that owns them. -
#1786
30fa651- Fix subagent limit and continue edges.- A background
startthat throws gives themaxCallsslot back.maxConcurrentcounts that child untildonesettles.startnow returnsdone. - A schema result that is not an object does not drop
sessionIdorbackground. A top-levelanyOfschema still shows those fields. - A suspended child with no stored
basekeeps its transcript. - When the parent deadline is already past, the model gets
subagent limit reached (no time left).
- A background
-
Updated dependencies [
85c2956]:- @tanstack/ai-utils@0.5.0