All notable changes to the Anda project will be documented in this file.
- Runners no longer compact after a fixed number of turns β
CompletionRunner::needs_compaction_withreturnedtrueonce a runner had run 81 turns, whatever its context usage, so a long tool-heavy task on a large-window model could lose its context to a lossy handoff, and the provider's cached prefix with it, long before the window filled. It now reports compaction only when the latest turn's reported input plus the input still to be sent reaches 80% of the model's context window, or when an admittedcompact_contextrequest is pending. The turn cap was also the backstop for providers that report no usage; with them, compaction now comes from the model's owncompact_context, whose usage notes estimate the conversation, or from the host's recovery when the provider rejects an oversized request.
- A tool that joins a DeepSeek conversation below max effort is usable
again β 0.17.2 activated every tool that joined a
deepseek-flashconversation from a systemtool_additionmessage, and below max effort the model often answered that the tool did not exist: asked to call a tool it had just been given, it said so in 16 of 20 requests atlowand 12 of 20 athigh, against none of 50 when the tool was declared as usual. The Anthropic adapter now uses mid-conversation tool additions only for requests that run at max effort, by their own effort or the model's default, which missed once in 46 requests. Other requests declare every tool as usual and replay the conversation without its activations, so they read nothing from the cache after a tool joins. Anda Bot's defaultdeepseek-flashruns at max and keeps the cached prefix.
anda_engine 0.17.2 requires anda_core 0.17.1.
CompletionRequest::tool_additionsβ the names oftoolsthat joined the conversation after the request that produced the replayedraw_history. The completion runner fills it in for every request; a caller that builds requests itself can leave it empty. Code that builds aCompletionRequestwith a struct literal needs..Default::default().
- A tool that joins a DeepSeek conversation keeps the prompt cache β tools
come first in the prompt a provider caches, so a tool added in the middle of
a conversation (an attachment's reader, a skill's callable, a merged
discovered tool) made the provider re-read the whole conversation at the
uncached price. DeepSeek's Anthropic-compatible API can activate a tool
where it joins: the runner now reports each request's new tools, and for
deepseek-flashthe Anthropic adapter declares them withdefer_loading: trueafter the tools offered from the start, activates them from a systemtool_additionmessage after the input that brought them, and sendsanthropic-beta: mid-conversation-tool-changes-2026-07-01. The activation joins the raw history at its place, so later requests replay the same prefix; pruning tool rounds keeps it, and an activation whose tool is no longer offered is dropped.CompletionModel::with_tool_additionsswitches the capability for other models; it is off for every model butdeepseek-flash, including Anthropic's own, which accepts no system messages. Other adapters send new tools as before. Against the live API, the request that added a tool read 2,560 of the previous request's ~2,690 input tokens from the cache, against 128 without the extension; the ignoredtests/deepseek_live.rsrepeats the check withDEEPSEEK_API_KEY.
minimalturns DeepSeek's thinking off β DeepSeek thinks unless thinking is disabled, sominimal, sent aslow, still thought. On a DeepSeek model the Anthropic adapter now sendsthinking: {"type": "disabled"}forminimalinstead; live, the answer took one output token and no thinking. The other efforts pass through as before, and a request effort turns thinking back on over aminimaldefault. Other models keep their mapping.
- A strict tool that joins a DeepSeek conversation no longer costs the
cache β Anthropic caps the strict tools of one request, and the adapter
spent that budget in request order before it moved the joining tool behind
the tools offered from the start. A strict tool added once the caps were
reached (an extension tool, or one appended ahead of the discovered tools
and
compact_context) took a strict slot from a tool declared before, whose declaration then changed and invalidated the prefix the deferred declaration exists to keep. The joining tool is now moved first, so it gets only the budget the tools offered before it leave. - The README describes the DeepSeek mappings β
anda_engine's README still saidminimaleffort maps tolowon every Anthropic model, and it did not mentionCompletionRequest::tool_additionsorCompletionModel::with_tool_additions.
- Follow-ups after a Claude tool turn no longer fail β at an idle boundary
the runner prunes completed tool calls and results from the provider raw
history, but it kept each tool round's signed thinking, so a
[thinking, tool_use]message became a thinking-only message. With the tool results between them gone, Anthropic merged it into the turn's final answer and rejected the next request with "thinkingorredacted_thinkingblocks in the latest assistant message cannot be modified". Models that think by default, such as Claude Opus 5.5, failed on every message after a tool-using turn. A message that lost a tool call is no longer the response its thinking was signed for, so itsthinkingandredacted_thinkingblocks now leave with the call, after an interrupt as well as at the idle boundary. Thinking is optional in request input; the final answer is unchanged and keeps its thinking.
This release removes remote engines, agents and tools, which changes the
anda_core context traits, and adds agent-initiated context compaction to
anda_engine. Because the trait change is breaking, every workspace crate
moves to 0.17.0 instead of taking a patch release.
-
Workspace release alignment β
anda_core,anda_engine,anda_engine_server,anda_web3_client, andanda_clinow share version0.17.0; internal crate requirements have been raised to0.17. The server, web3 client and CLI sources are unchanged. -
tokio 1.53.3 minimum β the workspace
tokiorequirement rises from1to1.53.3, the first 1.53 release withAsyncFd::register. -
serde-saphyr 2 β the YAML library
anda_engineuses for skill frontmatter and skill metadata sidecars moves from 1 to 2. No code changes were needed.
- Remote calls in the context traits β
AgentContextno longer hasremote_tool_definitions,remote_agent_definitionsorremote_agent_run, andBaseContextno longer hasremote_tool_call; it is now a pure bound over the capability traits, implemented with an emptyimpl.AgentContext::tool_callandagent_runreturn the output alone instead of pairing it with the principal of the remote engine that ran it. Theremote_idfields ofToolCallandContentPart::ToolOutputstay so stored histories keep decoding.
- Remote engines, remote agents and remote tools β
RemoteEngines,RemoteEngineArgs,RemoteTool,RemoteAgent,EngineBuilder::register_remote_engine, theDYNAMIC_REMOTE_ENGINES(_engines) registry, and theRT_/RA_routing prefixes (REMOTE_TOOL_PREFIX,REMOTE_AGENT_PREFIX) are gone.AgentCtxroutes only to local tools, tool providers, local agents andSA_subagents;RT_andRA_names are now ordinary names, and the completion runner and tool search no longer treat them specially.EngineCardandAgentInforemain forEngine::information.
- Agent-initiated context compaction β
CompletionRunner::set_self_compaction(SelfCompaction) offers the model a runner-intrinsiccompact_contextcall (COMPACT_CONTEXT_TOOL, a reserved name). The model calls it at a task or phase boundary with a handoff it writes itself, sohandoffseeds the replacement runner with that text and sends no summary turn, which would otherwise replay the whole context without the prompt cache. The runner accepts a request only when it is the sole call in its response, carries a 200 Bβ64 KiB handoff, and context usage is at leastmin_percent(default 30%) of the window, and it is not the first turn after a compaction; otherwise the model gets the reason as a tool error and carries on. An accepted request makesneeds_compaction_withreturntrue, so hosts that check it before every step run it through their existinghandoffpath; elsewhere it is answered as unavailable. If the model asks before acting on its latest user input, that input is delivered again verbatim after the handoff, ahead of any queued steering. Fromnotice_percent(default 50%) the runner reports context usage in a short request-only$systemnote: on every user turn, and once per 10% band during tool rounds. The note follows the tool outputs and is never written tochat_history. Before the first turn of a runner seeded with a conversation, usage is estimated from that history. The definition is appended after the request's own and discovered tools. Subagent session runners enable it by default.
-
Compaction no longer stalls an unfinished task β when
handofffolded tool outputs the model had not seen into the summary (compaction during a tool round, or outputs left by a failed step) and nothing else was queued, the replacement runner had no input and went idle, so a subagent session stopped mid-task. The replacement runner now resumes withCOMPACTION_CONTINUE_PROMPT. Hosts that queue their own continuation prompt after a handoff should do so only when the runner is idle, or the two prompts share one user turn. -
PTY sessions register their terminal with
AsyncFd::registerβ tokio 1.53.3 deprecatesAsyncFd::newas unsound, since it cannot know the descriptor stays open for the registration's lifetime. The PTY master is now registered through theunsafereplacement; theFilethat owns it moves into theAsyncFd, which upholds that requirement.
- MCP Events client β
McpToolProviderimplements the client side of the experimental MCP Events extension.list_eventsreads a server's event types (and returnsNonewhen the server does not support them);subscribe_eventsruns a poll or push subscription and reports events, cursors, list changes, errors and termination to an applicationMcpEventSink, using a cursor only after the sink accepted it;McpEventSubscription::canceldoes not wait for a busy sink.subscribe_webhookandunsubscribe_webhookmanage webhook subscriptions whose deliveries the application receives itself, and report failures asMcpEventErrors;call_server_toollets the application call a server's tool by its remote name, outside the model's catalog. Event notifications are taken off the transport in arrival order, since rmcp hands each notification to its own task, and a pushed event keeps its_meta(without the stream's subscription id) as a polled one does. Both generations of the draft's error codes are recognized. SeeMCP_INTEGRATION.md.
- Claude requests use the prompt cache β Anthropic caches a prompt only up
to a
cache_controlbreakpoint, and the adapter never sent one, so every round of an agent loop billed its whole prefix at the full input price and reportedcached_tokens: 0. Requests now turn on Anthropic's automatic caching with the top-levelcache_controlfield, which moves the breakpoint forward as the conversation grows, and mark the end of the system prompt, which caches the tools and system for conversations that share them. The messages, and the raw history replayed from them, are sent as given. A top-levelcache_controlset in the default request is kept, and its TTL applies to the system marker too. Anthropic-compatible endpoints that honor only block markers, such as MiniMax, cache just the tools and system. - Responses requests keep one prompt cache per conversation β the OpenAI
API routes a request to a prompt cache by
prompt_cache_key, and ChatGPT's Codex backend by thesession-idheader. The Responses adapter sent neither, so the rounds of one conversation landed on unrelated caches. It now sends a hex digest of the instructions and the first input item as both; aprompt_cache_keyset in the default request is kept.
- Tools with top-level schema combinators work on Claude β Anthropic
rejects
oneOf,allOfandanyOfat the top level of any tool's input schema and fails the whole request with a 400. KIP'sexecute_kipandexecute_kip_readonlystatecommandxoroperationsthat way, so every Brain Formation, Maintenance and Recall pass on a Claude model failed. The adapter now drops those top-level keywords; tools enforce such cross-field rules when parsing their arguments, and MCP servers' schemas are covered too.
- Strict tools stay within Anthropic's request caps β Anthropic caps the
strict schemas of one request in total: 20 strict tools, 24 optional
parameters, and 16 parameters with
anyOfor a type array. Hosts with many nullable fields (such as anda-bot's browser tools) exceeded the union cap, and the whole request failed with a 400. The adapter now counts what each strict schema spends, starting with the JSON output schema. A strict tool that would exceed a cap is sent as a regular tool with its schema unchanged, as a tool outside the strict subset already was. Every nested schema is counted, so the count can only overestimate the API's. - Nullable enums no longer spend the union budget β A multi-type enum is now
sent as a bare
enum(its values already list every allowed type) instead of theanyOfsplit added in 0.16.6, which counted as a union parameter.
- Strict tools with a nullable enum work on Claude β Anthropic strict mode
checks each enum value against a single declared type, so it rejected the
nullable enum that OpenAI strict mode documents (
"type": ["string", "null"]withnullinenum) with a 400 (Enum value ... does not match declared type), and every request offering such a tool failed, including the built-in subagent tools'effort. When the Anthropic adapter prepares a strict tool or output schema, it now splits a multi-type enum into ananyOfwith one branch per declared type. Other providers still receive the schema unchanged. - A session command reported as running always reports its exit β The
session supervisor emitted the background start and end events only if it
moved the command to the background itself. When it first ran after the
process had already exited, which a busy runtime makes likely for
background: truecommands, the starting call could still replyRunningwhile neither event came, so a host waiting for the end (such as a cron job) waited forever. The reply now decides what it reports under the status lock the supervisor publishes the exit through; a command reported as running always gets its start and end events, and one reported finished gets none.
- Task-scoped inline attachments β
CompletionRunner::set_transient_inline_datalets a long-lived runner send attachment bytes without keeping them: requestInlineDataparts (andFileDatawith adata:URI) reach the model on every turn of the task that introduced them, never enter the neutralchat_history(a message left empty keeps a short note), and are replaced inraw_historyby a text note when the runner goes idle,stop_current_taskruns, ordiscard_in_flight_requestdrops the task. Media the model generated stays in its reply. A model switch orhandoffin the middle of a task replays the neutral history, so the bytes leave early. A runner created byhandoffinherits the setting. CompletionFeaturesDyn::prune_inline_mediaβ replaces inline media in provider raw history (Anthropic base64/text image and document blocks, OpenAI Chatimage_url/video_urldata URLs,input_audioandfiledata, OpenAI Responsesinput_image/input_file/input_audioincludingdata:file URLs, GeminiinlineDataanddata:fileData) with a note in the provider's text shape, keeping media referenced by URL or file id and the messages the model wrote. Gemini function-response media, whichFunctionResponsePartaccepts only as media, leavesparts, and its note joins the response text. The default is a union over the built-in adapters;Model::prune_inline_mediaforwards to it.CompletionRunner::add_toolsβ appends tool definitions not already offered without resetting the tools discovered throughtools_select, whichset_toolsforgets.
- Stopping a task keeps the conversation the runner replays β
CompletionRunner::stop_current_taskno longer clears the request history. A stop that interrupted the first turn (a subagent or host control drops the in-flight step) used to drop the seededchat_historyand the note index, so every later request reached the model without the earlier conversation. The request history is now cleared only together with the pruned raw tool calls of an interrupted tool round. - Discarding a follow-up tool round leaves no orphaned tool output β When
queued follow-up was delivered with tool results and that request failed,
discard_in_flight_requestpruned the raw tool calls but kept the committed tool message, so the next request sent a tool result without its call and the provider rejected it. handoffdelivers pending input instead of summarizing it β Request input the runner had already accepted (an initial prompt, documents, or follow-up promoted after the last reply) now moves to the replacement runner as its next request, along with the implicit context, which was dropped before. Only pending tool outputs are summarized with the history. Previously that input was folded into the summary, so a replacement without other queued input went idle without answering it.- A failed
handoffkeeps executed tool outputs queued β Pending tool calls run before the summary turn; if the summary then failed, their outputs had already been committed and the next step never sent them to the model. They now stay as request input until a summary succeeds. CompletionStreamends after an error β A failed step was retried on every poll, so collecting a stream against a persistently failing model (or no configured model) never finished. The runner keeps its state for callers that want to recover throughstream.runner.- Interrupted tool calls are answered in a replayed history β After a live
model switch the request replays the neutral history, which holds the pending
tool calls; a stop or discard closed them only in
chat_history, so the next request sent a tool call without a result and the provider rejected it. - Implicit context no longer splits tool calls from their outputs β A step
that sends tool outputs left queued by a failed
handoffor a failed request inserted the implicit context as a user message between the calls and their outputs. It now waits for the next user turn, like the note index. - Per-caller memory requests no longer carry the provisioning future inline β
Under
Tenancy::PerCallerSpaceevery KIP tool call embedded the ~43 KB future that provisions a caller's Principal and Space, and each layer above it reserved that much again, so debug builds overflowed a 2 MiB thread stack (theper_caller_spaces_isolate_one_tenant_from_anothertest aborted withanda_cognitive_nexus0.14.3). Provisioning is boxed, which shrinks a memory tool call future from 48 KB to 6 KB.
- Inline images are no longer sized by their bytes β
ContentPart::estimated_tokenscaps animage/*InlineDatapart, or aFileDatapart whosedata:URI carries an image, at 5,000 tokens. Providers bill an image by its resolution and downscale large ones, so thelen / 3estimate made one phone photo look like a million tokens and pushed callers into needless context compaction. Other inline data keeps the byte-based estimate. - Forced tool choice documented β
CompletionRequest::tool_choice_requirednow explains that adapters map it to the provider's forced tool choice, and that Claude Opus 5.5, Claude Sonnet 5.5, and Claude Fable 5.1 reject it with a 400: leave it unset for those models, name the tool in the prompt, and mark the toolstrictwhen schema-valid arguments matter.
- Simpler completion runner internals β Tool execution returns its outputs
without an infallible
Resultor optional results and shares one error constructor, the unprefixed-subagent dispatch branch that could only fail is gone, andfinalize,AgentCtx::completion, history replay, idle snapshots, queued-input draining, and the note index loader share helpers instead of repeating them. Steps without a subagent scope no longer allocate a default one. - Dependencies β The workspace requires
rmcp3.5 andsha20.11.sse-streamstays on 0.2, the version rmcp builds itsBoxedSseResponsefrom, andanda_enginehex-encodes SHA-256 digests throughhex, sincesha20.11 output no longer formats with{:x}; the encoded strings, and so checkpoint keys and patch hashes, are unchanged.
tools_selectworks on current Claude models β The internal selector request no longer sendstemperature: 0.0. Claude models from Opus 4.7 on reject a non-default temperature with a 400, which the selector swallowed, so selecting tools byquerysilently returned nothing. The structured output schema already constrains the answer.- Context compaction keeps working under preserved thinking β The summary
turn clears
tools, which invalidates the replayed thinking blocks on Claude Opus 5.5, Claude Sonnet 5.5, and Claude Fable 5.1 (a 400 for accounts the check is enforced on), sohandoffand subagent compaction failed. That one request now replays the provider-neutral history instead ofraw_history, as a model switch does; the replacement runner is unchanged.
- Tool descriptions state their contracts β
read_file,edit_file,write_file,search_file, andfetch_web_resourcesnow describe their actual behavior: paging and the 256 KiB inline cap, that an edit with zero matches succeeds withtotal_matches: 0, parent-directory creation and full overwrite, path-only glob matching andscan_truncated, and the public-host, non-2xx, raw-body, and 20 MB rules for fetches. The legacyShellToolbackgroundparameter describes what the model sees instead of host hook internals. - Docs β The
anda_engineREADME notes that Anthropic mapstool_choice_requiredtotool_choice: any, which current Claude models reject. - Dependencies β
anda_enginedeclares itsanda_corerequirement as0.16instead of0.16.2.
- Shell approval gates survive the switch to
ShellCommandToolβ AShellToolHookinstalled for the legacyshelltool now also gatesShellCommandTool: itsbefore_tool_callsees the legacy fields just before execution and its rewrites apply, and itsafter_tool_callsees the legacy part of the result. Before, it still received the background events of session commands while their arguments bypassed it.ShellCommandToolHookandShellSessionToolHookname the tools' own typed hooks. - Sessions in separately authorized directories β
NativeRuntime::for_workspacederives a runtime rooted at another directory that shares the runtime's policy and sessions. A separately constructed runtime, which the docs suggested for such calls, cancelled its sessions when dropped, so a command still running after the foreground wait was killed andshell_sessioncould not reach it. - No console windows for session commands on Windows β Session processes are
created with
CREATE_NO_WINDOW, so a host without a console of its own no longer opens a window per command.
This release adds supervised shell sessions and workspace patches, an isolated
subagent runtime, bounded skill catalogs, MCP runtime policies, and paged note
retrieval to anda_engine, and provider-neutral tool result presentations to
anda_core. Only these two crates move to 0.16.2.
-
Release scope β
anda_coreandanda_enginemove to0.16.2, andanda_enginenow requiresanda_core0.16.2forToolPresentation.anda_engine_server,anda_web3_client, andanda_clihave no changes and stay at0.16.1. -
Dependencies β The workspace requires
rmcp3.4.1 and addssha2andsse-stream.anda_enginealso depends onthiserrorand enables theWin32_Storage_FileSystemandWin32_Foundationfeatures ofwindows-sys.
- Tool result presentations β
ToolOutput<T>gains an optionalmodel_output: Option<ToolPresentation>(text plus inlineToolMedia) that the runner sends to the model in place ofoutput, which callers and audit hooks still receive in full. The presentation is stored as an explicitly tagged value insideContentPart::ToolOutput.output, so the tool-call boundary and replay across providers are preserved. Older serialized outputs default to no override; struct literals must initialize the new field, and hooks that rewrite results must update or clear it.
- Add the opt-in
extension::workspace::coding_toolsbundle (shell,shell_session,apply_patch) plusreadonly_file_toolsandfile_tools.ShellCommandToolreplaces the legacyShellToolin that bundle (both are namedshell; register only one).ExecArgs,ExecOutput,ShellTooland customExecutors stay source-compatible; the new session methods onExecutordefault to unsupported. - Supervise process sessions scoped by a host-created
ShellSessionScope, the engine, caller and agent, withSessionLimitsbounding retained sessions, runtime, output previews and raw logs. Interactive stdin and Unix PTYs require host opt-in.SandboxPolicywraps commands withsandbox-execon macOS orbwrapon Linux, denies network by default, and never falls back to unrestricted execution. apply_patchprevalidates and locks multi-file patches, supports dry runs and expected SHA-256 versions, preserves encodings and line endings, and never overwrites on add or move. Writes are atomic per file, not across files.- Filesystem tools read and replace files through validated no-follow handles,
serialize cooperating writes, and bound the bytes actually read. Truncated
legacy
shelloutput now keeps both its beginning and its tail.
- Validate todo writes atomically; reject missing items, unknown operations and statuses, empty task fields, and oversized lists. Preserve explicit empty-list clearing, stable IDs, incremental updates, and the cancelled status.
- Add optional task-change explanations through existing typed hooks and bounded active-task recovery after successful completion-runner handoff.
- Add bounded note ID reads, listings, literal search, and UTF-8-safe pagination bound to the query and current store. Keep the persisted note shape unchanged.
- Add opt-in note context indexes and error-preserving
try_load_notes; preserve the existing full-snapshot convenience loader. Note lock waits respect cancellation. - Todo store/session writes now return
Result; note reads are paged and argument structs gain optional fields. Seedocs/note-todo.mdfor API migration and limits.
- Isolate worker session aliases by host-created root scope and caller. Expose execution lineage, work-turn completion hooks, bounded terminal retention, event cursors and cancellable any/all waits.
- Add attributed queue-only messages, bounded admission, atomic model/session permits, shared inference budgets and absolute runner deadlines.
- Add optional idle checkpoint storage and explicit neutral-history handoffs. Restore identity/history under current host policy without replaying in-flight tools or persisting provider raw history.
- Preserve nested worker artifacts and avoid duplicate final delivery after an idle turn result. Coalesce priority controls, protect reused child aliases from stale callbacks, and release initialization reservations on cancellation.
- Hosts continuing one root task across entry contexts must reinstall its
SubAgentScope; session aliases no longer implicitly join another root task.
- Add
skills_listandskills_read(registered bySkillManager::tools) for paginated discovery and package-scoped text resources next to the name-onlyskills_manager. Catalogs are immutable generations with stable path-based IDs, structured diagnostics, explicitinvalidate/reload, an optional explicit-only policy, presentation metadata and dependency preflight. - Bound discovery depth, entries, skill count, file and response sizes; reject
symlinked, multiply-linked and oversized skill files and skip hidden
descendant directories. Duplicate names within one root are readable by ID
only, and names over 58 characters use stable
skillh_*callables.
- Add registration-bound catalog publication, stable collision mappings, bounded pagination/transport input, per-server deadlines/concurrency/startup policy, server status inspection, and retained MCP tool metadata.
- Add opt-in standard elicitation callbacks and explicit resource APIs; forward credential refresh transaction guards to rmcp.
- Add
ToolOutput.model_outputwith provider-neutral presentations, bounded MCP text/media, private metadata separation and native image tool results where supported. Bound discovery outputs and accumulated schemas without weakening schema constraints.
- Stdio now forwards platform essentials and explicit environment overrides by
default. Set
inherit_env = trueonly when full inheritance is intended. - Calls default to serial execution; hosts can opt into parallel/read-only parallel operation. Static and OAuth HTTP clients reject redirects and limit response bodies. Logical-call deadlines include all continuation rounds.
- Use
McpServerConfigconstructors and defaults for new policy fields;ToolOutputliterals now include optionalmodel_output. See MCP_INTEGRATION.md.
- Opt-in note indexes are request-only context: the runner replays them after a
live model switch but keeps them out of the returned
chat_history, so persisted or resumed conversations no longer accumulate stale copies. An index loads only when the request offers the localnotetool, so nested and internal completions that inheritNoteContextConfigskip the note store. - Note writes validate trimmed IDs, matching
idslookups, and note results omitoffset_charsandtruncatedwhen they are zero or false. - Subagent sessions no longer cancel when a background tool or nested worker
result exceeds the caller input byte limit, and a nested turn result reaches
the parent once instead of being repeated by the turn-completion notice. Model
requests beyond
max_parallel_requestswait for a slot instead of failing the root or worker turn. - Tools returned by
tools_selectstay callable in allowlisted runners after the accumulated schema-merge budget is full. An excluded or app-only MCP tool over the schema/description budget no longer fails its server; MCP HTTP clients reuse pooled connections, and results build their model presentation once with the server's limits. skills_manageragain lists the configured skill directories and the default skill creation directory in its description.- Omit blank text blocks from Anthropic image-only tool results, and omit content entirely for empty presentations. Gemini text fallbacks now affect only wire requests, preserving tool media in neutral history for replay across models.
- Align Completion budget, reasoning effort, strict schemas, media inputs, and
Anthropic Bearer version headers with the provider APIs. Gemini thinking
budgets now accept
-1for dynamic thinking. - Reject incomplete streams before exposing tool calls, preserve Gemini signed Parts, distinguish safety feedback from blocking, and normalize omitted Gemini function arguments to empty objects.
- Pair missing tool-call IDs when replaying foreign histories to OpenAI and Anthropic. Document Responses' mandatory streaming and reject unsupported stop sequences explicitly.
- Anthropic keeps a tool strict only when its schema fits the strict subset;
tools with other constraints, such as
minimumon unsigned integers or the default agentminLength, are sent as regular tools. Results answering calls kept only in native history are sent unchanged, Chat Completions keepsvideo_urlparts for compatible providers, and in-band stream errors typedserver_errorare retried. - Session
shellcalls now wait for the process to exit oryield_time_msto elapse instead of returning at the first output. Background hooks fire only for commands still running after that wait, so foreground results are no longer re-reported as background completions. The docs note thatShellToolHookdoes not gateShellCommandToolorShellSessionToolarguments. shell_sessionlog reads no longer split multibyte characters, and the raw log labels only switches between stdout and stderr. Legacy raw-output files are flushed before their path is returned.apply_patchdeletes non-text files, treats blank lines in update chunks as empty context, and tolerates whitespace around the patch envelope.- The Linux sandbox binds
/etc/hostsand/etc/resolv.confand keeps the PTY as the controlling terminal of tty sessions.edit_filereports a missing file only when the file is actually absent. - Workspace file tools,
apply_patch,skills_readandatomic_write_fileagain reach files below a directory the process may traverse but not list, such as Android's/dataor a0711home directory. The no-follow walk now opens ancestors with search-only descriptors (O_PATHon Linux and Android,O_SEARCHon Apple platforms) and still refuses symlinked components. - MCP model presentations show structured content once: a text block that only
repeats
structuredContentas serialized JSON, which the MCP spec asks servers to include for older clients, is left out of the model view. Other text blocks and the auditedoutputenvelope are unchanged.
This release moves the AndaDB stack to 0.14, upgrades the cloud and TEE
dependencies, and ships the review fixes staged since 0.16.0.
-
Workspace release alignment β
anda_core,anda_engine,anda_engine_server,anda_web3_client, andanda_clinow share version0.16.1. -
Published dependency upgrades β
anda_db,anda_db_schema,anda_db_tfs,anda_kip, andanda_cognitive_nexusmove from 0.13 to 0.14,anda_cloud_cdkfrom 0.6 to 0.7, andic_tee_cdkandic_tee_gateway_sdkfrom 0.7 to 0.8.ic_auth_typesnow requires 0.10.5, whereByteArrayB64isCopy. -
Hosts upgrade these crates in step β
anda_core::Resource,anda_engine::context::AgentInfo, andanda_web3_client::tee'sTeeGatewayClient/TeeGatewayClientBuilderare re-exported from the upgraded crates, and the memory APIs take AndaDB 0.14 handles. AndaDB 0.14 does not migrate Spaces activated under thecognitive-memory2.1.0 draft; follow its upgrading notes before opening existing memory stores.
- Reject engine registry keys that differ from the engine's principal.
- Honor discovery
Acceptweights, wildcards, and exclusions; return406when no representation matches. Mark caller-bearing responsesno-storeand retainVary: Acceptalongside compression's content-encoding variation. - Accept case-insensitive Bearer/ICP authentication schemes and multiple spaces before tokens, while preserving signature and CWT validity checks.
- Release encoded RPC parameters before agent/tool execution and borrow server discovery metadata during serialization. Use owned middleware registration and shared response encoding; clarify authentication and middleware-order contracts.
- Report RPC execution and encoding errors with their
Displaytext instead ofDebugoutput. On Windows, the listener no longer setsSO_REUSEADDR, which would let another socket bind the same port.
- Drop the
ic_tee_agentdependency and theic_auth_verifierfullfeature (verification needs onlyenvelope), removingic-agentand a secondic_cosestack from the server build.handler::anda_enginenow takes the localRPCRequestBodyextractor instead ofContentWithSHA3<RPCRequest>; content-type selection and415/400/413rejections are unchanged.
- Isolate subagent sessions and model-visible status by caller; keep background progress IDs consistent with start/end IDs. Stop/cancel interrupts pending session work, and native shell cancellation also cleans up descendants and readers.
- Scope unfiltered object listings to their namespace and serialize note updates per document to prevent lost concurrent writes.
- Preserve complete discovery schemas when request merging is capped, upgrade directory entries on selection, honor selector limits, and retain selector usage. Resolve exact routed names, call dynamically registered remote engines, and validate lazy provider dependencies after initialization.
- Keep queued stream input at normal completion, retain runner state when compaction fails, and run remaining end hooks after a hook reports an error.
- Keep Anthropic server tools out of local dispatch, reject Responses stream errors and missing terminal responses, and use Gemini JSON Schema fields for provider-neutral schemas.
- Bound MCP setup, listing, tool requests, task polling, and cancellation acknowledgements; preserve discovery and legacy lifecycle fallback.
- Omit conversation bodies and credential-bearing headers from routine model and MCP configuration logs; fix and run the public-engine quick-start example.
- Apply
ModelConfig::max_output: explicit request output budgets are capped at it, and the Anthropic and Gemini adapters send it instead of their fixed defaults (64000 / 65535), so models with lower limits are not rejected. Other providers still omit the budget when a request does not set one. - Memory resource tools reject URIs that resolve to non-public addresses before fetching, matching the fetch tool's SSRF guard.
Engine::tool_callchecks caller visibility before reporting whether a tool exists, matchingagent_run.- Idle subagent sessions prune raw history once per turn instead of on every idle tick. Model error bodies are read and reported up to 8 KiB.
- The memory tool description no longer advertises KIP command log listing.
- Keep MCP tool names deterministic during concurrent startup discovery by applying fetched server snapshots in server-id order. Unreachable servers retain the existing tolerant-startup and explicit-refresh error behavior.
- Gemini
FunctionDeclarationaddsparameters_json_schema; callers constructing struct literals must initialize it. The existingparametersfield remains available for OpenAPI schemas. ResponsesStreamEventadds anErrorvariant. - Caller-free
SubSessions::get_sessionreturnsNonefor ambiguous IDs; use caller-scoped lookup/status methods for user-facing access. Progress callback IDs now match the existingagent:sessionstart/end ID format. - Search candidates borrow schemas until selected; capability groups obtain static names without regenerating schemas. Synchronous glob traversal runs outside Tokio workers. Reuse context construction and conversation expiry logic.
CompletionRunner::last_output()no longer carrieschat_history; usechat_history(). Handoff clones the request once and moves discovery and allowed-callable state into the new runner.- Add
Client::new_with_clientfor the Anthropic, OpenAI, and Gemini adapters andCompletionModel::with_max_outputfor Anthropic and Gemini;ModelConfigno longer builds a throwaway HTTP client. - Refresh MCP servers concurrently. Load dynamic remote engines once per
definition listing and deduplicate remote tool and agent names
case-insensitively; consolidate
RemoteEnginesresolution helpers. - Cache parsed
SKILL.mdfiles by size and modification time so name scans skip unchanged files while keeping directory priority and ambiguity checks. - Drop
async_traitfromManagementand avoid redundant clones of subagent resources, resource metadata, conversation documents, and context state.
- Cache/store helpers use the same canonical lowercase path for both layers,
and initialization propagates read failures other than typed
NotFounderrors. Document that storage CAS does not make cache fills or deletions linearizable. - Agent and tool registries forward custom resource selection through dynamic
dispatch. Empty registries no longer require a
Defaultcontext. - Explicit binary media MIME types retain their file representation even for UTF-8 bytes. URI-only and undecodable text attachments remain available after prompt document extraction.
- Correct the generic tool example and compile the README example as a doctest.
canister_rpcerrors report the endpoint as given andpathas{canister}/{method}for encoding, transport, response, and decoding failures; transport errors previously omitted the method.PromptCommandlowercases command names with ASCII rules, consistent withcommand_argument.
- Collect provider function metadata from one definition snapshot per provider; preserve name ordering and duplicate-provider precedence.
- Serialize prompt documents without a JSON value intermediate, avoid copying documents that need no delimiter escaping, and deserialize request metadata without cloning the source JSON value.
- Consolidate cache write branches and replace mock-only tests with regression coverage for the public contracts and scoped engine cache/store integration.
CacheStoreFeaturesdefault methods returnSendfutures withoutasync_trait, and theasync-traitdependency is removed.cache_store_setno longer requiresT: DeserializeOwned.Document::from_textacceptsimpl Into<String>.- Decode message
contentfrom CBOR or JSON without buffering an untagged copy, move resource blobs into content parts, decode resource text before building document metadata, and share one definition snapshot between providerdefinitionsandfunctions.
TeeClientsigned HTTP and RPC calls reject malformed URLs, non-HTTP(S) schemes, and embedded userinfo before signing, matching the generic client.tee_attestationchecks for TEE information before requesting attestation.- Share envelope signing and derivation-path construction in the generic client.
This release moves persistent memory to KIP 2.0, adds caller-isolated memory
spaces, and aligns every workspace crate on version 0.16.0.
-
Workspace release alignment β
anda_core,anda_engine,anda_engine_server,anda_web3_client, andanda_clinow share version0.16.0; internal crate requirements have been raised to0.16. -
Published dependency upgrades β
anda_db,anda_db_schema, andanda_db_tfsmove from 0.11 to 0.13,ic_coseandic_cose_typesfrom 0.10 to 0.11, andanda_cloud_cdkfrom 0.5 to 0.6. The temporary sibling-checkout patches for the KIP 2.0 stack are removed now that the 0.13 crates are published.
-
KIP 1.0 β KIP 2.0 β Upgraded
anda_kipandanda_cognitive_nexusfrom 0.11 to 0.13, the release that rewrites both crates for KIP 2.0. -
The KIP tools take a model-facing argument type β
MemoryManagementandMemoryReadonlypreviously took the wireRequestas theirTool::Args, which made the model write the protocol tag, nestdry_rununderoptions, and respectdeny_unknown_fields. They now take the newKipArgs: a singlecommandor anoperationsbatch whose items may be bare command strings, withdry_runat the top level.KipArgs::into_requestbuilds the envelope and is where thecommand/operationsexclusion is rejected, since an envelope built from both would silently run only one of them.KipOperationis the string-or-object entry of that batch. Hosts that construct these tool calls themselves must migrate fromRequesttoKipArgs; unknown fields are rejected instead of being silently ignored. -
Tool definitions come from
anda_kipβFUNCTION_DEFINITIONis now deserialized fromanda_kip::KIP_FUNCTION_DEFINITIONinstead of being spelled out here, so the schema a model sees and the envelope the engine executes stay in step across protocol revisions. The read-only tool gains its ownREADONLY_FUNCTION_DEFINITION(fromKIP_READONLY_FUNCTION_DEFINITION) rather than reusing the writable schema under a different name: KIP 2.0's read-only entry point offers no write vocabulary and noexecutionmodes, so the shared schema had been advertising a batch mode the read path has no use for.MemoryReadonly::with_kip_function_definitionsoverrides it, matching the writable tool. -
The read-only gate moved into the protocol crate β the batch read-only check is now
anda_kip::execute_request_readonly, which 0.13 added. The hand-rolled equivalent here parsed every operation, then letexecute_requestparse them all again, and failed the whole request when any one of them did not parse. The upstream gate parses once and reports an unparseable operation as its own result, so a caller can still correlate it byop_id. Admission is decided on what each command parses to, never on thelanguagelabel an operation declares, so no envelope field can talk a write past the boundary. -
Read-only batch defaults β
KipArgs::into_readonly_requestsuppliesindependentmode for a multi-read tool call, because the read-only tool schema does not expose execution settings. Writable calls and raw KIP wire requests still require an explicit batch mode; explicit modes are preserved. -
MemoryManagement::describe_systemβdescribe_selfβ renamed for what it reads: the agent's ownSelfModelConcept, not the host system.
-
Multi-tenant persistent memory (
Tenancy) βMemoryManagement::with_tenancychooses how a caller is bound to an identity and a MemorySpace. The default,Tenancy::Shared, is what every existing engine already did: one brain, every call authorized as the engine's own system Principal.Tenancy::PerCallerSpaceis for a host serving more than one caller β each verified caller runs as its own Governance Principal, in a MemorySpace it owns, created on its first KIP call.Isolation rests on ownership rather than on Grants the host has to write and keep correct: a Space's owner is authorized in it and nobody is authorized in a Space they do not own, so a caller reaches its own memory and no one else's with no policy to get wrong. The Space is written by the engine onto every request, over whatever the envelope held β
KipArgsexposes no Space field and the shipped tool schemas advertise none, and overwriting keeps that true if either ever gains one.A new tenant Space inherits the Schema Environment in force in the default Space. A fresh Space otherwise resolves Core alone, and Core declares no Concept types, so a host that installed its own vocabulary would find the same command working for the owner and failing for every tenant.
An anonymous caller gets no Space of its own.
StateFeatures::callerreturns the anonymous Principal precisely when nothing was verified, and one shared Space for all of them would pool strangers' memories; it runs as the Nexus's anonymous Principal on the default Space, where default deny gives it nothing until a Space policy says otherwise. -
MemoryManagement::query_as/execute_as/session_forβ The caller-scoped counterparts ofquery/execute, for a host running commands on a tenant's behalf.describe_callerandget_or_init_callernow go through them, so a caller's own Person Concept lands in that caller's Space.session_forbinds a supplied request to the caller's Space and returns the provisionedSessionfor executing it. -
MemoryManagement::query/executeβ Run one KIP command and get its result value back, rather than an envelope to unpack.querygoes through the read-only gate,executethrough the state-capable one. Both are the host acting on the default Space under every tenancy.
-
SkillManager::set_skill_filterβ A host can now install aSkillFilterpredicate deciding which skills on disk the manager may hold. Rejecting a skill drops it everywhere at once: not loaded, not callable, absent from the resident catalog in the tool description, and reported as not found by the reader tool β so an application-level enable/disable switch cannot leave a hidden skill reachable by name. The predicate runs duringloadbefore duplicate resolution, so rejecting the copy in a higher-priority directory promotes the next directory's copy instead of dropping the name. Installing a filter also prunes what it rejects immediately, so the registry is never inconsistent with the policy while waiting for a reload. This exists so an embedding application does not have to keep a second skill registry of its own, which would inevitably disagree with this one about what is dispatchable. -
openai-responseprovider family βModelConfiggains a newfamilythat always routes through the OpenAI Responses API (completion_model_v2) with streaming and effort support, where the existingopenaifamily decides by model name (gpt*β Responses API, otherwise Chat Completions). A config that must pin the Responses API no longer depends on agpt-prefixed model name.
- Reading a shadowed skill by name no longer fails as ambiguous β
skills_managerresolved a name present in several configured skill directories by refusing it, whileloadhad already resolved the same collision by directory priority. A personal skill shadowing a bundled one was therefore loaded and callable but unreadable, which under the inline execution default means unusable. The read path now applies the same priority rule; an ambiguity within one directory still errors, since there is no priority there to break the tie.
This release folds in the entries previously staged as 0.14.6, which was
never published, and adds a deep-module restructuring of anda_engine and
anda_core guided by a full design review (kept as an internal working
document, not part of the repository). Behavior is preserved across the
restructuring except where noted below. Every workspace crate moves to
0.15.0 because the restructuring changes public API in anda_core and
anda_engine.
- MCP 2026-07-28 β The MCP host now speaks the stateless revision alongside the older
initialize-based ones.McpServerConfig::lifecyclepicks how a server is approached:auto(default) probesserver/discoverand falls back to the legacy handshake β including on a fresh transport, since a pre-2026 server often drops the connection rather than answering "method not found" β whilediscoverandinitializepin one lifecycle.2026-07-28is only negotiated through discovery, so a server that merely echoes a proposed version cannot pull the host onto a revision it does not implement. Discovery metadata (title, description,instructions) feeds the same tool groups the handshake used to. - Subscription-based tool updates β SEP-2575 removed unsolicited server pushes, so for
2026-07-28peers that advertisetools.listChangedthe provider opens asubscriptions/listenstream per session and drains it in the background. Streams are not resumable: an ended stream is reopened while the transport is up, and the session is marked dirty across the gap so a change announced while nothing was listening still triggers a re-list. - MRTR and tasks handling β A
tools/callno longer always returns a result. Aninput_requiredround (SEP-2322) carrying onlyrequestStateis echoed back and the call continues; one that actually asks for sampling, elicitation, or roots β none of which this host advertises β comes back as a failed tool result, so the model can pick another path instead of losing the turn. The SEP-2663 tasks extension is opt-in per server throughMcpServerConfig::tasks: the provider then pollstasks/getat the server's suggested interval (clamped to 250 msβ10 s) up tomax_wait_secs, and cancels any task it walks away from. - MCP re-authorization β
complete_authorizationnow drops the server's live session after persisting the new grant, so re-running the OAuth flow (e.g. for updated scopes) takes effect on the next call instead of waiting for the old session to die. Newdisconnect_serverdrops a session while keeping the server and its routes β holding the per-server connect lock, so a reconnect already in flight cannot reinstate the retired credentials; newclear_credentialsalso deletes the persisted grant, forcingMcpAuthorizationRequiredand a fresh consent. A grant the authorization server has revoked now also surfaces asMcpAuthorizationRequiredinstead of an opaque transport error, so applications learn to re-run the flow. The headless/SSH flow (present the URL as text, paste the redirect URL back) is now documented inMCP_INTEGRATION.md. - Skill execution modes β Skills now run inline by default: the
skills_managertool returns the full SKILL.md and the calling agent follows it in its own context, keeping the conversation, the user, and the turn's resources in reach. A skill can opt into isolated execution withexecution: subagent(ormetadata.execution: subagent) in its frontmatter, exposing it as anSA_<agent_name>worker for long-running, parallelisable, or context-hungry procedures. The tool response now reportsexecution, acallablename for subagent skills, andbase_dirfor resolving bundled files. Skills that previously ran as subagents must declare the execution mode to keep that behavior. resource-tagsfor skill subagents β New frontmatter field narrows which offered resources a delegated skill receives; when absent it accepts every offered resource so the current turn's attachments reach it.model::testing::ScriptedCompleterβ A programmable completion double (queued replies, closures, error injection, request recording, echo fallback) available to downstream crates, replacing the need to hand-roll a fake provider per test.subagent::ConversationRecordsβ The two-method persistence port the subagent conversation recorder actually needs (create/update).memory::Conversationsis its AndaDB adapter; AndaDB field encoding no longer crosses into the subagent layer, andSubAgentConversationRecorder::with_storeaccepts custom implementations, so subagent persistence is testable without AndaDB.context::DiscoveredToolsβ Discovery-tool policy (observation, merge probing, output compaction) extracted from the completion runner into the module that owns the discovery vocabulary.BaseCtx::path()β Public accessor for the context namespace path (a_<agent>for an agent call,t_<tool>for a tool call). Tool hooks are keyed by argument/output types, so two tools with identicalArgs/Outputshare one hook slot; this is how a hook identifies which one invoked it.
- Registries own their invariant β The
setmap onToolSet,AgentSet, andToolProviderSetis now private, so the lowercase-key invariant can no longer be bypassed by direct mutation. The replacement interface:add_dyn(validated insert of a type-erased entry),iter()((lowercase_name, entry)pairs in name order), and ownedIntoIteratoryielding entries in name order.EngineBuilder::register_tools/register_tool_providers/register_agentsnow merge throughadd_dyninstead of re-implementing the duplicate check. CacheFeatures::cache_raw_iterremoved β It leaked the runtime's internal cache entry representation ((Arc<String>, Arc<(Bytes, Option<CacheExpiry>)>)) into the capability trait and had no production consumers. Context implementations simply drop the method; the engine keeps an equivalent test-only iterator on its internalCacheService.- Dead text-decoding entry points removed β
text_fromandutf8_text_from(unused owned-Vecvariants) are gone, andutf8_text_from_bytesis now private (it equalstext_from_bytes_with_encoding(data, None)). Every entry point the engine actually calls is untouched:text_from_bytes_with_encoding,text_encoding_for_label,text_encoding_label,platform_text_encoding, andwindows_code_page_encoding.text_from_bytesis also retained β it has no in-tree caller, but it is the only ergonomic platform-default entry point (text_from_bytes_with_encoding(data, platform_text_encoding())) and is plausibly used downstream, so it was kept rather than widening the break.
modelsplit into cohesive submodules βmodel.rsnow hosts only the call-contract types (agent/tool inputs and outputs, usage, request metadata, function definitions). Chat content (Message,ContentPart, the CBOR-safe wire codecs, data-URL helpers) moved tomodel::content, prompt documents tomodel::document, and text decoding tomodel::text. All names are still re-exported from the crate root, so existing imports are unaffected.- Registry mechanics deduplicated β Group aggregation and name-filtered
definition/function selection are shared by
ToolSetandAgentSetthrough one internalregistrymodule instead of two hand-copied implementations. DocumentsimplementsIntoIteratorβ Consuming iteration over the contained documents;CompletionRequest::append_documentsnow uses it instead of reaching into private fields.- Shared test fixtures β The two duplicated ~300-line mock contexts in
agent.rsandtool.rstests are now oneMockContextin an internal test-support module, and the twin mock-coverage tests merged into one.
- Provider seam unified on
CompletionFeaturesDynβ The delegatinganda_core::CompletionFeaturesimpls on the Anthropic and GeminiCompletionModels are removed (they ignoredresourcesand merely forwarded; OpenAI's models never had them). Code that called the models through that trait should callCompletionFeaturesDyn::completioninstead. - Memory internals no longer public β The
Arc<Collection>fields onmemory::Conversationsandmemory::MemoryManagementare now private, so the storage abstraction can no longer be bypassed; use the methods, or the newConversationRecordsport (below).
- One completion driver for all providers β The four provider adapters
(Anthropic, Gemini, OpenAI Chat, OpenAI Responses) now share a single
drive_completionalgorithm behind an internalWireFormatseam; each adapter contributes only its wire mapping. The raw-history ordering and skip/drain invariants are enforced structurally instead of being hand-copied four times. Only observable difference: the Chat adapter's request debug log message is now"Completion request"like the other adapters. - Raw-history pruning moved behind the provider seam β
CompletionFeaturesDyngainsprune_unanswered_tool_calls/prune_tool_interactionswith conservative default implementations; the completion runner no longer hard-codes any provider's wire shapes. Custom providers can override both with their own typed knowledge. - Workspace sandbox is one implementation β The filesystem tools now
resolve every path through an internal
WorkspaceScope; the shell runtime's separate narrowing logic is gone and shell now honors the sameworkspace/workspacesrequest hints (string, path, or array forms) as the filesystem tools β a strict widening, still bounded by the configured root. The path-resolution helpers other modules could previously call directly are now private to the filesystem module. - Module layout β
context/agent.rs(6.7k lines) is split intocontext/agent.rs,context/runner.rs, and shared test fixtures;extension/mcp.rs(3.6k lines) intomcp/auth.rs(OAuth protocol),mcp/session.rs(transports and lifecycle), andmcp/router.rs(name mapping and call rounds); the background-task registry moves fromhook.rsto a newbackgroundmodule. All previous public paths keep working through re-exports. - Single engine assembly path β
EngineBuilder::build,::empty, and::mock_ctxshare one assembly routine, removing three hand-synced copies.mock_ctxis now documented as the supported way for downstream agent and tool authors to obtain anAgentCtxin their own tests. MockImplemented::model_name()now returns"mock_implemented"instead of"not_implemented", so mocks and the null model are distinguishable (the runner compares model names to detect live model switches).- One tool-call protocol for the built-in extension tools β The nine
extension tools (fetch, the four filesystem tools, todo, note, shell,
skills manager) and the six memory tools now share
extension::tool_definition(parameter schema derived from the typed argument struct,strict: Some(true)) andextension::hooked_call(cancellation gate plusDynToolHookbefore/after wiring), replacing ten hand-writtenjson!schemas and seven copies of the hook boilerplate. The two memory schemas that stay hand-built have hard reasons recorded in comments (KIP definitions come from outside;memory_apiflattens an internally tagged enum thatschemarswould render asanyOf). Observable differences:- Every tool now rejects a call whose context is already cancelled
(previously only
search_fileand the shell background path honored the token); the fetch, skills-manager, and memory tools gain hook support (newFetchToolHook/SkillToolHookaliases; the two KIP tools share theDynToolHook<Request, Response>slot since hooks are keyed by argument/output types β a hook tells them apart through the newBaseCtx::path()). - The note tool's failed operations (missing items, size limit, unknown op)
and the shell tool's executor-failure and timeout outcomes still resolve
to
Okwith the same typed output, but are now flagged withis_error: Some(true)so hooks, providers, and telemetry see the failure signal. A shell command that runs and exits non-zero remains regular output. - Derived schemas carry additional metadata the hand-written ones lacked
(
defaultvalues from serde defaults,minimum: 0on unsigned integers, descriptions on nested item objects); property shapes, enums, and required lists are unchanged.
- Every tool now rejects a call whose context is already cancelled
(previously only
allowed-toolsis now an upper bound β A skill that declaresallowed-toolsis granted exactly those tools as a subagent; only skills that declare nothing inherit the manager's default tool set. Previously the configured defaults were merged into every skill, which could escalate a restriction written in a third-party SKILL.md.
- Rustdoc builds clean again β Intra-doc links in
extension::mcp::authand theextensionmodule docs were left unresolved by the MCP/hook restructuring and failedRUSTDOCFLAGS="-D warnings" cargo doc; they now resolve. - The OpenAI Chat adapter no longer discards the assistant message on a
failure verdict β A non-success
finish_reason(length,content_filter, β¦) previously dropped the whole turn: the truncated text, reasoning, and tool calls vanished and onlyfailed_reasonsurvived. The Chat adapter now matches the Anthropic and Gemini adapters: the message is preserved in bothraw_historyandchat_history, while the extractedcontent/thoughts/tool_callsremain gated on success. - Interrupted tool calls no longer strand an OpenAI Responses
reasoningitem β Dropping unanswered tool-call requests (after steering, discard, or stop) now takes the reasoning item that must immediately precede a pruned call out with it, the way completed-interaction pruning already did. Previously an interrupt mid-tool-call on the Responses adapter left an orphaned reasoning item inraw_history, and every subsequent request was rejected for a reasoning item without its required following item β wedging the conversation until the process restarted. - Live model switch replay β
raw_historybelongs to the model that produced it: replaying one provider's native message JSON (OpenAIinput_textparts, Anthropic content blocks, Gemini parts) through another provider makes the request unparseable and the provider rejects the whole call, wedging the conversation until restart. Each turn now re-resolves the routed model; when it changes, the engine dropsraw_historyand replays the provider-neutralchat_history.
- Explicit remote HTTP opt-in β New global
--allow-httpflag is required before the CLI sends signed requests to a non-loopbackhttp://endpoint. Local loopback endpoints continue to work without configuration.
Changed β anda_core v0.14.5, anda_engine v0.14.5, anda_engine_server v0.14.5, anda_web3_client v0.14.5, anda_cli v0.14.5
- AndaDB 0.10β0.11 β Upgraded
anda_db,anda_db_tfs,anda_cognitive_nexus,anda_db_schema, andanda_kipto 0.11. Conversation pagination now uses the database's newest-first ID query while preserving stable newest-first results across pages.
- rmcp 2.2β3.0 β Updated the MCP client integration for rmcp 3.0, including its peer metadata and OAuth metadata APIs.
- MCP server discovery β Tool-provider groups now retain an MCP server's handshake title, description, and instructions so consumers can present a coherent server capability bundle instead of only a flat tool list.
- On-demand cache namespaces β Agent, tool, runtime-discovered MCP, and subagent cache namespaces are now created lazily while retaining per-namespace isolation and configured capacity limits.
- Workspace-bound filesystem and shell tools β Caller-controlled
workspace/workspacesmetadata can now only narrow to a resolved subdirectory of a configured workspace; it can no longer redirect file or shell operations outside the configured roots. File search additionally caps scanned entries and observes cancellation, andedit_filerejects a replacement result exceeding the file-size limit before allocation. - Conversation resource authorization β
get_resource_contentnow requires the owning conversation and verifies both conversation ownership and resource membership before returning a resource, closing global resource-ID disclosure. Conversation persistence also preserves concurrently queued steering/follow-up messages and clears stale failure reasons after a successful update. - Model-adapter edge cases β OpenAI-compatible completions preserve messages when
finish_reasonis omitted and treat empty tool arguments as{}. Anthropic requests derive matching non-empty tool IDs when an upstream provider omitted one, omit persisted reasoning blocks lacking a valid signature, and bound provider-supplied streaming content-block indexes to prevent oversized allocations. - Callable routing and cancellation β Allowlisted subagents and remote callables now match their advertised routing prefixes; duplicate callable names across local and remote sources are suppressed; cancellation closes visible in-flight tool calls with interruption outputs so persisted histories remain replayable.
- Hook cleanup β When a later agent/tool start hook rejects, all previously started hooks are unwound even if an intermediate end hook fails, preventing stateful hooks from stranding leases.
- Skills and background tasks β Loaded skills retain stable subagent session registries across lookups and reloads; directly reading a skill refreshes its materialized subagent. Background task IDs are namespaced by subagent, preventing same-named sessions from colliding.
- MCP resilience and routing β Tool-list change notifications cannot be lost during a refresh; failed lists retry instead of accepting a stale route table; local-name collisions are safely disambiguated; peer-closed transports reconnect; client-credentials sessions reconnect before expiry; and stdio transport environment values are redacted from
Debugoutput. - Remote engine initialization β
EngineBuilder::mock_ctxnow registers configured remote engines just asbuilddoes, so remote tools and agents are available in mock contexts.
- Credential handling fails closed β Requests carrying malformed authorization headers are rejected instead of silently becoming anonymous, and configured CWT bearer tokens must carry an expiration claim.
- Signed endpoint URL guard β Rejects embedded URL userinfo such as
https://trusted.example@attacker.example/, which could otherwise send a signed request to a misleading host.
- Anthropic structured output β
output_schemawas silently dropped for Anthropic models; now mapped tooutput_config.formatwithjson_schematype, so structured output requests work across all three model families.
- SSE deserialization zero-copy β Wire enums in Anthropic, OpenAI, and Gemini type layers now deserialize by reference (
&str) instead of cloning the bufferedValue, removing per-event deep copies on SSE streaming hot paths. - Reduced per-request cloning β The full conversation is no longer cloned per request; request-log clones now live inside the log branch, raw history is built before converting content blocks, and v2
output/parsed_outputare filled in a single pass. - Shared model helpers β
null_default,resolve_endpoint, andstring_enum_serde!(now with input aliases) hoisted tomodel.rs; applied to five hand-written open string enums. - Consolidated test scaffolding β Triplicated HTTP mock scaffolding merged into
model/test_support.rs. - Deduplicated model infrastructure β Model constructors,
Models::clone/replace, error-chain walkers, and the OpenAI media content-part mapping are now de-duplicated.
Changed β anda_core v0.14.3, anda_engine v0.14.3, anda_engine_server v0.14.3, anda_web3_client v0.14.3
- anda_db 0.9β0.10 β Upgraded
anda_db,anda_db_tfs,anda_cognitive_nexus,anda_db_schema, andanda_kipto 0.10. No API changes required in this workspace.
- MCP OAuth 2.1 authorization β
McpOAuthConfigsupports two flows for Streamable HTTP MCP servers: interactive Authorization Code with PKCE (begin_authorization/complete_authorization/cancel_authorization) and headless Client Credentials (SEP-1046). The library drives the protocol but the consuming application owns the browser, redirect callback, and credential store. - Pluggable credential persistence β
McpCredentialStoretrait withload/save/clearlets applications back OAuth tokens with an encrypted store.InMemoryMcpCredentialStoreis the default for development. McpAuthorizationRequirederror β Typed error returned when a session needs interactive auth; consumers downcast to trigger the authorization flow.discover_http_oauthβ Static method probes an HTTP MCP endpoint for OAuth capabilities (scopes, DCR support) without connecting.register_server/remove_serverβ Register an auth-requiring server without connecting, for deferred connection after the interactive OAuth flow completes.- Validation: no
bearer_token+authmixing βMcpStreamableHttpTransportrejects configs that set both.
- rmcp 1.7β2.2 β Upgraded with the
authfeature, replacing the static bearer-token path withAuthClient-based transport whenMcpOAuthConfigis present. Transport config split intobase_transport_config(for auth client injection) andtransport_config(static bearer, preserved for backward compat). list_rootsremoved β The deprecatedlist_rootsclient handler is removed to match rmcp 2.x.
- CBOR RPC response body cap β
MAX_RPC_RESPONSE_BYTES(16 MiB) enforced with streaming chunk-by-chunk guard; oversized responses are rejected before full buffering, protecting memory-constrained TEE runtimes. RemoteErrorerror variant β Split fromResultErrorso callers can distinguish transport-level decode failures from application-level remote errors.- Documents closing-tag injection guard β
Documents::Displayneutralizes literal</tag>delimiters inside untrusted attachment content (case-insensitive), preventing document content from closing the block early.
- Subagent execution-time tool allowlist β
CompletionRunner::set_allowed_callablesenforces the subagent's tool whitelist at dispatch time, not just in the definitions sent to the model. An empty allowlist rejects every call; discovered tools (from allowed discovery tools) are granted implicitly. - Completion response body cap β
MAX_COMPLETION_RESPONSE_BYTES(64 MiB) guards against a runaway or malicious provider streaming unbounded body. Enforced with streaming chunk-by-chunk guard;Content-Lengthis pre-checked before the first byte. - ModelConfig api_key redacted from Debug β Custom
Debugimpl replaces theapi_keyfield with[REDACTED]so a{:?}log line never leaks a credential. - Export name validation β
EngineBuilder::check_exportsrejects misspelled agent names (hard error) and warns on unresolved tool names before the engine starts. - Root-level cache namespace β
Path::default()is registered on every engine so root-contextcache_get_withcalls (e.g. dynamic remote-engine resolution) hit memory instead of always falling through to the store.
- RemoteTool / RemoteAgent cleanup β Removed the stale
engine: Principalfield; the target engine is resolved fromendpointat call time.RemoteAgentno longer lowercases a caller-provided name (the caller is expected to supply an already-valid lowercase name, consistent withRemoteTool). - Resource selection fixed for remote tools/agents β
select_tool_resources/select_agent_resourcesnow use the same longest-handle + exact-name resolution as endpoint routing, so resources always match the engine/tool the call is routed to even with overlapping handle prefixes. - Engine visibility checks hardened β
ctx_withandctx_with_basenow enforce anonymous/private/protected rules (previously onlyagent_runandtool_calldid).ctx_with_basealso validates the agent name before creating the context. - Anonymous principal excluded from management β
is_controllerandis_managerreject the anonymous principal even when the engine was built without a Web3 identity andcontrollerdefaults to anonymous. - Agent/tool end hooks always paired with start β
agent_runandtool_callnow invokeon_agent_end/on_tool_endon the failure path (with a placeholder output) so hooks that track leases (e.g.SingleThreadHook) release their accounting. - Challenge endorsement guard β
Engine::challengevalidates that the request'sAgentInfomatches the engine's own info (deterministic CBOR comparison) before signing, preventing registry entry hijacking via forged agent-info payloads. - File search always canonicalizes β Every path match is now canonicalized and re-checked against the workspace root, closing the symlink escape: a workspace-internal directory symlink pointing outside can no longer enumerate external filenames.
- ToolsSearch wildcard returns names only β The
*query now enumerates name + description (no parameter schema) and is capped at 64 results, keeping listing cheap and preventing context-window blowout. - Store list strips namespace prefix β
store_liststrips the context namespace fromprefix/offsetso a returnedObjectMeta.locationcan be fed back as paginationoffseton a non-root context without a doubled namespace. - Cache
get_withpreserves error source chain βCacheInitErrorwraps the initializer error instead of flattening it to a string, so downstream code can downcast for retryable/status signals. - Models::from_configs β Disabled models are skipped with
info!; misconfigured models that fail to build are skipped withwarn!instead of being swallowed silently. - OpenAI Chat Completions β
response_format.json_schemanow wraps the schema in the required{name, schema, strict}envelope; streaming requests requeststream_options.include_usagefor billing tracking;CompletionResponsepopulates themodelfield. - Gemini β
tool_choice_requiredis honored viaFunctionCallingMode::Any;tool_use_prompt_token_countmoved fromoutput_tokenstoinput_tokens(it is an input-side count, distinct fromprompt_token_count). - Anthropic β Empty/whitespace-only partial JSON in a tool-use block finalization now preserves the existing
{}default instead of overwriting with an empty string, matching the official SDK'sJSON.parse(buf || \"{}\")guard.
- Context compaction recovery β A transport failure during the summarization turn now restores the runner's tools, discovered tools, queued input, and unbound flag; a retry finds a usable runner instead of a permanently tool-less one.
- Subagent allowlist survives compaction β
handoffnow carriesallowed_callablesinto the replacement runner. Without this the subagent tool whitelist was silently dropped on the first context compaction, letting the subagent call any callable in the engine afterwards. - Unanswered tool calls flushed in discard β
discard_in_flight_requestnow unconditionally closes unanswered tool calls in the visible history, fixing the case where the tool round executed but the follow-up model call failed (pending calls drained, but visible history still held aToolCallwith no result β unreplayable by providers). - MCP cross-server local-name collision β When two MCP servers produce the same local tool name, the newcomer is disambiguated with a stable hash suffix. If the disambiguated name still collides, the tool is dropped with an error log instead of silently hijacking another server's route.
- File search symlink escape β Previously a workspace-internal directory symlink pointing outside let a plain pattern enumerate external filenames. All matches are now canonicalized against the workspace root.
- Memory expiry no longer deletes shared resources β
delete_expired_conversationsnow leaves resources intact because they are content-deduplicated and may be shared by other active conversations; reclaiming orphans requires a dedicated reference-counted GC pass. - Gemini token miscount β
tool_use_prompt_token_countwas incorrectly added tooutput_tokensinstead ofinput_tokens, inflating the output count and undercounting input.
- Serde buffering replaces
serde_json::ValueforContentPartβ Deserialization now uses serde's untagged/type-tagged machinery directly, preserving CBOR byte strings (InlineData.data,Principal,Action.signature) across RPC round-trips that previously lost them through the JSON intermediate. - Path encoding hardened β
path_lowercaseandpath_joinno longer double-encode%in already-encoded object-store keys. Re-joining a namespace with astore_listkey is idempotent. - Definitions/functions deduplicated by lowercase name β
AgentSetandToolSetsuppress duplicate schemas when the same tool/agent is requested multiple times (some model providers reject repeated definition names). /pingwith arguments accepted β/ping nowresolves toPinginstead of being treated as an unknown command; bare/and/ argare plain prompts rather than empty commands.- Blank
failed_reasontreated as success β An all-whitespace failure reason is normalized toNoneinAgentOutput::into_tool_output. - MCP secrets redacted from
Debugβ CustomDebugimpls onMcpStreamableHttpTransportandOAuthClientCredentialsConfigreplace bearer tokens and client secrets with[REDACTED]. validate_function_name: characters β bytes β Name length is now checked in bytes rather than characters.- Dependency cleanup β Removed unused
futuresandserde_bytesfromanda_coreproduction dependencies (futureskept for dev-dependencies).
%double-encoding in object-store paths βFrom<String>re-encodes%to%25, sopath_lowercaseandpath_joinswitched toPath::parse/Path::from_iterto preserve already-encoded segments.cache_store_deleteordering β Store is now deleted before cache to prevent a concurrentcache_store_getfrom repopulating a ghost cache entry that survives the delete.- Inline data token estimation β Switched from
(len + 3) / 4tolen.div_ceil(3)to match base64 expansion ratio (~4/3 chars per byte, not 3/4).
- Signed envelope digest required on RPC β
verify_usernow rejects aSignedEnvelopethat omits its committeddigeston body-bound RPC paths, instead of falling back to the server-computed body hash. This is a fail-closed hygiene check (the client must explicitly commit to the body hash); it is not a standalone defense against a signing oracle sharing the key β the signature is still verified over the same hash, and genuine resistance requires domain separation in the signature scheme. ApiKeyMiddleware::exempt_prefixβ Prefix-based exemption for discovery subtrees with dynamic segments (e.g./.well-known/covers both/informationand/agents/{id}) that cannot be enumerated exactly withexempt_path.
- README rewritten β Accurately describes the server as a thin, stateless forwarder; session management, tool integrations, and access control belong to
anda_engine. - Removed unused
originβ Builder field andwith_originmethod removed (no consumer used it). - Removed unused public
verify_cwtβ Only the internalverify_cwt_tokenis needed. - Decode error hygiene β Param decode failures now use
Display(notDebug) so the client sees the parser's error message without the raw request bytes.
- URL smuggling guard β
check_urlnow parses withreqwest::Urlinstead of a string-prefix check, rejecting non-http(s) schemes (file,ftp,data,ws), bare strings, and URLs with no host. Previously afile:///etc/passwd-style target would pass the prefix guard and attempt a connection. - All-zero root secret warning β
ClientBuilder::buildlogs awarn!when the default all-zero secret is used; the derived identity and all sub-keys are public and predictable. - Identity load improvements β
load_identitydetects existing file paths before falling back to hex decoding, so a PEM file withSecp256k1Identityerrors now surfaces the real parse failure instead of being misinterpreted as a hex string.identity_from_pemno longer masks the Ed25519 parse error when a Secp256k1 parse fails.
- Root secret zeroized β
root_secretfield wrapped inZeroizing<[u8; 48]>; the long-lived copy is wiped from memory onDrop, reducing the exposure window. - Dependencies β Added
zeroizeto workspace dependencies;logandzeroizeare now optional dependencies gated behind theclientfeature. - Query-signature verification (clarified, not changed) β Documented that the default
Agentdoes not request node keys or verify query signatures β the behavior since 0.14.0, now spelled out in a code comment: a non-TEE client reads canister state through a trusted boundary node. Pass your ownAgentviawith_agentto enable verification. - README rewritten β Accurate feature descriptions (ICP canister calls, signed HTTP/CBOR-RPC, deterministic key derivation), feature-flag documentation, and security caveats (endpoints passed to signed calls must be trusted).
CanisterCallerfromBaseContextβ TheCanisterCallerbound is removed fromBaseContext. Runtimes requiring canister access must implement the trait separately on their context type. All built-in impls (AgentCtx,BaseCtx) and theMockCanisterCallertest helper are removed.- Canister methods from
Web3ClientFeaturesβcanister_query_rawandcanister_update_raware removed from the Web3 client trait surface. Runtimes needing raw canister access should use their own client directly. Web3SDKenum andWeb3Clientwrapper βWeb3SDKis now a plain struct wrappingArc<dyn Web3ClientFeatures>instead of aTee/Web3enum. All match-based dispatch is flattened to direct trait-object calls.
- Per-task background cancellation β
BackgroundHandle+BackgroundTaskControlshook primitives with per-task child tokens for shell commands. New/stop_task <task_id>subagent control command stops individual background tasks without disturbing sibling tasks or the session. tee_attestation()onWeb3ClientFeaturesβ New trait method (defaultOk(None)) so TEE-backed clients can attach attestation evidence. Engine'schallenge_responsenow uses a single unified path for both TEE and non-TEE flows.anda_web3_clientfeature flags βclient(non-TEE,ic-agent+ local key derivation),tee(TEE gateway),full(both). Default build pulls neitheric-agentnoric_tee_*crates.crypto+teemodules inanda_web3_clientβ Deterministic key derivation ported fromic_tee_gateway_sdk::crypto(byte-for-byte identical), andTeeClientadaptingic_tee_gateway_sdkto the engine'sWeb3ClientFeaturestrait.
- Dependency upgrades:
ic-agent0.47β0.48,ic_auth_types0.9β0.10,ic_auth_verifier0.9β0.10; new:ic-ed255190.6,ic-secp256k10.3. - Engine dependency diet β Removed
ic_cose,ic_tee_cdk,ic_tee_gateway_sdkfromanda_engine;ic_auth_verifierfeature reduced fromfulltoenvelope. rand_bytesself-implemented β Usesrand::filldirectly instead of re-exportingic_cose::rand_bytes, eliminating the lastic_cosedependency fromanda_engine.
- Base64 blob serialization β Test assertions updated to match
b64:prefix format for inline data and resource blob encoding.
- Background handle typed payload β
BackgroundHandlenow carries a typedArc<dyn Any + Send + Sync>payload viawith_data()/data()methods, replacing the parallelbackground_tasksmap inSubSessionwith data stored directly on the handle.created_atandelapsed_ms()provide lifecycle observability on the handle itself. BackgroundTaskControlsergonomics β InternalMutexupgraded toRwLock; newget()/get_data()/handles()/is_empty()accessors;finish()now returns the removed handle;finish_all()clears all tasks;stop_all()removed with stop logic inlined at call sites.
- Provider raw-history pruning β
prune_req_raw_history()reclaims context-window budget for long-lived subagent sessions by removing consumed tool calls and results from accumulated provider-native JSON history, operating directly on raw JSON to preserve provider-specific shapes (OpenAI Chat/Responses, Anthropic, Gemini) that a Message round-trip would lose. - SSRF protection for model-controlled HTTP fetches β
validate_public_url()blocks requests to loopback, private, link-local, metadata, and unspecified addresses before any outbound connection is made. - Shell process-group cleanup β Spawned shell children now belong to their own process group; cancellation kills the entire group so background descendants are not left behind.
- anda_db 0.8 β 0.9 β Bumped anda_db, anda_db_tfs, anda_cognitive_nexus, anda_db_schema, and anda_kip dependencies from 0.8 to 0.9, adopting the hardened JSON serialization, KQL pagination, and full-scan capping from anda-db 0.9.0.
- Auth failures now return 401 β Bad credentials (wrong signature, tampered body, expired token, wrong target) now return HTTP 401 instead of silently downgrading to anonymous access.
- Store size enforcement β Uploads exceeding
MAX_STORE_OBJECT_SIZEare now rejected at put time with a clear client error.
- Symlink escape via workspace β Filesystem reads now re-verify the canonicalized path resides inside the workspace after resolving symlinks, preventing reads to host files through workspace-local symlinks.
- Subagent conversation persistence β Engine builders can now install a subagent conversation recorder so blocking subagent calls and background sessions are persisted as conversations, expose their conversation IDs, and retain status, usage, artifacts, metadata, and failure information for operational audit.
- Subagent compaction with pending tool calls β Subagent session compaction now executes pending tool calls and records their tool outputs before summarizing, preventing compacted histories from stranding unanswered tool-call requirements.
- Interrupted tool-call history β Completion runners now append explicit error tool outputs when pending tool calls are discarded, stopped, or interrupted by steering, preventing follow-up requests from carrying dangling tool-call state.
- Provider raw-history cleanup β Raw provider histories now prune unanswered tool calls recursively across nested OpenAI Responses, shell, patch, MCP approval, and Gemini function-call shapes while preserving surrounding text and metadata context.
- OpenAI minimal reasoning compatibility β OpenAI model requests now map
ModelEffort::Minimaltolowreasoning effort instead of sending the unsupportedminimalvalue.
- Hyphenated function names β Agent, tool, and function-name validation now allows hyphens (
-) in addition to letters, digits, and underscores, with documentation and tests updated to match the accepted naming rules.
- Mutable completion chat history access β
CompletionRunner::chat_history_mut()exposes ordered mutable access to accumulated chat history messages so callers can update recorded context in place without appending replacement messages.
- MCP peer metadata compatibility β MCP tool discovery now handles the current
rmcppeer-info return shape when capturing server title, description, and instructions for capability groups.
- Child agent context namespaces β Child agent contexts now switch to the child agent namespace while preserving inherited extension state, so nested agent and tool contexts use the correct agent ownership metadata.
- Reusable completion handoffs β Completion runners can now summarize long conversations into compact continuation handoffs and restart from the resulting summary while preserving base tool configuration and accumulated usage metadata.
- Subagent compaction reuse β Subagent session compaction now delegates to the shared completion-runner handoff path, reducing duplicated context-reset logic while preserving session artifacts and tool usage.
- Model request retries β Completion model requests now retry up to three times with a longer retry delay cap, improving resilience to transient provider failures.
- Agent capability groups β Agents can now declare
ToolGroupInfometadata so related agent callables are discoverable as coherent bundles alongside tool and provider groups.
- Agent group discovery β Discovery helpers now include grouped agents in
tools_groups/tools_selectoutputs, preserving bundle instructions and sibling agent member lists when a grouped agent is selected.
- Subagent compaction scheduling β Removed the extra idle-loop compaction trigger so subagent sessions rely on the pending-input compaction path instead of compacting again at every idle boundary.
- Tool capability groups β Tools and dynamic providers can now expose
ToolGroupInfo/ToolGroupmetadata so related callables are discoverable as bundles without changing model-provider function schemas.
- Tool group discovery helpers β Added
tools_groupsplustools_select { group }expansion and group annotations intools_search/tools_selectoutputs, letting agents survey capability bundles first and then load all member schemas on demand. - Built-in filesystem and memory groups β Filesystem workspace tools and persistent-memory/conversation tools now advertise shared capability groups with usage guidance and complete member lists.
- MCP server capability groups β MCP tool providers now capture server title, description, and
instructionsfrom the initialize handshake and surface each server as one discovery-layer tool group. - Subagent live status polling β Added read-only
/statussession polling and manager-levelstatuscatalog output with elapsed time, idle time, token usage, turns, latest progress, and active background tasks. - Grapheme-safe output truncation β Shared truncation helpers now respect Unicode grapheme-cluster boundaries, so shell/status/filesystem inline previews do not split multi-codepoint emoji, flags, skin-tone modifiers, or combining marks.
- Tool group discovery normalization β
tools_groups/tools_selectnow filter stale or shadowed provider group members and merge duplicate group ids before returning discovery output, so group expansion matches the callable schemas actually visible to the current model turn. - Streaming completion request timeout override β Streaming model requests now set their own 10-minute total timeout at the request level, so downstream applications that inject a shared HTTP client with a shorter generic timeout do not abort long-but-progressing SSE completions before the model transport budget.
- Dynamic tool provider contract β Added
ToolProvider,ToolProviderSet, and borrowedBoxFutsupport so runtimes can expose tools discovered at runtime while keeping staticTool/ToolSetbehavior intact.
- Runtime-discovered tool providers β Engine builders can now register dynamic tool providers, merge provider-backed functions into tool discovery, route direct and agent-driven tool calls through providers, and initialize providers during engine build.
- MCP tools extension β Added
anda_engine::extension::mcp, a reusable MCP host/client provider backed byrmcpwith stdio and Streamable HTTP transports, tool allow/deny filters, legal Anda tool-name mapping, dirty refresh ontools/list_changed, audited tool outputs, and explicit exclusion of deprecated Roots/Sampling/Logging control capabilities.
- Streaming completion timeout handling β Model completion clients no longer use HTTP/2 keep-alive PINGs as the liveness detector for long SSE reasoning streams. Completion transport now relies on a per-read body idle timeout plus the existing total request timeout, preventing provider/CDN PING ACK delays from aborting streams that are still producing body chunks.
- Subagent compaction before oversized input batches β Idle subagent sessions now compact before attaching large batched follow-up or steering inputs, preventing background-result bursts from overflowing the context window before summarization can run. Compaction also refreshes session activity so small idle timeouts do not immediately reclaim freshly compacted sessions.
- Slash command argument parsing β Added
PromptCommand::command_argument()so command handlers can consistently extract the user-provided text after a slash command prefix.
- Subagent stop and cancel semantics β
/stop <reason>now stops the current session task while keeping the session idle and reusable, while/cancel <reason>continues to end the session runner. Stopped background task output is suppressed so stale child results are not forwarded after a stop. - Subagent compaction tool discovery retention β Session compaction now preserves whether discovered tool definitions should be merged into future completion requests, avoiding tool-discovery state loss after long session handoffs.
- Runtime model registry replacement β Added
Models::replaceto atomically replace a model registry from anotherModelsinstance, enabling callers to reload model configuration without preserving stale labels from the previous registry.
- Subagent session lifecycle controls β Subagent definitions and the manager tool schema now support an
idle_timeoutsetting for session mode, letting callers tune idle session reclamation while preserving the engine default when unset. - Subagent progress and compaction robustness β Session runners now emit visible progress signals before the next idle boundary, filter signalless tool-call noise, preserve usage/artifacts across context compaction, and fail loudly instead of replacing history with an empty compaction summary.
- Native shell environment injection β Restored the native shell runtime identity so safe host environment variables such as
PATHare forwarded correctly, while keeping background task IDs prefixed with the shell tool name. - Native shell finalization latency β stdout and stderr reader shutdown checks now run concurrently, avoiding doubled grace-period waits when descendant processes keep both pipes open.
- Workspace release and dependency alignment β Bumped the Anda workspace crates to
0.13.0, aligned internal crate dependencies on the0.13series, and upgraded the ICP/TEE/Anda data stack toanda_db_tfs 0.8,anda_db_schema 0.8,anda_cloud_cdk 0.5,ic_cose 0.10,ic_cose_types 0.10,ic-oss-types 1.3,ic_auth_types 0.9,ic_auth_verifier 0.9, andic_tee_* 0.7. - CBOR serialization backend β Replaced direct
ciboriumusage across the published workspace crates withcbor2, using canonical encoding for cache/store data, HTTP RPC payloads, signed Web3 requests, engine server RPC responses, notes, and subagent persistence.
- Transport error diagnostics β Completion transport errors now preserve their source chain and include upstream request IDs, received stream byte counts, and elapsed time for response-body and mid-stream SSE failures, making timeout and upstream-abort diagnosis more actionable.
- Bounded filesystem and shell tool output β File reads now cap inline text and binary previews with explicit truncation markers, glob searches tolerate unreadable or dangling entries while enforcing a scan cap, and native shell execution bounds captured output, terminal-progress rendering, inherited-pipe waits, and cancellation cleanup for background processes.
- Subagent session robustness β Session calls now validate structured arguments, atomically claim session IDs to avoid duplicate runners, report inactive control commands cleanly, preserve usage and artifacts across compaction, forward background usage deltas, and buffer stream steering while a completion step is in flight.
- Conversation and memory edge cases β Conversation batch reads, pagination, search limits, expired-deletion loops, timestamp serialization, resource existence checks, and resource ownership checks are now hardened to avoid default-limit truncation, cursor overlap, dangling references, and cross-conversation resource reads.
- Model transport and response handling β Completion retries now include a short backoff honoring capped
Retry-Afterhints, shared HTTP request timeout allows long reasoning calls, SSE[DONE]detection is line-anchored so generated text cannot truncate streams, and OpenAI Responsesincomplete/failed/cancelledstatuses surface as failure reasons while preserving partial content.
- Embeddable and testable HTTP server router β
ServerBuilder::build_router()exposes the configured Axum router for embedding and integration tests, while engine RPC dispatch now shares CBOR/JSON decoding and result encoding paths, resolves thedefaultengine consistently, and keeps request logging behavior unified. - API key middleware hardening β API key checks now compare equal-length keys in constant time and keep exempt-path configuration clone-friendly.
- Signed HTTP/RPC client hardening β The Web3 client now caches its principal at build time, centralizes HTTPS guard and signed-header construction, avoids echoing secret material in identity-load errors, and shares signed CBOR RPC request construction across async and trait-based call paths.
- Follow-up delivery during tool execution β
CompletionRunnernow queues follow-up messages for the next safe user turn and delivers them after pending tool-call results finish instead of waiting for a fully idle boundary. Tool outputs are preserved in chat history before the follow-up is sent, while steering messages still take priority.
-
Todo tool operation API β Replaced the
todos/mergewrite contract with explicitop=read|set|updateanditemsparameters.setreplaces the list,updatepatches only changed ids, empty ids are ignored instead of materialized, and write calls now return summary counts whilereadreturns the full item list. This reduces tool-call payload size during long-running work and keeps task-list updates focused on changed items. -
Note tool operation API β Replaced substring-based
action=add|replace|removewrites with stable-idop=read|set|upsert|deleteanditemsparameters. Notes now persist in thenotes_v2store, writes return compact summary counts, and read/load operations return structured note items with usage summaries. -
Deprecated extension cleanup β Removed the deprecated
googleandextractorextension modules fromanda_engine, eliminating the legacy Google Custom Search tool and generic extractor helper that were previously marked deprecated. -
Unified child context paths β Agent and tool child context paths now consistently use underscore-separated names such as
a_echo_agentandt_echo_toolon every platform.
-
HTTP client response decoding defaults β Restored
reqwest's default response decoding behavior for the shared model HTTP client instead of globally disabling gzip, Brotli, zstd, and deflate decoding. -
Streaming completion body read resilience β SSE completion readers now return immediately after receiving
data: [DONE], preserving completed OpenAI-compatible streams even if the server or proxy closes the HTTP body with a late transport error.reqwestdecode errors are also classified as retryable model transport failures so upper layers can apply delayed retry behavior instead of treating them as permanent completion failures. -
Structured subagent tool arguments β Subagent calls now preserve full structured argument objects, including
session,model, andeffort, instead of collapsing any object with apromptfield down to the prompt string. This keeps asynchronous/session subagent calls working while preserving legacy plain-string and single-promptagent behavior.
- Adaptive discovered-tool request merging β
CompletionRunnernow tracks schemas returned bytools_searchandtools_select, promotes repeatedly selected discovered tools into subsequent request tool definitions, and compacts repeated discovery outputs once schema merging is enabled. This lets long-running agents call tools after repeated discovery without resending full schemas in every tool-output context. - Identity-encoded completion transport β The shared model HTTP client now disables automatic response decompression so streaming readers can consume raw SSE bytes even when a provider or proxy mislabels
Content-Encoding.
- Failed in-flight tool-result cleanup β Added
CompletionRunner::discard_in_flight_request()to clear stale request content, pending tool calls, and dangling raw tool-call history after a transport-level model failure before processing newly queued input.
- Legacy text decoding support β Added shared text encoding helpers that keep UTF-8 as the preferred path while allowing platform-local legacy text fallback on Windows. Resource-to-text conversion now uses MIME-aware fallback decoding for text-like resource blobs without treating binary media as text.
- Filesystem, shell, and skill tools handle platform-local text encodings β File reads, writes, shell output previews/progress, and
SKILL.mdloading now decode or encode supported text encodings such as GBK when needed, preserving UTF-8 behavior by default and keeping binary/unsupported data on the base64 or error paths. Shell progress streaming now preserves multibyte character boundaries for both UTF-8 and common legacy multibyte encodings. - Streaming completion responses are more robust β OpenAI-compatible, Anthropic, and Gemini streaming requests now ask providers for identity-encoded event streams and the shared parser accepts BOM-prefixed SSE, NDJSON, plain JSON event payloads, and JSON arrays. This avoids provider/proxy response-shape surprises while preserving existing SSE handling.
- Windows-compatible agent and tool context paths β Agent/tool child context paths now avoid colon separators on Windows while preserving existing colon-based namespaces on Unix-like systems, keeping existing deployments stable and making Windows storage paths valid.
- Native shell final progress flush β Background shell tasks now emit any final complete stdout/stderr progress lines before the background-end hook, so short-lived commands do not lose their last progress update when they exit before the next progress interval.
- Portable skill output paths β
skills_managernow normalizes relativeSKILL.mdpaths with/separators on Windows, matching the existing API output shape on Unix-like systems.
- BOM-only resource text is filtered out β Legacy text decoding now rejects non-empty byte slices that decode to an empty string, preventing Windows fallback decoding from turning BOM-only binary or empty-looking resources into empty prompt documents.
- Model completion retry and retry metadata β Added a shared
ModelErrortype and provider request helper that retry transient completion failures once across OpenAI-compatible, Anthropic, and Gemini adapters. Retryable HTTP statuses now include request timeout, rate limiting, 5xx gateway/server failures, and provider-specific529; exhausted retryable failures expose retryability, HTTP status, andRetry-Aftermetadata for upper-layer delayed retry decisions. Streaming response read failures now use the same retryable transport error path, and Anthropic streamoverloaded_error/rate_limit_errorevents are marked retryable. - Tool discovery wording clarified β Updated
tools_searchandtools_selectdescriptions to make schema discovery explicit: returned callable schemas live in tool-output context and should be called directly instead of being dynamically inserted into subsequentCompletionRequest::tools. Added regression coverage that selected tool schemas remain available as context without expanding the request tool list.
- Recursive strict JSON Schema normalization β
normalize_strict_schema()now recursively appliesadditionalProperties: false,properties: {}, andrequired: []defaults to all nested object schemas, not just the root. Previously propertyless nested objects (e.g.parameterswithout explicitproperties) were left open, breaking strict-mode contracts for tools with nested object parameters. Added tests for nested propertyless object closure and recursive normalization.
- Reasoning content merged into message text β
ContentPart::Reasoning { text }inmessage_into()is now appended asContentItem::Textin the current message content block instead of being emitted as a separateMessageItem::Reasoning. This simplifies the OpenRouter-facing message structure and avoids injecting standalone reasoning items that providers may reject or misinterpret.
- Multiple skill directories in
SkillManagerβ AddedSkillManager::new_with_dirs()so skill loading and lookup can scan the default skill creation directory plus additional read-only skill roots. Skill descriptions now list all configured directories, duplicate directory entries are deduplicated, duplicate skill names are skipped after the first load root, and displayedSKILL.mdpaths are made relative to the matching configured root. Added coverage for loading and reading skills across multiple directories.
-
Subagent session resource and compaction edge cases β Resource-only session follow-ups now run instead of being dropped, resource attachments are converted independently so one invalid resource no longer discards all content, compaction handoff calls temporarily disable tool definitions while preserving them for subsequent turns, final session output falls back to the latest visible progress when compaction produces no reportable result, and background session start hooks are emitted only after acknowledgement hooks succeed. Added regression coverage for resource-only follow-ups, compaction finalization, tool restoration after compaction, and failed acknowledgement hooks.
-
Agent completion runner edge cases β Fixed completion streams so a pending model future is retained across polls instead of being recreated, document-only requests now execute without requiring prompt/content text, steering after tool calls prunes only unanswered raw tool-call items while preserving prior raw history and assistant reasoning, completed runners ignore late steering/follow-up queues, and remote agent resource selection now uses the unprefixed agent name. Added regression coverage for the stream polling, raw-history pruning, steering, and document-only paths.
- Hardened OpenAI-compatible response parsing β Chat Completions and Responses now tolerate DeepSeek-compatible variants: nullable usage detail objects and counters, unknown
service_tierstrings,reasoningaliases for reasoning content,tool_call/tool_use/function_callfinish reasons, JSON object tool arguments/inputs, and missing message/tool-call role/type/id fields. Added tests for non-streaming and streaming compatibility shapes. - Expanded provider compatibility tolerance β Anthropic streaming now ignores unknown stream events and content deltas instead of failing; Gemini now preserves unknown enum values, accepts nullable usage/safety metadata, and treats missing/unspecified finish reasons as successful candidate output. OpenAI Responses now preserves unknown statuses and streaming tool-call chunks can omit indexes. Added provider tests for these compatibility shapes.
- Removed fallback model support β Removed the
fallback_modelregistry slot fromModels, thewith_fallback_model()builder API, thefallback_model()accessor, fallback routing fromget_model()andresolve(), and all fallback-related completion logic inCompletionRunner::step(). Afallbacklabel is now an ordinary label with no special runtime behavior. Model routing uses the primary model plus explicit labels only. - Refactored response body reading β Replaced
response.text().await+serde_json::from_strwithresponse.bytes().await+serde_json::from_sliceacross OpenAI, Anthropic, and Gemini completions. Error messages now useString::from_utf8_lossyfor safer non-UTF-8 body display. - Improved subagent output lifecycle β Added
with_session(),latest_output(),finalize_output(), andrecord_failed_output()helpers toSubSessionRunnerfor consistent session tracking and error reporting. Cancellation failures and compaction errors now properly record their output before returning errors. Fixedneeds_compaction()panic oncontext_window == 0by treating it as unlimited. Added 3 new tests for output lifecycle and edge cases.
utf8_text_from_bytes()andutf8_text_from()β New text-detection helpers that check for excessive control characters (β€5%) in the first 4KB before treating a byte buffer as text. Prevents binary blobs from being incorrectly rendered as garbled text inContentPartandDocumentconversions.- Refined blob-to-text conversion β
ContentPart::TryFrom<Resource>andDocument::From<&Resource>now use the new text-detection functions instead of naiveString::from_utf8.Documentconversion also usesResourceRefto excludeblobfrom metadata and now correctly includes blob text content when detected as text.
ContentPart::Anyhandling unified across providers β Refactored theContentPart::Anyβ typed-part conversion paths in OpenAI, Anthropic, and Gemini modules to consistently (1) attempt deserialization into the provider's known typed form, and (2) fall back to wrapping the original JSON as a text part. Each provider now uses a dedicated helper (chat_completion_content_part_from_any/content_item_from_any/message_item_from_any/content_block_from_any/part_from_any) and explicitly filters out the catch-allAnyvariant so a round-trip throughAnynever silently replaces a typed part. This guarantees that structured content (e.g.input_image,web_search_call,tool_use) round-trips with full semantics, while truly unknown JSON is preserved verbatim as a stringified text payload for downstream inspection.
to_message_inputs_only_preserves_known_any_content_partsβ New test inanda_engine/src/model/openai.rsasserts thatContentPart::AnyJSON with a knownimage_urlshape is preserved as-is, while unknown shapes are wrapped as text with the original JSON serialized inside.message_into_only_preserves_known_any_itemsβ New test inanda_engine/src/model/openai/types.rscovers bothContentItemandMessageItemdeserialization paths: a knowninput_imagestays in the user message content, a knownweb_search_callbecomes its ownMessageItem, and an unknown shape falls back to a text payload carrying the original JSON.- Anthropic
ContentBlock::Anytest coverage β Extended the existingcontent_part_into_preserves_anthropic_specific_variantstest inanda_engine/src/model/anthropic/types.rswith three additional cases: rawtextblocks round-trip asContentBlock::Text, unknowntypevalues fall back to text-wrapped JSON, and malformedtool_useshapes also fall back to text-wrapped JSON instead of erroring. content_part_any_only_preserves_known_gemini_partsβ New test inanda_engine/src/model/gemini/types.rscovers the same fallback contract for Gemini parts: rawtextshapes becomePartKind::Text, unknown shapes are wrapped as text, and malformedfunctionCallshapes fall back to text-wrapped JSON.
Models::resolve()simplified fallback chain β Removed the deprecatedfallback_model()path and the arbitrary-first-model extraction fromresolve(). The resolution order is now: exact label match βget_model()(which returns the primary model, or the first available if none is configured). This eliminates a code path that could silently return an unexpected model and alignsresolve()with the well-testedget_model()behavior. Updated doc comment and test expectation accordingly.
-
ContentPart::any_fromandany_intohelpers β New typed constructors forContentPart::Any:any_from::<T>(ty, val)builds aContentPart::AnyJSON value with a"type"field, andany_into::<T>(ty)deserializes it back, verifying the type tag and returningErr(self)on mismatch. Enables type-safe round-trips for structured JSON content (e.g.Resource) without routing through specific provider types. -
test_content_part_any_from_and_any_into_resourceβ New test inanda_core/src/model.rsvalidates round-trip serialization ofResourcethroughContentPart::Any, covering all fields includingblob(base64),metadata,uri, andmime_type. Also verifies type mismatch returnsErrand non-Resourceinput is rejected.
- Model-aware subagent manager descriptions β
SubAgentManagernow carries amodels: Vec<String>field set viawith_models()builder. When models are registered, the tool description dynamically includes available model names for routing decisions (e.g. "This manager supports the following models for routing decisions: flash, pro, primary."). resourcesfield in conversation storage βConversation::to_fields()now persists theresourcesfield alongsidemessagesandartifacts. Previously, resources attached to a conversation were lost on save/load cycles.ContentPart::AnyResource serde test β Newtest_content_part_any_supports_resource_serdevalidates round-trip serialization ofResourcethroughContentPart::Any, covering all fields includingblob,metadata,uri, andmime_type.
- Effort enum values:
xhighβmaxβ SubAgent and SubAgentManager function schemas now list"max"instead of"xhigh"in theeffortenum, aligning with theModelEffortrename in v0.12.24. Updated in three locations (subagent schema, manager schema, test assertion). model_names()doc comment β Added documentation for theModels::model_names()method describing its return value.ModelConfig.effortdoc:xhighβmaxβ Documentation string updated to reflect the renamed variant.
ModelEffortβ provider-agnostic reasoning/thinking effort β NewModelEffortenum (minimal,low,medium,high,max) provides a single vocabulary for reasoning effort across all model providers. Includesas_str()andDisplayimpl.- Per-request effort on
CompletionRequestβCompletionRequestnow carries aneffort: Option<ModelEffort>field, enabling callers (including subagents) to select reasoning effort per completion rather than only at model-config time.
- Tool error propagation via
is_errorβToolOutputandContentPart::ToolOutputnow carryis_error: Option<bool>. Agent runner setsis_error: Some(true)on tool/agent execution failures and tool-not-found paths. Memory tools signal Nexus errors through the same flag. Anthropic passesis_errorthrough toToolResult; Gemini routes errors toFunctionResponseValue.errorinstead of the normaloutputfield. - Subagent catalog, operation routing, and runtime state preservation β
SubAgentManagergainsoperationfield ("upsert"or"list"),catalog()method returning all registered subagents with metadata, andpreserve_runtime_state()to keep active subsessions alive across upserts.SubAgent::definition_description()now enriches descriptions with tags, allowed tools, output schema info, active sessions, default model, and default effort. Session-mode responses include session ID. - Provider-agnostic
ModelEffortwith per-request application βModelEffort(promoted from engine to core) maps to each provider's native reasoning level: AnthropicOutputEffort(saturates atMax), GeminiThinkingLevel(saturates atHigh), OpenAIReasoningEffort(now includesMaxvariant), and OpenAI Responses v2Reasoning. All three providers now applyreq.effortduringcompletion(), not only model-config defaults. - Subagent model and effort selection β
SubAgentandSubAgentArgsgainmodel: Stringandeffort: Option<ModelEffort>fields, with case-insensitive deserialization (accepts"HIGH","max",null, empty string, or JSON value). Session compaction preserves model and effort across context cycles. BothSubAgentandSubAgentManagerfunction schemas exposemodelandeffortparameters. - Model label resolution β
AgentCtxnow resolves models by label viamodels.resolve(label)instead of the previous name-only lookup.CompletionRunnergainsset_model()andset_effort()methods for mid-run model/effort switching. - Subagent tests for model/effort selection β New
subagent_run_allows_model_and_effort_selectiontest verifies end-to-end flow: subagent receives a model label, the correctCompletionRequestreaches the provider with the expected model and effort. NewRecordingRequestCompletermock capturesCompletionRequestfor assertion. All existing tests updated for new fields.
- Anthropic error messages include model name β Non-200 HTTP responses now include the model name in the error text for easier debugging of provider failures.
- Shell foreground commands auto-move to background after 42 seconds β Previously native foreground commands were waited on with
wait_with_output(), and if they exceededSHELL_TIMEOUT_SECS(180s) the tool would return a timeout error. Nowexecute_commandusestokio::select!withSHELL_AUTO_BACKGROUND_SECS(42s): if the process hasn't finished within that window, it is transparently moved to background execution with a "moved to background" message containing the task ID for hook delivery. TheRunningProcessstruct now bundles child/stdout/stderr/readers immediately after spawn, and extractedfinalize_process_output()is shared between the foreground-completed and background-completed paths. Added testexecute_auto_moves_long_running_foreground_to_backgroundverifying the auto-transition and hook delivery. - Comprehensive sub-session compaction tests β New mock infrastructure (
UsageCompleter,RecordingCompactionCompleter,RecordingAgentHook,request_texthelper) and three new tests:needs_compaction_respects_usage_thresholdverifies compaction triggers at exactly 100,000 input tokens;needs_compaction_triggers_at_turn_limitverifies compaction afterMAX_TURNS_TO_COMPACTturns with unbound runner;subsession_runner_compacts_context_and_continues_from_handoffexercises the full compaction flow β context is compacted into a single assistant handoff message, instructions/role/output_schema are preserved, and subsequent follow-up input resumes correctly from the compacted history.
- SubAgentManager now isolates subagent storage under
subagents/prefix β Previouslystore_listwas called with no prefix filter (loading everything from root), and subagents were stored directly at the root level. Now bothload()andsave()usestore_prefix() = "subagents"as the listing and storage path prefix, separating subagent data from other store entries. Added test verifying that a legacy agent stored at root level is not loaded bySubAgentManager::load. - Fixed
ContentPart::FileDataconversion for non-remote URIs β In Anthropic, Gemini, and OpenAI (bothto_message_inputsandmessage_into),ContentPart::FileDatanow guards onfile_uristarting withdata:orhttps://before converting to provider-native blocks (Image, Document, File, Video, Audio). Non-remote URIs likefile://now correctly fall back to JSON-serialized text content blocks instead of being sent as inaccessible document/file references to the API. Added tests across all four conversion paths.
- Removed
modelfield fromAgentCtxβ The cached default model instance is no longer stored on the context. Insteadmodels.get_model()is called inline at the point of use (completion_runner,model_name()). Child and spawn agent contexts no longer need to clone the parent's model. This reduces cloning and simplifies the context lifecycle. - Compaction threshold raised to 80% of context window β
needs_compactionnow triggers at 80% (saturating_mul(8) / 10) instead of 50% (saturating_div(2)), with the minimum raised from 50,000 to 100,000 tokens. Fewer unnecessary compactions, preserving more context across turns.
normalize_schema_objectnow correctly handles["object", "null"]types β Theis_objectcheck previously only matched"type": "object", missing nullable object schemas like["object", "null"]. Newschema_type_contains_object()helper checks both string and array type values. Also,additionalProperties: falseis now set for all object schemas, not only those withpropertiesβ so{"type": ["object", "null"]}without properties also gets it. Added testtest_normalize_strict_schema_handles_nullable_objectscovering both cases.- OpenAI Responses v2: filter empty Reasoning and ItemReference from history β
normalize_message_itemnow returnsNoneforReasoningitems withencrypted_content: None(empty reasoning blocks) andItemReferenceitems (by-ID references with no content). Previously these passed through as-is and could cause provider errors.raw_history_intonow chains.filter_map(normalize_message_item)on the coreMessagehistory path too, which previously bypassed normalization.
output_schemain subagents_manager now uses["string", "null"]β The JSON schema is submitted as a JSON-encoded string instead of a nested object. This avoids the structured-output problems that nested object schemas cause with strict function-calling providers. Addeddeserialize_optional_json_schemacustom deserializer that accepts JSON strings (parsed inline) ornull. New tests:subagents_manager_definition_uses_strict_safe_output_schema,subagents_manager_args_accept_json_encoded_output_schema.
- Stripped non-essential JSON Schema keywords from tool definitions β Removed
default,uniqueItems,minLength,minLength(input/description),pattern,maxLength,minimum, andmaximumfrom all tool schemas. These keywords add unnecessary strictness in a world whererequiredalready lists every field; some providers may also fail to handle them correctly in strict function calling mode. Affected tools:tools_search/tools_select: removeddefaultanduniqueItemsskills_manager: removedpattern,maxLengthlist_conversations/search_conversations: removeddefault,minimum,maximumsubagents_manager/subagent: removedminLength,uniqueItems,pattern
- Removed automatic
max_output_tokensinjection βCompletionRunnerno longer appliesself.model.max_outputas a defaultmax_output_tokenswhen the request omits it. This responsibility now belongs to each provider's completion method or the caller, giving more precise control over token limits per request. Previously the runner automatically setreq.max_output_tokens = Some(self.model.max_output)for any request without an explicit limit.
- Strict schema normalization β
normalize_strict_schema()inanda_core::jsonrecursively normalizes JSON schemas for strict function calling: rewritesrequiredto contain every key inproperties, defaultsadditionalPropertiestofalse, and traverses nested schemas (items,$defs,allOf/anyOf/oneOf,if/then/else,not, etc.). NewFunctionDefinition::normalize_strict_parameters()method calls this normalization whenstrict: true. All tool definitions (ToolDefinition::from,CompletionRequesttool-building) now go through this normalization path β ensuring consistent strict schemas regardless of provider. - Model-level stream control β Added
stream: boolfield toModelConfigandwith_stream()builder methods on all completion model types: Anthropic (CreateMessageParams.stream), Gemini (GenerateContentRequest.stream), OpenAI Chat Completions (ChatCompletionRequest.stream), and OpenAI Responses v2 (CompletionRequest.stream). Each provider'sCompletionModel::new()now readsself.streamand sets the default request accordingly. - OpenAI Responses v2 defaults to streaming β The v2 model now defaults to
stream: truewithstore: falsefor stateless requests. The originalreasoningdefault parameter was removed. - OpenAI Responses v2 message history normalization β New
raw_history_into()converts rawJsonhistory intoMessageItemvalues with proper role-aware normalization: coreMessagehistory is unwrapped viamessage_into(), legacy ResponsesMessageItem::Messagerecords are normalized throughnormalize_message_item(), and unrecognized values pass through asAny. Assistant messages in history now useoutput_textcontent type (required by Responses API) rather than plaintext. Content-type filtering distinguishes input (text) from output (output_text) based on role. - OpenAI Responses v2 stream aggregation β
responses_response_from_stream_events()now handlesResponseOutputItemDoneevents: when the final response has an emptyoutputarray, output items collected fromdoneevents fill in viaoutput_indexordering.
- All tool schemas now declare complete
requiredarrays β Every tool definition schema lists all property keys inrequiredwithadditionalProperties: false, including optional fields. Previously many tools omitted optional fields fromrequired, which triggers strict schema validation failures with providers. Affected tools:tools_search,tools_select,list_conversations,search_conversations,memory,note,todo,subagents_manager,read_file,write_file,edit_file,search_file,shell,extractor(SubmitTool). anyOfavoidance in tool schemas βnotetool now uses"type": ["string", "null"]with inlineenumcontainingnullinstead ofanyOf-based union schemas.todoandsubagents_managertools similarly use["array", "null"]and["object", "null"]type arrays. These patterns avoidanyOfwhich many providers (especially Anthropic) reject in strict mode.- Memory tool gets hand-written schema β Replaced auto-generated schema with a manually crafted one that lists all 9 operation types in a flat
typeenum with all optional fields explicit, avoiding the nested discriminator pattern that produced invalid schemas across providers. - Gemini default request β Removed the hard-coded
top_p: 0.95default fromCompletionModel::new(). - Subagent
sessiondescription β Changed from "Omit session" to "Leave session empty" to match strict schema nullability. - All tests updated β Test function definitions, schema assertions, and expected values updated to include full
requiredarrays andadditionalProperties: false.
- SSE streaming support across all backends β Anthropic, Gemini, OpenAI Chat Completions, and OpenAI Responses backends now support true SSE (Server-Sent Events) streaming. Previously responses were always read as complete JSON payloads; now streaming responses are parsed and aggregated from
text/event-streamchunks. Added genericread_sse_json_events<T>()inmodel.rswith proper line-buffering, UTF-8 validation, and multi-linedata:event concatenation.- Anthropic:
response_from_stream_events()reconstructsCreateMessageResponsefrom stream events β handles MessageStart, ContentBlock{Start,Delta,Stop}, MessageDelta, and MessageStop. Content blocks (text, thinking, tool_use, server_tool_use) are incrementally assembled; cursor deltas accumulate non-zero fields. Streaming enabled when requeststream=true. - Gemini:
response_from_stream_chunks()aggregates:streamGenerateContent?alt=sseSSE responses. Candidates with the same index are merged β text parts are concatenated, finish_reason/safety_ratings/citation_metadata from later chunks overwrite earlier, and non-empty fields from later chunks supersede earlier defaults. - OpenAI Chat Completions:
chat_completion_response_from_stream_chunks()reconstructsCompletionResponsefromchat.completion.chunkSSE events. Stream delta accumulation handles: text content concatenation, content parts extension, tool call incremental assembly (id/type/function/custom), reasoning_content, refusal, function_call, and finish_reason. Tool calls are built viaToolCallStreamBuilderwith per-indexBTreeMaptracking. - OpenAI Responses:
responses_response_from_stream_events()extracts the most recent response from events β picks upresponse.created/in_progress/completed/failed/incomplete. Parses output after reconstruction.
- Anthropic:
- All streaming backends set
Accept: text/event-streamheader and conditionally dispatch to streaming vs non-streaming code paths based on request configuration. - Gemini:
streamrequest flag β Addedstream: boolfield toGenerateContentRequest(serde-skipped, local-only) for choosing the streaming endpoint. - Unit tests:
aggregates_anthropic_stream_events(text + tool_use with partial JSON),aggregates_gemini_stream_chunks(text concatenation across chunks),aggregates_chat_completion_stream_chunks(text + streaming tool calls),aggregates_responses_stream_completed_event(response.completed extraction).
CompletionRunner::is_idle()β New method that reports whether the completion runner has no pending work: prompt, content, documents, steering message, follow-up message, and pending tool calls are all empty. Useful for polling-based control loops that need to detect when the agent is ready for new input.
- Subagent
tagsdescription refined β Added concrete examples (image,text,audio) to thetagsfield description in thesubagents_managertool schema.
- Error messages include model name across all backends β HTTP request send failures and response body read failures in Anthropic, Gemini, OpenAI, and OpenAI V2 backends now include the model name in the error message. Previously these relied on bare
?propagation, yielding opaque errors like "connection refused" with no model context, making multi-model debugging difficult. - OpenAI: simplified model reference β Removed redundant local
let model = self.model.clone(). Logging and errors now user.modelconsistently.
- OpenAI: Multi-tool-output splitting β
to_message_inputrefactored toto_message_inputs, now returningVec<MessageInput>. When a singleMessagecontains multipleToolOutputcontent parts, each output is emitted as a separate tool-role message with its owntool_call_id. This fixes the case where multiple tool outputs were flattened into one message, which violated the OpenAI API contract (one tool message per call). - OpenAI: Non-text content part preservation β
MessageOutput β Messageconversion rewritten. The oldtext()filter that discarded non-text parts is replaced withchat_completion_content_into_parts/chat_completion_content_part_into/file_data_content_partpipeline. Image URLs, input audio, files, video URLs, and refusals now survive the round-trip conversion as properContentPartvariants instead of being silently dropped. MessageInputnow carriesnameβname: msg.name.clone()is propagated toMessageInputduring conversion.
implicit_contextinjection timing β When pending tool calls are executed mid-turn, the implicit context is no longer injected on the same request. Instead it's deferred to the next user-facing turn, preventing implicit context from being consumed on tool-result rounds where it would have no effect.- OpenAI: DeepSeek
tool_choicecompatibility β Skip settingtool_choicefor models whose name starts with"deepseek", as DeepSeek's API does not support this parameter and returns errors when it is present.
implicit_contextonCompletionRunnerβ newimplicit_context(&mut self, message: Message)method stores a context message that is automatically injected into the next request'schat_historyand consumed on use. This enables steering/follow-up messages to pass contextual information without manual request manipulation.- Prompt ordering fix across all providers β system prompt now uses
content.insert(0, req.prompt.into())instead ofcontent.push(), ensuring the prompt appears before the conversation content in all three model backends (Anthropic, Gemini, OpenAI, and OpenAI v2). This fixes cases where the prompt was appended after content instead of leading. - OpenAI:
modelfield fix βCompletionModelnow explicitly setsr.model = self.model.clone()on each request, fixing a bug where the model field in the cloneddefault_requestwas not being updated to the current model selection.
Resource β ContentPartconversion now usesTryFromwith MIME detection βimpl TryFrom<Resource> for ContentPartreplaces the infallibleFromimpl. Binary blobs now useinfer2to detect the actual MIME type from bytes instead of defaulting toapplication/octet-stream. Resources with neither blob nor URI returnErr(res)instead of serializing to text.inline_data_from_data_urlβ New helper to parse data URLs (data:[<mime>][;base64],<data>) and plain base64 strings into(ByteBufB64, mime_type)pairs. Handles both base64-encoded and percent-encoded payloads.decode_percent_encoded_bytesβ Internal helper for percent-decoding URL-encoded byte sequences.- Comprehensive test suite for
anda_core::modelβ Added 10 test functions coveringAgentInput,ToolInput,PromptCommand,AgentOutput::into_tool_output, data URL round-trips,ContentPart::try_from(Resource)edge cases,RequestMeta,Usage::accumulateoverflow,FunctionDefinition,Document/Documents, andMessagedeserialization.
- Anthropic: Extended API surface β Full support for the latest Anthropic Messages API:
SystemPromptenum (string or content blocks),CacheControlEphemeralfor prompt caching,OutputConfigwithOutputEffortandJsonOutputFormat, structuredStopDetails::Refusal,ToolChoiceconstructors (auto(),any(),tool()),ThinkingDisplayandThinkingType::Adaptive/Disabled, extendedContentBlockvariants (document, search_result, server_tool_use, web_search/fetch results, code execution results, container_upload),ToolResultContentas text-or-blocks,CitationsConfig/TextCitation,UsageServiceTier/CacheCreation/ServerToolUsageonUsage,Containerin responses. - Gemini: Extended API surface β
SafetySetting/HarmBlockThreshold,cached_content/service_tier/storeonGenerateContentRequest,ModelStatus,GroundingAttribution/GroundingMetadatawith rich chunk types (web, images, maps, retrieved context),LogprobsResult,UrlContextMetadata,SpeechConfig/VoiceConfig,ImageConfig/MediaResolution,Modalityenum,response_json_schemafields,seed,enable_enhanced_civic_answers, extendedFinishReasonvariants.SatisfyRatingrenamed toSafetyRatingwith backward-compatible alias. Fixedsatefy_ratingstypo with serde alias. - OpenAI: Full
ChatCompletionRequesttype β Structured request builder replacing ad-hoc JSON construction. Supportsaudio,modalities,reasoning_effort,response_format(text/json_object/json_schema),service_tier,stop(string or array),stream_options,tool_choice(none/auto/required/allowed_tools/function/custom),verbosity,web_search_options,prediction,prompt_cache_key/prompt_cache_retention,logprobs/top_logprobs,safety_identifier,seed,store,metadata,user,parallel_tool_calls,frequency_penalty/presence_penalty. - OpenAI: Content types and refusal handling β
ChatCompletionMessageContentsupports text, content parts (text/image_url/input_audio/file/refusal), andnulldeserialization. Refusal detection from both legacyrefusalfield and content-block refusals.MessageOutputnow usesChatCompletionMessageContentand provideshas_output()/has_refusal()helpers. - OpenAI: Custom tool support β
ToolDefinitionas enum withFunctionandCustomvariants.CustomToolDefinitionwith text/grammar format.ToolCallOutputsupports bothfunctionandcustomcall types.CustomToolCallwith rawinputstring. - OpenAI: Tool calls extracted to
tool_callsfield β Assistant messages now serialize tool calls in a top-leveltool_callsarray alongsidecontent, matching the OpenAI API shape. - OpenAI: Usage details β
CompletionTokensDetails(reasoning_tokens, audio_tokens, accepted/rejected_prediction_tokens) andPromptTokensDetails(audio_tokens). - OpenAI: Media type routing β File/image/audio/video
ContentPartitems now route to the correct content block type (image_url,input_audio,video_url,file) based on MIME type. - OpenAI Responses API v2: Extended types β
StreamEventenum with 11 event types.MessageItemexpanded with file_search_call, computer_call, web_search_call, tool_search, compaction, image_generation, code_interpreter, shell calls, apply_patch, MCP calls, custom tools.ToolDefinitionexpanded with file_search, computer, web_search, MCP, code_interpreter, image_generation, local_shell, shell, custom, namespace, tool_search, apply_patch.ContextManagementandResponseConversationfor conversation state. - Model routing β OpenAI models starting with
gptnow usecompletion_model_v2(Chat Completions API), while non-gpt models use the standard Responses API path. - SubAgent:
FromβTryFrommigration β SubAgent resource-to-ContentPart conversion updated to use the newTryFromimpl.
- Case-insensitive model label lookup β
Models::get(),Models::contains(), andModels::resolve()now normalize labels withto_ascii_lowercase()before lookup. Labels are stored lowercase ininner_set. This meansget("GPT-4")andget("gpt-4")resolve to the same model. - Model names auto-registered as labels β
inner_set()now appendsmodel_name.to_ascii_lowercase()to the label set. A model withmodel_name = "primary"is now findable viaget("primary"), removing the need for manual label aliasing.
CompletionRunnertools_select auto-loading β the completion loop no longer parsestools_selectresults to automatically inject selected tool definitions into the next turn. Tool selection/loading is now handled externally by the calling context, simplifying the runner's responsibility and removing ~25 lines of specialized handling code.is_tools_select_name()helper removed β no longer needed after the above simplification.- ToolsSelect/ToolsSearch integration tests β
ToolsSelectFlowCompleter,ToolsSelectQueryFlowCompleter, andToolSelectorCompleter(~280 lines of test infrastructure) removed alongside the auto-loading behavior they tested.
- Relaxed ToolsSelect/ToolsSearch parameter constraints β
minLength,minItems, andminimumconstraints removed from JSON schemas. These validations now happen at the implementation level, giving models more flexibility in parameter usage. - Optimized
select_requested_names_with_modelserialization β newToolItemRefstruct serializes onlyname+descriptionwhen passing candidates to the selector model, instead of fullFunctionDefinition(which includes large parameter schemas). Reduces token usage.
local_date_hour(now_ms: u64) -> Option<String>β converts a Unix millisecond timestamp to a local datetime string in"YYYY-MM-DD HH(AM/PM) Β±TZ"format.- Test coverage for
lib.rsutilities βrand_number,rfc3339_datetime,json_set_unix_ms_timestamp,json_convert_rfc3339_timestamp, andlocal_date_hournow have comprehensive tests.
with_caller()for context cloning βBaseCtx::with_caller(caller)andAgentCtx::with_caller(caller)clone the context with a new caller principal while preserving all extensions and internal state. Useful for sub-operations that execute under a different identity.
- Background progress interval 3s β 5s β reduces noise for long-running commands (e.g., model inference, large builds). New
NativeRuntime::background_progress_interval()builder method allows per-runtime customization for environments that need faster or slower tick rates.
- 2D terminal emulation for shell progress β
TerminalProgressStateupgraded from single-line buffer to full 2D terminal model with multi-line scrolling, cursor row tracking, and dirty-row incremental output. Supports CSI cursor movement (A/B/C/D), absolute positioning (G/H/f), line/screen erase (J/K), and multi-line parallel progress bars β all rewritten lines across rows are reported together per progress tick. - Smarter rewrite-mode detection β
has_rewrite_control()now only activates rewrite mode for actual terminal-control CSI sequences, not passive styling (colors, decorations). Plain ANSI-styled output stays in line-buffered mode and is emitted on newline boundaries only.
- Plain progress mode is line-buffered β non-rewrite output accumulates until
\n, then emits complete lines viacompleted_linesbuffer. No more mid-line fragmentation in plain-text progress.
- Background shell progress hooks β
on_background_progress()hook delivers incremental stdout/stderr every ~3 seconds while a background command runs.TerminalProgressStatenormalizes rewritten terminal lines (\r,\b, ANSIESC[K) to their latest visible text, so the model sees clean output instead of raw control characters. UTF-8 boundary-safe chunking viacomplete_utf8_prefix_len()prevents splitting multi-byte sequences across progress deliveries. insecure()mode for NativeRuntime β builder option to skipenv_clear(), allowing the shell to inherit host environment variables.
execute_command()extracted as public method onNativeRuntimeβ takes astd::process::Commanddirectly, enabling non-shell invocations through the native runtime.build_shell_command()now returnsstd::process::Command(decoupled from tokio).ToolsSearch::NAMEandToolsSelect::NAMEadded aspub constβ used inname()instead of raw constants for cleaner code.
Executor::temp_dir()removed from the trait β no longer part of the public executor interface.NativeRuntimegains atemp_dir()builder method instead.- Native executor
name()changed from"native_shell"to"shell". tools_searchremoved fromDEFAULT_SKILL_TOOLSβ skill agents no longer receivetools_searchby default.
- Remove deprecated tests β test modules removed from
extractor.rs(106 lines) andgoogle.rs(52 lines), both already marked#[deprecated]since 0.12.0.
- Multi-workspace file tools β
ReadFileTool,EditFileTool,WriteFileTool, andSearchFileToolnow acceptworkspaces: Vec<PathBuf>instead of a single workspace. Context metaworkspace/workspacesfields take precedence over defaults, with automatic fallback.SearchFileTooliterates all workspaces and merges results. Newwith_workspaces()constructor for multi-default setups.
- Workspace-scoped error messages β all filesystem tool errors now include workspace, requested path, and resolved path context for faster debugging. New
workspace_access_error()helper produces consistent "not accessible from any configured workspace" messages. - Note storage limits doubled β
NOTE_CHAR_LIMIT8 KB β 16 KB,NOTE_MATCH_PREVIEW_LIMIT80 β 120 chars.
- Skill cache now correctly updates on successful load (
write().entry().insert_entry).
google,extractormodules marked#[deprecated(since = "0.12.0")]β will be removed in a future release.
- Unified
ToolsOutputβToolsSearchOutputandToolsSelectOutputmerged into singleToolsOutputstruct withVec<FunctionDefinition>(full tool definitions, not just name+description), so the model can invoke tools immediately aftertools_search. IntermediateToolsSearchItemstruct removed;rank_search_itemsreturns names directly, definitions resolved at output boundary. Default limits: search 0β10, select 0β5 with explicitMAXcaps.
- Agent failures as errors β when an agent call fails, return a
ToolOutputerror instead of breaking the conversation, allowing the LLM to correct and recover.
workspacefield removed fromExecArgsβ shell tool no longer accepts aworkspaceparameter; commands always execute in the runtime's workspace directory.join_current_dirhelper removed.
- Shell spawn failures now return structured
ExecOutputerrors instead of propagating as Rust errors, so the model can see and respond to command execution failures.
- Native shell runtime renamed from
"native"to"native_shell"; background shell tasks now return an immediatetask_idoutput so callers can track long-running commands.
- SubAgent extracted as top-level module β
SubAgent/SubAgentSet/SubAgentManagermoved fromcontext::subagentto top-levelcrate::subagent.SubAgentManageris now an Agent (viaAgentSet) instead of a Tool, enabling properAgentHookcallbacks for subagent lifecycle. Session-based background execution withSubSessionstracking, idle timeout (10 min), background task wait (1 hr), and automatic compaction at 81+ turns. - SkillManager reduced to read-only inspector β
SkillManagerno longer supportscreate/patch/edit/delete/write_file/remove_filedisk management operations. Skills are now created and updated by editing files directly on disk via shell or file tools.SkillArgssimplified to{name}only (withdeny_unknown_fields); output is nowSkillContentOutput. - Rename
work_dirβworkspaceacross all tools, runtimes, and context metadata. Shell output field changed fromwork_dirtoworkspace. - Drop legacy DeepSeek client β replaced by multi-label model support (
labels: Vec<String>onModelConfig). - Remove sandbox feature and
boxlitedependency from engine crate. Shell extension now always usesNativeRuntime. - Rename Dyn traits β
AgentDynβDynAgent,ToolDynβDynTool; internal storage switched fromBox<dyn ...>toArc<dyn ...>. - Rename
stepβturnsinCompletionRunnerAPI. - Remove
CompletionHookβ superseded byAgentHookwith background support. - Remove
prune_raw_historypipeline β trait method, 4 provider impls,CompletionRunnermethod, andpruned_placeholderhelper all deprecated since 0.11.0 have been removed. WorkDirrenamed toWorkspaceinExecArgs,ExecOutput, and context metadata.MAX_OUTPUT_BYTESincreased from 128 KB to 256 KB.ModelConfig::model()now returnsResultinstead of silently producing anot_implementedmodel.- Remove deprecated
evaluate_tokens(anda_core) andbuild_model(anda_engine) β superseded byestimate_tokensandmodel().
- Downcast support β
as_any()/into_any()onDynAgentandDynTooltraits withdowncast_ref/downcastconvenience methods for type-safe concrete type recovery. PromptCommandenum withFrom<String>for slash-command parsing (/ping,/command ..., plain text).AgentOutputimprovements βthinkingβthoughtsrename; newsessionfield;PartialAgentOutputstruct;into_tool_output()method.CustomEnvwith auto-inject β shell tool environment variables can be marked asdefaultfor automatic injection; key/description metadata exposed to model providers without leaking values.- Per-agent model selection β sub-agents and call-agents use their configured model from the
modelsmap, falling back to the parent model. CompletionRunner::finalizeβ clean unbound runner completion; steering interrupt; follow-up message consolidation.ConversationDeltaβ offset-based incremental conversation fetching for large conversations.- Per-tool usage tracking β
AgentOutputandCompletionRunnertrack per-toolUsage;ToolOutput.tools_usagefield. - Multi-label model support β
Modelsnow supportsHashMap<String, Vec<Model>>, allowing multiple models per label. - Tool call statistics β per-tool call counts tracked in
CompletionRunner.tool_call_stats. - Safe env vars for native shell β
SAFE_ENV_VARSwhitelist passes only functional host env vars (PATH,HOME,TERM, β¦) to shell commands, never secrets. - Prefix constants β
REMOTE_TOOL_PREFIX(RT_),REMOTE_AGENT_PREFIX(RA_),SUB_AGENT_PREFIX(SA_) defined as constants; prefixing centralized atAgentCtx::definitions(). CompletionRunner::unboundbuilder β enables unconstrained completion execution.EngineReffor late binding β switch toArc<Engine>throughout.- Per-agent storage and
output_schemaforSubAgentβ refactored from single CBOR to individual files per agent. - Multiple steering/follow-up messages queue β
Vec<String>andVecDeque<String>replace singleOption<String>. - Batch conversation retrieval β
batch_get_conversationsfor efficient multi-conversation loading. - Persistent note tool β agent-scoped durable notes with add/replace/remove operations.
- Todo tool β session-scoped task list shared with subagents.
- Strict mode enabled for all tool definitions (
strict: Some(true)). Conversationextra field β extensible metadata for conversations.Thoughtsmethod onMessageβ extract reasoning content.Idlestatus added toConversationStatus.- CWT verification support in engine-server.
- Subagent module extracted from context to top-level with session-based background execution.
- Hook system enhanced β
PrefixedId,on_background_progress,ToolBackgroundHook,DynToolJsonHook. - Model API simplified β
set_model_byβset,get_model_byβget+resolve;Models::from_configshelper. - Remote dispatch β now checks function registration, prefers longest matching handle.
- Native shell runtime β removed shell detection infrastructure; always uses
shon Unix,cmd.exeon Windows. - Defined prefix constants and centralized prefixing to outer layer.
BaseCtxpassed as parameter instead of stored inSubAgentManager.- Tool calls deferred to next turn.
EngineBuilder::empty()made async for proper initialization.- Child contexts clone parent state instead of sharing
Arc<RwLock>. SkillFrontmattermetadata upgraded fromBTreeMap<String, String>toBTreeMap<String, Json>.- Tool errors use JSON
{"error": "..."}formatting instead of plain strings.
- Auto-fill
max_output_tokensfrom model config when caller doesn't set it. - Cap Anthropic max tokens at 64,000.
- Normalize model labels to lowercase for consistent matching.
cache_store_setfix β cache was deleted instead of updated afterstore_put.nullcontent in Message deserialization handled correctly.select_resourcespreserves ordering withO(n)single-pass algorithm.SO_REUSEADDRfallback for platforms withoutSO_REUSEPORT.ToolsSelectOutputdeserialization fixed for nested.contentfield.- All 4 model response parsers now consistently set
output.thoughts.
- anda_engine β all module docs, README rewritten with Install, Quick Start, Core Concepts, Feature Flags, Security.
- anda_core β all module docs, README rewritten with module map, concepts, and minimal
Toolimplementation example.
- Upgrade
inferβinfer2(v0.21),boxliteβ 0.9 (crates.io). - Search file default limit set to 1000.
DEFAULT_SKILL_TOOLSexpanded to includetodo,tools_search,tools_select.- User name max length relaxed from 32 to 96 chars.
SkillFrontmattergainsextrafield withserde(flatten)for forward compatibility.