Changelog
All notable changes to @nhtio/adk are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
2026-06-05
Added
- The
evaluate_katexmath tool now evaluates calculus numerically. It previously mangled any calculus input —\int_{0}^{1} x dxhad its bounds stripped by the LaTeX flattener and produced a crypticSyntax error in part "\int^(1) x dx". The tool now detects calculus on the raw LaTeX before flattening and computes it numerically with the bundled mathjs (no new dependency): definite integrals (\int_{a}^{b} f \,dx) via Simpson quadrature, derivatives at a point (\frac{d}{dx} f \big|_{x=a}) via central finite difference, and limits (\lim_{x \to a} f, includinga = \pm\infty) via a two-sided approach. Results are rounded and labelledResult (numeric):to flag the approximation. Genuinely uncomputable inputs (indefinite integrals, derivatives without a point, infinite integration bounds, singular integrands, divergent limits) return a specific, guiding error instead of a garbled one. mathjs has no symbolic integration and its symbolicderivativeis intentionally blocklisted here, so these are numeric methods.
Fixed
evaluate_katexnow maps inverse trig to the correct mathjs names.\arcsin,\arccos, and\arctanwere passed through asarcsin/arccos/arctan, which mathjs does not define, so every inverse-trig expression errored withUndefined function. They now translate toasin/acos/atan.
2026-06-04
Fixed
- LLM batteries now surface reasoning from providers that use the
reasoningfield. The OpenAI and WebLLM Chat Completions batteries read onlyreasoning_content, so thinking output from endpoints that emitreasoning(Ollama's/v1, post-rename vLLM, OpenRouter) produced no thought events in either streaming or non-streaming mode. Reasoning is not part of OpenAI's official Chat Completions spec, so OpenAI-compatible providers disagree on the field name; both batteries now readreasoningandreasoning_contentacross both the streaming delta and non-streaming message shapes. Verified live against a per-model matrix of real endpoints (claude-haiku-4-5, gemini-3.5-flash, gemma4, deepseek-v4-flash, glm-5.1, gpt-oss:20b, kimi-k2.6, and a workstation Ollama tag).
Added
reasoningFieldPrecedenceoption on the Chat Completions batteries. An ordered, de-duplicating control over which provider reasoning field wins. When more than one listed field is present with identical content (or only one is present) a single thought is emitted, attributed to the highest-precedence field; when they diverge, each surfaces as its own thought rather than silently dropping one (in streaming mode both stream live and are de-duplicated by content at persistence). Defaults to['reasoning', 'reasoning_content']. A typedreasoningfield was added to theChatCompletionsChunkDeltaandChatCompletionsResponseMessagewire shapes, and the newReasoningField/ReasoningFieldPrecedence/ReasoningExtracttypes plus theextractReasoningFieldshelper are exported from both batteries.
2026-06-03
Changed
- MCP install examples now render the current package version at docs build time. The ADK MCP guide uses a
1.20260605.0token for pinned@nhtio/adk@...examples, and the docs build rewrites it frompackage.jsonfor VitePress pages, LLM artifacts, the Ask ADK index, and the packaged MCP corpus. Release docs now stay aligned with the published package version without hand-editing install snippets before every tag.
2026-06-02
Fixed
- Corrected the
callIddocumentation on the tool-execution events.ToolExecutionStartEvent.callIdandToolExecutionEndEvent.callIdwere documented as correlating withToolCall.id. They do not:callIdissha256({ tool, args })— the same value asTurnToolCallContent.checksumandToolCall.checksum. The two buses join ontoolCall.checksum === toolExecution*.callId, never ontoolCall.id. The hash collides by design for identical(tool, args)(that is whatDispatchContext.toolCallCountcounts), so order or disambiguate repeated calls by theDateTimefields (createdAt/updatedAt,startedAt/endedAt). TSDoc and the Events guides now state this contract; no runtime behavior changed.
2026-06-01
Added
- Embeddings batteries (
@nhtio/adk/batteries/embeddings/openai,@nhtio/adk/batteries/embeddings/webllm) — two opt-in embedders that share one shape and differ only in their engine.OpenAIEmbeddingsAdapterPOSTs to any OpenAI-/v1/embeddings-compatible endpoint over rawfetch(Node/browser/edge/workers);WebLLMEmbeddingsAdapterembeds in-process on WebGPU via@mlc-ai/web-llm. Both exposeembed/embedMany/dimensions/preload/reset/isAvailable, return wire-nativenumber[]/number[][], require an explicitmodel(no default), and handle query/document instruction prefixes identically via a sharedkind: 'query' | 'document'option. The environment-neutral OpenAI battery is re-exported from@nhtio/adk/batteries/embeddings; the WebGPU-only WebLLM battery is reachable only via its own subpath. Embedders are tools you call from your own retrieval middleware — they do not plug into an executor slot. See the newdocs/assembly/batteries-embeddings.md.
Fixed
E_INVALID_TURN_RUNNER_CONFIGnow names the offending field. A misconfiguredTurnRunnerpreviously threw a generic "cannot be instantiated with the provided configuration" with no indication of which field failed. The exception now carries the validator's field-level detail (e.g.…: storeMediaBytesCallback is required) and attaches the rawValidationErroroncause.- Unknown-tool errors now list the available tools. When the model calls a tool that is not in the registry, the OpenAI and WebLLM Chat Completions batteries persist a tool-call error reading
Tool not found: <name>. Available tools: <a, b, c>.(orNo tools are available this turn.) so the model can self-correct on the next iteration instead of dead-ending on an opaque "not found".
2026-05-31
Added
- Packaged ADK Assembly MCP server (
src/mcp/server.ts) —@nhtio/adknow ships a local stdio MCP server that can be launched withnpx -y @nhtio/adk. The server exposes ADK assembly guidance, packaged documentation search, document reads, generated API lookup, and pasted-code assembly review through MCP tools, resources, and prompts. - Version-aligned MCP documentation corpus (
dist/mcp/adk-docs-corpus.json) — package generation now copies hand-written docs, generated TypeDoc API pages, changelog content, and the ADK assembly Skill into a read-only corpus for the MCP server. The corpus is built from the docs available at package time so MCP answers match the installed package version. - ADK MCP documentation page (
docs/mcp.md) — added a VitePress guide for installing and using the ADK MCP across common coding-agent clients, including VS Code / Copilot, Claude Code, Claude Desktop, Cursor, Windsurf, Cline / Roo Code, and Continue. - Unified
ByteStore<R>storage contract (src/lib/contracts/byte_store.ts) — the single low-level "give bytes, get a reader" shape every storage layer implements, withSpoolStore(ByteStore<SpoolReader>) andMediaStore(ByteStore<MediaReader>) semantic aliases.writeacceptsstring | Uint8Array | ReadableStream<Uint8Array>; string input is UTF-8-encoded. Exported alongsideimplementsByteStoreandbyteStoreSchema. - Injectable
spoolStoreoption on the OpenAI and WebLLM Chat Completions batteries — back tool-output artifacts with durable storage (OpfsSpoolStore, a Flydrive-backed store) instead of the default per-dispatch in-memory store. Durable stores also stream large/binary tool output to disk rather than buffering it in memory. ctx.storeMediaBytes(id, bytes)→MediaReaderandctx.storeRetrievableBytes(id, bytes)→SpoolReader— handler-reachable byte-persistence conduits that route tool-generated media and large extracted RAG text into consumer storage. Both accept aReadableStream. Exposed onTurnContextandDispatchContext;ConduitBytesis exported from the public API.- Reader-backed
Retrievable.content—contentnow accepts aSpooledArtifactin addition tostring | Tokenizable, so large extracted RAG text can live in a consumerByteStoreinstead of permanently on the heap. NewRetrievable.estimateTokens(encoding)andRetrievable.contentString()accessors. (Note: token estimation and render still materialise the body transiently; reader-backing removes permanent heap residency, not the transient allocation.)
Fixed
InMemorySpoolStoreno longer corrupts binary tool output. It previously UTF-8-decoded everyUint8Arrayat write time, mangling non-text bytes (PDFs, images). Bytes are now stored byte-faithfully;InMemorySpoolReaderdecodes on demand for line/text reads and reports the true stored byte length.
Changed (BREAKING)
- Documentation now builds before the library package in CI. The package build consumes the generated docs, API reference, and changelog artifact so the npm package always includes the MCP documentation corpus when built from tagged/default-branch CI jobs.
- The generated npm package now exposes an
adkbinary.bin/package.tswritesbin.adk = "./adk-mcp.mjs"into the packaged manifest and bundles the MCP SDK/Zod-backed server entry while keeping those MCP implementation dependencies out of the published runtime dependency list. - Render helpers are now async.
renderFirstPartyRetrievables,renderThirdPartyPublicRetrievables,renderThirdPartyPrivateRetrievables,renderRetrievables, andrenderChatCompletionsSystemPromptonChatCompletionsHelpersnow returnPromise<string>(previouslystring). Consumers who override these helpers must update their signatures. TurnRunnerConfiggains two required callbacks —storeMediaBytesCallbackandstoreRetrievableBytesCallback(both arity 3).RawDispatchContextgains the matching requiredstoreMediaBytes/storeRetrievableBytesfields.- Tool-output spool writes are now awaited — a custom
spoolStore.write()may return aPromise(required forReadableStreaminput).