Skip to content

Latest commit

Β 

History

History
1952 lines (1468 loc) Β· 161 KB

CHANGELOG.md

File metadata and controls

1952 lines (1468 loc) Β· 161 KB

Changelog

All notable changes to the Anda project will be documented in this file.

[Unreleased]

[anda_engine 0.17.4] β€” 2026-10-20

Changed β€” anda_engine

  • Runners no longer compact after a fixed number of turns β€” CompletionRunner::needs_compaction_with returned true once 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 admitted compact_context request is pending. The turn cap was also the backstop for providers that report no usage; with them, compaction now comes from the model's own compact_context, whose usage notes estimate the conversation, or from the host's recovery when the provider rejects an oversized request.

[anda_engine 0.17.3] β€” 2026-10-18

Fixed β€” anda_engine

  • A tool that joins a DeepSeek conversation below max effort is usable again β€” 0.17.2 activated every tool that joined a deepseek-flash conversation from a system tool_addition message, 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 at low and 12 of 20 at high, 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 default deepseek-flash runs at max and keeps the cached prefix.

[anda_core 0.17.1, anda_engine 0.17.2] β€” 2026-10-17

anda_engine 0.17.2 requires anda_core 0.17.1.

Added β€” anda_core

  • CompletionRequest::tool_additions β€” the names of tools that joined the conversation after the request that produced the replayed raw_history. The completion runner fills it in for every request; a caller that builds requests itself can leave it empty. Code that builds a CompletionRequest with a struct literal needs ..Default::default().

Added β€” anda_engine

  • 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-flash the Anthropic adapter declares them with defer_loading: true after the tools offered from the start, activates them from a system tool_addition message after the input that brought them, and sends anthropic-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_additions switches the capability for other models; it is off for every model but deepseek-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 ignored tests/deepseek_live.rs repeats the check with DEEPSEEK_API_KEY.

Changed β€” anda_engine

  • minimal turns DeepSeek's thinking off β€” DeepSeek thinks unless thinking is disabled, so minimal, sent as low, still thought. On a DeepSeek model the Anthropic adapter now sends thinking: {"type": "disabled"} for minimal instead; 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 a minimal default. Other models keep their mapping.

Fixed β€” anda_engine

  • 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 said minimal effort maps to low on every Anthropic model, and it did not mention CompletionRequest::tool_additions or CompletionModel::with_tool_additions.

[anda_engine 0.17.1] β€” 2026-10-17

Fixed β€” anda_engine

  • 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 "thinking or redacted_thinking blocks 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 its thinking and redacted_thinking blocks 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.

[0.17.0] β€” 2026-10-17

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.

Changed β€” workspace

  • Workspace release alignment β€” anda_core, anda_engine, anda_engine_server, anda_web3_client, and anda_cli now share version 0.17.0; internal crate requirements have been raised to 0.17. The server, web3 client and CLI sources are unchanged.

  • tokio 1.53.3 minimum β€” the workspace tokio requirement rises from 1 to 1.53.3, the first 1.53 release with AsyncFd::register.

  • serde-saphyr 2 β€” the YAML library anda_engine uses for skill frontmatter and skill metadata sidecars moves from 1 to 2. No code changes were needed.

Removed β€” anda_core

  • Remote calls in the context traits β€” AgentContext no longer has remote_tool_definitions, remote_agent_definitions or remote_agent_run, and BaseContext no longer has remote_tool_call; it is now a pure bound over the capability traits, implemented with an empty impl. AgentContext::tool_call and agent_run return the output alone instead of pairing it with the principal of the remote engine that ran it. The remote_id fields of ToolCall and ContentPart::ToolOutput stay so stored histories keep decoding.

Removed β€” anda_engine

  • Remote engines, remote agents and remote tools β€” RemoteEngines, RemoteEngineArgs, RemoteTool, RemoteAgent, EngineBuilder::register_remote_engine, the DYNAMIC_REMOTE_ENGINES (_engines) registry, and the RT_ / RA_ routing prefixes (REMOTE_TOOL_PREFIX, REMOTE_AGENT_PREFIX) are gone. AgentCtx routes only to local tools, tool providers, local agents and SA_ subagents; RT_ and RA_ names are now ordinary names, and the completion runner and tool search no longer treat them specially. EngineCard and AgentInfo remain for Engine::information.

Added β€” anda_engine

  • Agent-initiated context compaction β€” CompletionRunner::set_self_compaction (SelfCompaction) offers the model a runner-intrinsic compact_context call (COMPACT_CONTEXT_TOOL, a reserved name). The model calls it at a task or phase boundary with a handoff it writes itself, so handoff seeds 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 least min_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 makes needs_compaction_with return true, so hosts that check it before every step run it through their existing handoff path; 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. From notice_percent (default 50%) the runner reports context usage in a short request-only $system note: on every user turn, and once per 10% band during tool rounds. The note follows the tool outputs and is never written to chat_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.

Fixed β€” anda_engine

  • Compaction no longer stalls an unfinished task β€” when handoff folded 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 with COMPACTION_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 deprecates AsyncFd::new as unsound, since it cannot know the descriptor stays open for the registration's lifetime. The PTY master is now registered through the unsafe replacement; the File that owns it moves into the AsyncFd, which upholds that requirement.

[anda_engine 0.16.10] β€” 2026-10-10

Added β€” anda_engine

  • MCP Events client β€” McpToolProvider implements the client side of the experimental MCP Events extension. list_events reads a server's event types (and returns None when the server does not support them); subscribe_events runs a poll or push subscription and reports events, cursors, list changes, errors and termination to an application McpEventSink, using a cursor only after the sink accepted it; McpEventSubscription::cancel does not wait for a busy sink. subscribe_webhook and unsubscribe_webhook manage webhook subscriptions whose deliveries the application receives itself, and report failures as McpEventErrors; call_server_tool lets 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. See MCP_INTEGRATION.md.

[anda_engine 0.16.9] β€” 2026-10-10

Fixed β€” anda_engine

  • Claude requests use the prompt cache β€” Anthropic caches a prompt only up to a cache_control breakpoint, and the adapter never sent one, so every round of an agent loop billed its whole prefix at the full input price and reported cached_tokens: 0. Requests now turn on Anthropic's automatic caching with the top-level cache_control field, 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-level cache_control set 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 the session-id header. 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; a prompt_cache_key set in the default request is kept.

[anda_engine 0.16.8] β€” 2026-10-09

Fixed β€” anda_engine

  • Tools with top-level schema combinators work on Claude β€” Anthropic rejects oneOf, allOf and anyOf at the top level of any tool's input schema and fails the whole request with a 400. KIP's execute_kip and execute_kip_readonly state command xor operations that 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.

[anda_engine 0.16.7] β€” 2026-10-08

Fixed β€” anda_engine

  • 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 anyOf or 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 the anyOf split added in 0.16.6, which counted as a union parameter.

[anda_engine 0.16.6] β€” 2026-10-08

Fixed β€” anda_engine

  • 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"] with null in enum) 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 an anyOf with 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: true commands, the starting call could still reply Running while 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.

[anda_core 0.16.3 / anda_engine 0.16.5] β€” 2026-10-08

Added β€” anda_engine

  • Task-scoped inline attachments β€” CompletionRunner::set_transient_inline_data lets a long-lived runner send attachment bytes without keeping them: request InlineData parts (and FileData with a data: URI) reach the model on every turn of the task that introduced them, never enter the neutral chat_history (a message left empty keeps a short note), and are replaced in raw_history by a text note when the runner goes idle, stop_current_task runs, or discard_in_flight_request drops the task. Media the model generated stays in its reply. A model switch or handoff in the middle of a task replays the neutral history, so the bytes leave early. A runner created by handoff inherits the setting.
  • CompletionFeaturesDyn::prune_inline_media β€” replaces inline media in provider raw history (Anthropic base64/text image and document blocks, OpenAI Chat image_url/video_url data URLs, input_audio and file data, OpenAI Responses input_image/input_file/input_audio including data: file URLs, Gemini inlineData and data: 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, which FunctionResponsePart accepts only as media, leaves parts, and its note joins the response text. The default is a union over the built-in adapters; Model::prune_inline_media forwards to it.
  • CompletionRunner::add_tools β€” appends tool definitions not already offered without resetting the tools discovered through tools_select, which set_tools forgets.

Fixed β€” anda_engine

  • Stopping a task keeps the conversation the runner replays β€” CompletionRunner::stop_current_task no 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 seeded chat_history and 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_request pruned 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.
  • handoff delivers 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 handoff keeps 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.
  • CompletionStream ends 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 through stream.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 handoff or 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::PerCallerSpace every 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 (the per_caller_spaces_isolate_one_tenant_from_another test aborted with anda_cognitive_nexus 0.14.3). Provisioning is boxed, which shrinks a memory tool call future from 48 KB to 6 KB.

Changed β€” anda_core

  • Inline images are no longer sized by their bytes β€” ContentPart::estimated_tokens caps an image/* InlineData part, or a FileData part whose data: URI carries an image, at 5,000 tokens. Providers bill an image by its resolution and downscale large ones, so the len / 3 estimate 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_required now 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 tool strict when schema-valid arguments matter.

Changed β€” anda_engine

  • Simpler completion runner internals β€” Tool execution returns its outputs without an infallible Result or optional results and shares one error constructor, the unprefixed-subagent dispatch branch that could only fail is gone, and finalize, 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 rmcp 3.5 and sha2 0.11. sse-stream stays on 0.2, the version rmcp builds its BoxedSseResponse from, and anda_engine hex-encodes SHA-256 digests through hex, since sha2 0.11 output no longer formats with {:x}; the encoded strings, and so checkpoint keys and patch hashes, are unchanged.

[anda_engine 0.16.4] β€” 2026-09-29

Fixed β€” anda_engine

  • tools_select works on current Claude models β€” The internal selector request no longer sends temperature: 0.0. Claude models from Opus 4.7 on reject a non-default temperature with a 400, which the selector swallowed, so selecting tools by query silently 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), so handoff and subagent compaction failed. That one request now replays the provider-neutral history instead of raw_history, as a model switch does; the replacement runner is unchanged.

Changed β€” anda_engine

  • Tool descriptions state their contracts β€” read_file, edit_file, write_file, search_file, and fetch_web_resources now describe their actual behavior: paging and the 256 KiB inline cap, that an edit with zero matches succeeds with total_matches: 0, parent-directory creation and full overwrite, path-only glob matching and scan_truncated, and the public-host, non-2xx, raw-body, and 20 MB rules for fetches. The legacy ShellTool background parameter describes what the model sees instead of host hook internals.
  • Docs β€” The anda_engine README notes that Anthropic maps tool_choice_required to tool_choice: any, which current Claude models reject.
  • Dependencies β€” anda_engine declares its anda_core requirement as 0.16 instead of 0.16.2.

[anda_engine 0.16.3] β€” 2026-09-28

Fixed β€” anda_engine

  • Shell approval gates survive the switch to ShellCommandTool β€” A ShellToolHook installed for the legacy shell tool now also gates ShellCommandTool: its before_tool_call sees the legacy fields just before execution and its rewrites apply, and its after_tool_call sees the legacy part of the result. Before, it still received the background events of session commands while their arguments bypassed it. ShellCommandToolHook and ShellSessionToolHook name the tools' own typed hooks.
  • Sessions in separately authorized directories β€” NativeRuntime::for_workspace derives 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 and shell_session could 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.

[0.16.2] β€” 2026-09-27

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.

Changed β€” workspace

  • Release scope β€” anda_core and anda_engine move to 0.16.2, and anda_engine now requires anda_core 0.16.2 for ToolPresentation. anda_engine_server, anda_web3_client, and anda_cli have no changes and stay at 0.16.1.

  • Dependencies β€” The workspace requires rmcp 3.4.1 and adds sha2 and sse-stream. anda_engine also depends on thiserror and enables the Win32_Storage_FileSystem and Win32_Foundation features of windows-sys.

Added β€” anda_core

  • Tool result presentations β€” ToolOutput<T> gains an optional model_output: Option<ToolPresentation> (text plus inline ToolMedia) that the runner sends to the model in place of output, which callers and audit hooks still receive in full. The presentation is stored as an explicitly tagged value inside ContentPart::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.

Added β€” anda_engine: shell sessions and workspace patches

  • Add the opt-in extension::workspace::coding_tools bundle (shell, shell_session, apply_patch) plus readonly_file_tools and file_tools. ShellCommandTool replaces the legacy ShellTool in that bundle (both are named shell; register only one). ExecArgs, ExecOutput, ShellTool and custom Executors stay source-compatible; the new session methods on Executor default to unsupported.
  • Supervise process sessions scoped by a host-created ShellSessionScope, the engine, caller and agent, with SessionLimits bounding retained sessions, runtime, output previews and raw logs. Interactive stdin and Unix PTYs require host opt-in. SandboxPolicy wraps commands with sandbox-exec on macOS or bwrap on Linux, denies network by default, and never falls back to unrestricted execution.
  • apply_patch prevalidates 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 shell output now keeps both its beginning and its tail.

Changed β€” anda_engine: notes and session tasks

  • 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. See docs/note-todo.md for API migration and limits.

Added β€” anda_engine: subagent runtime

  • 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.

Added β€” anda_engine: skill catalogs

  • Add skills_list and skills_read (registered by SkillManager::tools) for paginated discovery and package-scoped text resources next to the name-only skills_manager. Catalogs are immutable generations with stable path-based IDs, structured diagnostics, explicit invalidate/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.

Added β€” anda_engine: MCP runtime policies

  • 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_output with 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.

Changed β€” anda_engine: MCP compatibility

  • Stdio now forwards platform essentials and explicit environment overrides by default. Set inherit_env = true only 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 McpServerConfig constructors and defaults for new policy fields; ToolOutput literals now include optional model_output. See MCP_INTEGRATION.md.

Fixed β€” anda_engine

  • 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 local note tool, so nested and internal completions that inherit NoteContextConfig skip the note store.
  • Note writes validate trimmed IDs, matching ids lookups, and note results omit offset_chars and truncated when 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_requests wait for a slot instead of failing the root or worker turn.
  • Tools returned by tools_select stay 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_manager again 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 -1 for 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 minimum on unsigned integers or the default agent minLength, are sent as regular tools. Results answering calls kept only in native history are sent unchanged, Chat Completions keeps video_url parts for compatible providers, and in-band stream errors typed server_error are retried.
  • Session shell calls now wait for the process to exit or yield_time_ms to 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 that ShellToolHook does not gate ShellCommandTool or ShellSessionTool arguments.
  • shell_session log 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_patch deletes non-text files, treats blank lines in update chunks as empty context, and tolerates whitespace around the patch envelope.
  • The Linux sandbox binds /etc/hosts and /etc/resolv.conf and keeps the PTY as the controlling terminal of tty sessions. edit_file reports a missing file only when the file is actually absent.
  • Workspace file tools, apply_patch, skills_read and atomic_write_file again reach files below a directory the process may traverse but not list, such as Android's /data or a 0711 home directory. The no-follow walk now opens ancestors with search-only descriptors (O_PATH on Linux and Android, O_SEARCH on Apple platforms) and still refuses symlinked components.
  • MCP model presentations show structured content once: a text block that only repeats structuredContent as 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 audited output envelope are unchanged.

[0.16.1] β€” 2026-09-25

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.

Changed β€” workspace

  • Workspace release alignment β€” anda_core, anda_engine, anda_engine_server, anda_web3_client, and anda_cli now share version 0.16.1.

  • Published dependency upgrades β€” anda_db, anda_db_schema, anda_db_tfs, anda_kip, and anda_cognitive_nexus move from 0.13 to 0.14, anda_cloud_cdk from 0.6 to 0.7, and ic_tee_cdk and ic_tee_gateway_sdk from 0.7 to 0.8. ic_auth_types now requires 0.10.5, where ByteArrayB64 is Copy.

  • Hosts upgrade these crates in step β€” anda_core::Resource, anda_engine::context::AgentInfo, and anda_web3_client::tee's TeeGatewayClient / TeeGatewayClientBuilder are re-exported from the upgraded crates, and the memory APIs take AndaDB 0.14 handles. AndaDB 0.14 does not migrate Spaces activated under the cognitive-memory 2.1.0 draft; follow its upgrading notes before opening existing memory stores.

Fixed β€” anda_engine_server

  • Reject engine registry keys that differ from the engine's principal.
  • Honor discovery Accept weights, wildcards, and exclusions; return 406 when no representation matches. Mark caller-bearing responses no-store and retain Vary: Accept alongside 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 Display text instead of Debug output. On Windows, the listener no longer sets SO_REUSEADDR, which would let another socket bind the same port.

Changed β€” anda_engine_server

  • Drop the ic_tee_agent dependency and the ic_auth_verifier full feature (verification needs only envelope), removing ic-agent and a second ic_cose stack from the server build. handler::anda_engine now takes the local RPCRequestBody extractor instead of ContentWithSHA3<RPCRequest>; content-type selection and 415/400/413 rejections are unchanged.

Fixed β€” anda_engine

  • 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_call checks caller visibility before reporting whether a tool exists, matching agent_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.

Changed β€” anda_engine

  • Gemini FunctionDeclaration adds parameters_json_schema; callers constructing struct literals must initialize it. The existing parameters field remains available for OpenAPI schemas. Responses StreamEvent adds an Error variant.
  • Caller-free SubSessions::get_session returns None for ambiguous IDs; use caller-scoped lookup/status methods for user-facing access. Progress callback IDs now match the existing agent:session start/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 carries chat_history; use chat_history(). Handoff clones the request once and moves discovery and allowed-callable state into the new runner.
  • Add Client::new_with_client for the Anthropic, OpenAI, and Gemini adapters and CompletionModel::with_max_output for Anthropic and Gemini; ModelConfig no 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 RemoteEngines resolution helpers.
  • Cache parsed SKILL.md files by size and modification time so name scans skip unchanged files while keeping directory priority and ambiguity checks.
  • Drop async_trait from Management and avoid redundant clones of subagent resources, resource metadata, conversation documents, and context state.

Fixed β€” anda_core

  • Cache/store helpers use the same canonical lowercase path for both layers, and initialization propagates read failures other than typed NotFound errors. 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 Default context.
  • 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_rpc errors report the endpoint as given and path as {canister}/{method} for encoding, transport, response, and decoding failures; transport errors previously omitted the method.
  • PromptCommand lowercases command names with ASCII rules, consistent with command_argument.

Changed β€” anda_core

  • 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.
  • CacheStoreFeatures default methods return Send futures without async_trait, and the async-trait dependency is removed. cache_store_set no longer requires T: DeserializeOwned.
  • Document::from_text accepts impl Into<String>.
  • Decode message content from 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 provider definitions and functions.

Fixed β€” anda_web3_client

  • TeeClient signed HTTP and RPC calls reject malformed URLs, non-HTTP(S) schemes, and embedded userinfo before signing, matching the generic client. tee_attestation checks for TEE information before requesting attestation.
  • Share envelope signing and derivation-path construction in the generic client.

[0.16.0] β€” 2026-09-16

This release moves persistent memory to KIP 2.0, adds caller-isolated memory spaces, and aligns every workspace crate on version 0.16.0.

Changed β€” workspace

  • Workspace release alignment β€” anda_core, anda_engine, anda_engine_server, anda_web3_client, and anda_cli now share version 0.16.0; internal crate requirements have been raised to 0.16.

  • Published dependency upgrades β€” anda_db, anda_db_schema, and anda_db_tfs move from 0.11 to 0.13, ic_cose and ic_cose_types from 0.10 to 0.11, and anda_cloud_cdk from 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.

Changed β€” anda_engine

  • KIP 1.0 β†’ KIP 2.0 β€” Upgraded anda_kip and anda_cognitive_nexus from 0.11 to 0.13, the release that rewrites both crates for KIP 2.0.

  • The KIP tools take a model-facing argument type β€” MemoryManagement and MemoryReadonly previously took the wire Request as their Tool::Args, which made the model write the protocol tag, nest dry_run under options, and respect deny_unknown_fields. They now take the new KipArgs: a single command or an operations batch whose items may be bare command strings, with dry_run at the top level. KipArgs::into_request builds the envelope and is where the command / operations exclusion is rejected, since an envelope built from both would silently run only one of them. KipOperation is the string-or-object entry of that batch. Hosts that construct these tool calls themselves must migrate from Request to KipArgs; unknown fields are rejected instead of being silently ignored.

  • Tool definitions come from anda_kip β€” FUNCTION_DEFINITION is now deserialized from anda_kip::KIP_FUNCTION_DEFINITION instead 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 own READONLY_FUNCTION_DEFINITION (from KIP_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 no execution modes, so the shared schema had been advertising a batch mode the read path has no use for. MemoryReadonly::with_kip_function_definitions overrides 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 let execute_request parse 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 by op_id. Admission is decided on what each command parses to, never on the language label an operation declares, so no envelope field can talk a write past the boundary.

  • Read-only batch defaults β€” KipArgs::into_readonly_request supplies independent mode 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 own SelfModel Concept, not the host system.

Added β€” anda_engine

  • Multi-tenant persistent memory (Tenancy) β€” MemoryManagement::with_tenancy chooses 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::PerCallerSpace is 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 β€” KipArgs exposes 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::caller returns 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 of query / execute, for a host running commands on a tenant's behalf. describe_caller and get_or_init_caller now go through them, so a caller's own Person Concept lands in that caller's Space. session_for binds a supplied request to the caller's Space and returns the provisioned Session for executing it.

  • MemoryManagement::query / execute β€” Run one KIP command and get its result value back, rather than an envelope to unpack. query goes through the read-only gate, execute through the state-capable one. Both are the host acting on the default Space under every tenancy.

[0.15.1] β€” 2026-08-07

Added β€” anda_engine v0.15.1

  • SkillManager::set_skill_filter β€” A host can now install a SkillFilter predicate 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 during load before 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-response provider family β€” ModelConfig gains a new family that always routes through the OpenAI Responses API (completion_model_v2) with streaming and effort support, where the existing openai family decides by model name (gpt* β†’ Responses API, otherwise Chat Completions). A config that must pin the Responses API no longer depends on a gpt-prefixed model name.

Fixed β€” anda_engine v0.15.1

  • Reading a shadowed skill by name no longer fails as ambiguous β€” skills_manager resolved a name present in several configured skill directories by refusing it, while load had 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.

[0.15.0] β€” 2026-08-07

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.

Added β€” anda_engine v0.15.0

  • MCP 2026-07-28 β€” The MCP host now speaks the stateless revision alongside the older initialize-based ones. McpServerConfig::lifecycle picks how a server is approached: auto (default) probes server/discover and 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" β€” while discover and initialize pin one lifecycle. 2026-07-28 is 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-28 peers that advertise tools.listChanged the provider opens a subscriptions/listen stream 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/call no longer always returns a result. An input_required round (SEP-2322) carrying only requestState is 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 through McpServerConfig::tasks: the provider then polls tasks/get at the server's suggested interval (clamped to 250 ms–10 s) up to max_wait_secs, and cancels any task it walks away from.
  • MCP re-authorization β€” complete_authorization now 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. New disconnect_server drops 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; new clear_credentials also deletes the persisted grant, forcing McpAuthorizationRequired and a fresh consent. A grant the authorization server has revoked now also surfaces as McpAuthorizationRequired instead 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 in MCP_INTEGRATION.md.
  • Skill execution modes β€” Skills now run inline by default: the skills_manager tool 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 with execution: subagent (or metadata.execution: subagent) in its frontmatter, exposing it as an SA_<agent_name> worker for long-running, parallelisable, or context-hungry procedures. The tool response now reports execution, a callable name for subagent skills, and base_dir for resolving bundled files. Skills that previously ran as subagents must declare the execution mode to keep that behavior.
  • resource-tags for 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::Conversations is its AndaDB adapter; AndaDB field encoding no longer crosses into the subagent layer, and SubAgentConversationRecorder::with_store accepts 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 identical Args/Output share one hook slot; this is how a hook identifies which one invoked it.

Changed β€” anda_core v0.15.0 (breaking)

  • Registries own their invariant β€” The set map on ToolSet, AgentSet, and ToolProviderSet is 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 owned IntoIterator yielding entries in name order. EngineBuilder::register_tools / register_tool_providers / register_agents now merge through add_dyn instead of re-implementing the duplicate check.
  • CacheFeatures::cache_raw_iter removed β€” 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 internal CacheService.
  • Dead text-decoding entry points removed β€” text_from and utf8_text_from (unused owned-Vec variants) are gone, and utf8_text_from_bytes is now private (it equals text_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, and windows_code_page_encoding. text_from_bytes is 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.

Changed β€” anda_core v0.15.0

  • model split into cohesive submodules β€” model.rs now 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 to model::content, prompt documents to model::document, and text decoding to model::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 ToolSet and AgentSet through one internal registry module instead of two hand-copied implementations.
  • Documents implements IntoIterator β€” Consuming iteration over the contained documents; CompletionRequest::append_documents now uses it instead of reaching into private fields.
  • Shared test fixtures β€” The two duplicated ~300-line mock contexts in agent.rs and tool.rs tests are now one MockContext in an internal test-support module, and the twin mock-coverage tests merged into one.

Changed β€” anda_engine v0.15.0 (breaking)

  • Provider seam unified on CompletionFeaturesDyn β€” The delegating anda_core::CompletionFeatures impls on the Anthropic and Gemini CompletionModels are removed (they ignored resources and merely forwarded; OpenAI's models never had them). Code that called the models through that trait should call CompletionFeaturesDyn::completion instead.
  • Memory internals no longer public β€” The Arc<Collection> fields on memory::Conversations and memory::MemoryManagement are now private, so the storage abstraction can no longer be bypassed; use the methods, or the new ConversationRecords port (below).

Changed β€” anda_engine v0.15.0

  • One completion driver for all providers β€” The four provider adapters (Anthropic, Gemini, OpenAI Chat, OpenAI Responses) now share a single drive_completion algorithm behind an internal WireFormat seam; 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 β€” CompletionFeaturesDyn gains prune_unanswered_tool_calls / prune_tool_interactions with 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 same workspace/workspaces request 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 into context/agent.rs, context/runner.rs, and shared test fixtures; extension/mcp.rs (3.6k lines) into mcp/auth.rs (OAuth protocol), mcp/session.rs (transports and lifecycle), and mcp/router.rs (name mapping and call rounds); the background-task registry moves from hook.rs to a new background module. All previous public paths keep working through re-exports.
  • Single engine assembly path β€” EngineBuilder::build, ::empty, and ::mock_ctx share one assembly routine, removing three hand-synced copies. mock_ctx is now documented as the supported way for downstream agent and tool authors to obtain an AgentCtx in 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)) and extension::hooked_call (cancellation gate plus DynToolHook before/after wiring), replacing ten hand-written json! 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_api flattens an internally tagged enum that schemars would render as anyOf). Observable differences:
    • Every tool now rejects a call whose context is already cancelled (previously only search_file and the shell background path honored the token); the fetch, skills-manager, and memory tools gain hook support (new FetchToolHook / SkillToolHook aliases; the two KIP tools share the DynToolHook<Request, Response> slot since hooks are keyed by argument/output types β€” a hook tells them apart through the new BaseCtx::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 Ok with the same typed output, but are now flagged with is_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 (default values from serde defaults, minimum: 0 on unsigned integers, descriptions on nested item objects); property shapes, enums, and required lists are unchanged.
  • allowed-tools is now an upper bound β€” A skill that declares allowed-tools is 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.

Fixed β€” anda_engine v0.15.0

  • Rustdoc builds clean again β€” Intra-doc links in extension::mcp::auth and the extension module docs were left unresolved by the MCP/hook restructuring and failed RUSTDOCFLAGS="-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 only failed_reason survived. The Chat adapter now matches the Anthropic and Gemini adapters: the message is preserved in both raw_history and chat_history, while the extracted content/thoughts/tool_calls remain gated on success.
  • Interrupted tool calls no longer strand an OpenAI Responses reasoning item β€” 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 in raw_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_history belongs to the model that produced it: replaying one provider's native message JSON (OpenAI input_text parts, 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 drops raw_history and replays the provider-neutral chat_history.

[0.14.5] β€” 2026-07-31

Added β€” anda_cli v0.14.5

  • Explicit remote HTTP opt-in β€” New global --allow-http flag is required before the CLI sends signed requests to a non-loopback http:// 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, and anda_kip to 0.11. Conversation pagination now uses the database's newest-first ID query while preserving stable newest-first results across pages.

Changed β€” anda_engine v0.14.5

  • 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.

Fixed β€” anda_engine v0.14.5

  • Workspace-bound filesystem and shell tools β€” Caller-controlled workspace/workspaces metadata 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, and edit_file rejects a replacement result exceeding the file-size limit before allocation.
  • Conversation resource authorization β€” get_resource_content now 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_reason is 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 Debug output.
  • Remote engine initialization β€” EngineBuilder::mock_ctx now registers configured remote engines just as build does, so remote tools and agents are available in mock contexts.

Fixed β€” anda_engine_server v0.14.5

  • 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.

Fixed β€” anda_web3_client v0.14.5

  • 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.

[0.14.4] β€” 2026-07-17

Fixed β€” anda_engine v0.14.4

  • Anthropic structured output β€” output_schema was silently dropped for Anthropic models; now mapped to output_config.format with json_schema type, so structured output requests work across all three model families.

Changed β€” anda_engine v0.14.4

  • SSE deserialization zero-copy β€” Wire enums in Anthropic, OpenAI, and Gemini type layers now deserialize by reference (&str) instead of cloning the buffered Value, 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_output are filled in a single pass.
  • Shared model helpers β€” null_default, resolve_endpoint, and string_enum_serde! (now with input aliases) hoisted to model.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.

[0.14.3] β€” 2026-07-16

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, and anda_kip to 0.10. No API changes required in this workspace.

[0.14.2] β€” 2026-07-13

Added β€” anda_engine v0.14.2

  • MCP OAuth 2.1 authorization β€” McpOAuthConfig supports 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 β€” McpCredentialStore trait with load/save/clear lets applications back OAuth tokens with an encrypted store. InMemoryMcpCredentialStore is the default for development.
  • McpAuthorizationRequired error β€” 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 + auth mixing β€” McpStreamableHttpTransport rejects configs that set both.

Changed β€” anda_engine v0.14.2

  • rmcp 1.7β†’2.2 β€” Upgraded with the auth feature, replacing the static bearer-token path with AuthClient-based transport when McpOAuthConfig is present. Transport config split into base_transport_config (for auth client injection) and transport_config (static bearer, preserved for backward compat).
  • list_roots removed β€” The deprecated list_roots client handler is removed to match rmcp 2.x.

Added β€” anda_core v0.14.2

  • 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.
  • RemoteError error variant β€” Split from ResultError so callers can distinguish transport-level decode failures from application-level remote errors.
  • Documents closing-tag injection guard β€” Documents::Display neutralizes literal </tag> delimiters inside untrusted attachment content (case-insensitive), preventing document content from closing the block early.

Added β€” anda_engine v0.14.2 (continued)

  • Subagent execution-time tool allowlist β€” CompletionRunner::set_allowed_callables enforces 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-Length is pre-checked before the first byte.
  • ModelConfig api_key redacted from Debug β€” Custom Debug impl replaces the api_key field with [REDACTED] so a {:?} log line never leaks a credential.
  • Export name validation β€” EngineBuilder::check_exports rejects 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-context cache_get_with calls (e.g. dynamic remote-engine resolution) hit memory instead of always falling through to the store.

Changed β€” anda_engine v0.14.2 (continued)

  • RemoteTool / RemoteAgent cleanup β€” Removed the stale engine: Principal field; the target engine is resolved from endpoint at call time. RemoteAgent no longer lowercases a caller-provided name (the caller is expected to supply an already-valid lowercase name, consistent with RemoteTool).
  • Resource selection fixed for remote tools/agents β€” select_tool_resources / select_agent_resources now 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_with and ctx_with_base now enforce anonymous/private/protected rules (previously only agent_run and tool_call did). ctx_with_base also validates the agent name before creating the context.
  • Anonymous principal excluded from management β€” is_controller and is_manager reject the anonymous principal even when the engine was built without a Web3 identity and controller defaults to anonymous.
  • Agent/tool end hooks always paired with start β€” agent_run and tool_call now invoke on_agent_end / on_tool_end on the failure path (with a placeholder output) so hooks that track leases (e.g. SingleThreadHook) release their accounting.
  • Challenge endorsement guard β€” Engine::challenge validates that the request's AgentInfo matches 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_list strips the context namespace from prefix/offset so a returned ObjectMeta.location can be fed back as pagination offset on a non-root context without a doubled namespace.
  • Cache get_with preserves error source chain β€” CacheInitError wraps 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 with warn! instead of being swallowed silently.
  • OpenAI Chat Completions β€” response_format.json_schema now wraps the schema in the required {name, schema, strict} envelope; streaming requests request stream_options.include_usage for billing tracking; CompletionResponse populates the model field.
  • Gemini β€” tool_choice_required is honored via FunctionCallingMode::Any; tool_use_prompt_token_count moved from output_tokens to input_tokens (it is an input-side count, distinct from prompt_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's JSON.parse(buf || \"{}\") guard.

Fixed β€” anda_engine v0.14.2

  • 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 β€” handoff now carries allowed_callables into 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_request now 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 a ToolCall with 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_conversations now 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_count was incorrectly added to output_tokens instead of input_tokens, inflating the output count and undercounting input.

Changed β€” anda_core v0.14.2

  • Serde buffering replaces serde_json::Value for ContentPart β€” 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_lowercase and path_join no longer double-encode % in already-encoded object-store keys. Re-joining a namespace with a store_list key is idempotent.
  • Definitions/functions deduplicated by lowercase name β€” AgentSet and ToolSet suppress duplicate schemas when the same tool/agent is requested multiple times (some model providers reject repeated definition names).
  • /ping with arguments accepted β€” /ping now resolves to Ping instead of being treated as an unknown command; bare / and / arg are plain prompts rather than empty commands.
  • Blank failed_reason treated as success β€” An all-whitespace failure reason is normalized to None in AgentOutput::into_tool_output.
  • MCP secrets redacted from Debug β€” Custom Debug impls on McpStreamableHttpTransport and OAuthClientCredentialsConfig replace 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 futures and serde_bytes from anda_core production dependencies (futures kept for dev-dependencies).

Fixed β€” anda_core v0.14.2

  • % double-encoding in object-store paths β€” From<String> re-encodes % to %25, so path_lowercase and path_join switched to Path::parse / Path::from_iter to preserve already-encoded segments.
  • cache_store_delete ordering β€” Store is now deleted before cache to prevent a concurrent cache_store_get from repopulating a ghost cache entry that survives the delete.
  • Inline data token estimation β€” Switched from (len + 3) / 4 to len.div_ceil(3) to match base64 expansion ratio (~4/3 chars per byte, not 3/4).

Added β€” anda_engine_server v0.14.2

  • Signed envelope digest required on RPC β€” verify_user now rejects a SignedEnvelope that omits its committed digest on 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 /information and /agents/{id}) that cannot be enumerated exactly with exempt_path.

Changed β€” anda_engine_server v0.14.2

  • 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 and with_origin method removed (no consumer used it).
  • Removed unused public verify_cwt β€” Only the internal verify_cwt_token is needed.
  • Decode error hygiene β€” Param decode failures now use Display (not Debug) so the client sees the parser's error message without the raw request bytes.

Added β€” anda_web3_client v0.14.2

  • URL smuggling guard β€” check_url now parses with reqwest::Url instead of a string-prefix check, rejecting non-http(s) schemes (file, ftp, data, ws), bare strings, and URLs with no host. Previously a file:///etc/passwd-style target would pass the prefix guard and attempt a connection.
  • All-zero root secret warning β€” ClientBuilder::build logs a warn! when the default all-zero secret is used; the derived identity and all sub-keys are public and predictable.
  • Identity load improvements β€” load_identity detects existing file paths before falling back to hex decoding, so a PEM file with Secp256k1Identity errors now surfaces the real parse failure instead of being misinterpreted as a hex string. identity_from_pem no longer masks the Ed25519 parse error when a Secp256k1 parse fails.

Changed β€” anda_web3_client v0.14.2

  • Root secret zeroized β€” root_secret field wrapped in Zeroizing<[u8; 48]>; the long-lived copy is wiped from memory on Drop, reducing the exposure window.
  • Dependencies β€” Added zeroize to workspace dependencies; log and zeroize are now optional dependencies gated behind the client feature.
  • Query-signature verification (clarified, not changed) β€” Documented that the default Agent does 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 own Agent via with_agent to 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).

[0.14.0] β€” 2026-07-05

Removed β€” anda 0.14.0

  • CanisterCaller from BaseContext β€” The CanisterCaller bound is removed from BaseContext. Runtimes requiring canister access must implement the trait separately on their context type. All built-in impls (AgentCtx, BaseCtx) and the MockCanisterCaller test helper are removed.
  • Canister methods from Web3ClientFeatures β€” canister_query_raw and canister_update_raw are removed from the Web3 client trait surface. Runtimes needing raw canister access should use their own client directly.
  • Web3SDK enum and Web3Client wrapper β€” Web3SDK is now a plain struct wrapping Arc<dyn Web3ClientFeatures> instead of a Tee/Web3 enum. All match-based dispatch is flattened to direct trait-object calls.

Added β€” anda 0.14.0

  • Per-task background cancellation β€” BackgroundHandle + BackgroundTaskControls hook 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() on Web3ClientFeatures β€” New trait method (default Ok(None)) so TEE-backed clients can attach attestation evidence. Engine's challenge_response now uses a single unified path for both TEE and non-TEE flows.
  • anda_web3_client feature flags β€” client (non-TEE, ic-agent + local key derivation), tee (TEE gateway), full (both). Default build pulls neither ic-agent nor ic_tee_* crates.
  • crypto + tee modules in anda_web3_client β€” Deterministic key derivation ported from ic_tee_gateway_sdk::crypto (byte-for-byte identical), and TeeClient adapting ic_tee_gateway_sdk to the engine's Web3ClientFeatures trait.

Changed β€” anda 0.14.0

  • Dependency upgrades: ic-agent 0.47β†’0.48, ic_auth_types 0.9β†’0.10, ic_auth_verifier 0.9β†’0.10; new: ic-ed25519 0.6, ic-secp256k1 0.3.
  • Engine dependency diet β€” Removed ic_cose, ic_tee_cdk, ic_tee_gateway_sdk from anda_engine; ic_auth_verifier feature reduced from full to envelope.
  • rand_bytes self-implemented β€” Uses rand::fill directly instead of re-exporting ic_cose::rand_bytes, eliminating the last ic_cose dependency from anda_engine.

Fixed β€” anda 0.14.0

  • Base64 blob serialization β€” Test assertions updated to match b64: prefix format for inline data and resource blob encoding.

[0.14.1] β€” 2026-07-05

Changed β€” anda_engine v0.14.1

  • Background handle typed payload β€” BackgroundHandle now carries a typed Arc<dyn Any + Send + Sync> payload via with_data()/data() methods, replacing the parallel background_tasks map in SubSession with data stored directly on the handle. created_at and elapsed_ms() provide lifecycle observability on the handle itself.
  • BackgroundTaskControls ergonomics β€” Internal Mutex upgraded to RwLock; new get() / 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.

[0.13.15] β€” 2026-07-05

Added β€” anda_engine v0.13.15

  • 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.

Changed β€” anda_engine v0.13.15

  • 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_SIZE are now rejected at put time with a clear client error.

Fixed β€” anda_engine v0.13.15

  • 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.

[0.13.14] β€” 2026-06-29

Added β€” anda_engine v0.13.14

  • 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.

[0.13.13] β€” 2026-06-27

Fixed β€” anda_engine v0.13.13

  • 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.

[0.13.11] β€” 2026-06-25

Fixed β€” anda_engine v0.13.11

  • 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::Minimal to low reasoning effort instead of sending the unsupported minimal value.

[0.13.10] β€” 2026-06-25

Added β€” anda_core v0.13.10

  • 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.

Added β€” anda_engine v0.13.10

  • 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.

Fixed β€” anda_engine v0.13.10

  • MCP peer metadata compatibility β€” MCP tool discovery now handles the current rmcp peer-info return shape when capturing server title, description, and instructions for capability groups.

[0.13.9] β€” 2026-06-20

Fixed β€” anda_engine v0.13.9

  • 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.

[0.13.8] β€” 2026-06-20

Added β€” anda_engine v0.13.8

  • 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.

Changed β€” anda_engine v0.13.8

  • 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.

[0.13.7] β€” 2026-06-20

Added β€” anda_core v0.13.7

  • Agent capability groups β€” Agents can now declare ToolGroupInfo metadata so related agent callables are discoverable as coherent bundles alongside tool and provider groups.

Added β€” anda_engine v0.13.7

  • Agent group discovery β€” Discovery helpers now include grouped agents in tools_groups / tools_select outputs, preserving bundle instructions and sibling agent member lists when a grouped agent is selected.

Changed β€” anda_engine v0.13.7

  • 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.

[0.13.6] β€” 2026-06-18

Added β€” anda_core v0.13.6

  • Tool capability groups β€” Tools and dynamic providers can now expose ToolGroupInfo / ToolGroup metadata so related callables are discoverable as bundles without changing model-provider function schemas.

Added β€” anda_engine v0.13.6

  • Tool group discovery helpers β€” Added tools_groups plus tools_select { group } expansion and group annotations in tools_search / tools_select outputs, 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 instructions from the initialize handshake and surface each server as one discovery-layer tool group.
  • Subagent live status polling β€” Added read-only /status session polling and manager-level status catalog 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.

Fixed β€” anda_engine v0.13.6

  • Tool group discovery normalization β€” tools_groups / tools_select now 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.

[0.13.5] β€” 2026-06-18

Added β€” anda_core v0.13.5

  • Dynamic tool provider contract β€” Added ToolProvider, ToolProviderSet, and borrowed BoxFut support so runtimes can expose tools discovered at runtime while keeping static Tool/ToolSet behavior intact.

Added β€” anda_engine v0.13.5

  • 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 by rmcp with stdio and Streamable HTTP transports, tool allow/deny filters, legal Anda tool-name mapping, dirty refresh on tools/list_changed, audited tool outputs, and explicit exclusion of deprecated Roots/Sampling/Logging control capabilities.

Fixed β€” anda_engine v0.13.5

  • 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.

[0.13.4] β€” 2026-06-17

Fixed β€” anda_engine v0.13.4

  • 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.

[0.13.3] β€” 2026-06-15

Changed β€” anda_core v0.13.3

  • Slash command argument parsing β€” Added PromptCommand::command_argument() so command handlers can consistently extract the user-provided text after a slash command prefix.

Changed β€” anda_engine v0.13.3

  • 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.

[0.13.2] β€” 2026-06-14

Added β€” anda_engine v0.13.2

  • Runtime model registry replacement β€” Added Models::replace to atomically replace a model registry from another Models instance, enabling callers to reload model configuration without preserving stale labels from the previous registry.

[0.13.1] β€” 2026-06-14

Changed β€” anda_engine v0.13.1

  • Subagent session lifecycle controls β€” Subagent definitions and the manager tool schema now support an idle_timeout setting 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.

Fixed β€” anda_engine v0.13.1

  • Native shell environment injection β€” Restored the native shell runtime identity so safe host environment variables such as PATH are 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.

[0.13.0] β€” 2026-06-13

Changed

  • Workspace release and dependency alignment β€” Bumped the Anda workspace crates to 0.13.0, aligned internal crate dependencies on the 0.13 series, and upgraded the ICP/TEE/Anda data stack to anda_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, and ic_tee_* 0.7.
  • CBOR serialization backend β€” Replaced direct ciborium usage across the published workspace crates with cbor2, using canonical encoding for cache/store data, HTTP RPC payloads, signed Web3 requests, engine server RPC responses, notes, and subagent persistence.

Fixed β€” anda_engine v0.12.37

  • 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.

[0.12.36] β€” 2026-06-11

Fixed β€” anda_engine v0.12.36

  • 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-After hints, shared HTTP request timeout allows long reasoning calls, SSE [DONE] detection is line-anchored so generated text cannot truncate streams, and OpenAI Responses incomplete/failed/cancelled statuses surface as failure reasons while preserving partial content.

Changed β€” anda_engine_server v0.12.1

  • 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 the default engine 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.

Fixed β€” anda_web3_client v0.12.1

  • 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.

[0.12.35] β€” 2026-06-10

Fixed β€” anda_engine v0.12.35

  • Follow-up delivery during tool execution β€” CompletionRunner now 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.

[0.12.34] β€” 2026-06-10

Changed β€” anda_engine v0.12.34

  • Todo tool operation API β€” Replaced the todos/merge write contract with explicit op=read|set|update and items parameters. set replaces the list, update patches only changed ids, empty ids are ignored instead of materialized, and write calls now return summary counts while read returns 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|remove writes with stable-id op=read|set|upsert|delete and items parameters. Notes now persist in the notes_v2 store, writes return compact summary counts, and read/load operations return structured note items with usage summaries.

  • Deprecated extension cleanup β€” Removed the deprecated google and extractor extension modules from anda_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_agent and t_echo_tool on every platform.

Fixed β€” anda_engine v0.12.34

  • 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. reqwest decode 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, and effort, instead of collapsing any object with a prompt field down to the prompt string. This keeps asynchronous/session subagent calls working while preserving legacy plain-string and single-prompt agent behavior.

[0.12.33] β€” 2026-06-09

Changed β€” anda_engine v0.12.33

  • Adaptive discovered-tool request merging β€” CompletionRunner now tracks schemas returned by tools_search and tools_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.

Fixed β€” anda_engine v0.12.33

  • 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.

[0.12.32] β€” 2026-06-06

Changed β€” anda_core v0.12.8

  • 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.

Changed β€” anda_engine v0.12.32

  • Filesystem, shell, and skill tools handle platform-local text encodings β€” File reads, writes, shell output previews/progress, and SKILL.md loading 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.

Fixed β€” anda_engine v0.12.32

  • 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_manager now normalizes relative SKILL.md paths with / separators on Windows, matching the existing API output shape on Unix-like systems.

Fixed β€” anda_core v0.12.8

  • 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.

[0.12.31] β€” 2026-06-06

Changed β€” anda_engine v0.12.31

  • Model completion retry and retry metadata β€” Added a shared ModelError type 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-specific 529; exhausted retryable failures expose retryability, HTTP status, and Retry-After metadata for upper-layer delayed retry decisions. Streaming response read failures now use the same retryable transport error path, and Anthropic stream overloaded_error / rate_limit_error events are marked retryable.
  • Tool discovery wording clarified β€” Updated tools_search and tools_select descriptions to make schema discovery explicit: returned callable schemas live in tool-output context and should be called directly instead of being dynamically inserted into subsequent CompletionRequest::tools. Added regression coverage that selected tool schemas remain available as context without expanding the request tool list.

[0.12.30] β€” 2026-06-04

Changed β€” anda_core v0.12.7

  • Recursive strict JSON Schema normalization β€” normalize_strict_schema() now recursively applies additionalProperties: false, properties: {}, and required: [] defaults to all nested object schemas, not just the root. Previously propertyless nested objects (e.g. parameters without explicit properties) were left open, breaking strict-mode contracts for tools with nested object parameters. Added tests for nested propertyless object closure and recursive normalization.

Changed β€” anda_engine v0.12.30

  • Reasoning content merged into message text β€” ContentPart::Reasoning { text } in message_into() is now appended as ContentItem::Text in the current message content block instead of being emitted as a separate MessageItem::Reasoning. This simplifies the OpenRouter-facing message structure and avoids injecting standalone reasoning items that providers may reject or misinterpret.

[0.12.29] β€” 2026-06-04

Added β€” anda_engine v0.12.29

  • Multiple skill directories in SkillManager β€” Added SkillManager::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 displayed SKILL.md paths are made relative to the matching configured root. Added coverage for loading and reading skills across multiple directories.

Fixed β€” anda_engine v0.12.29

  • 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.

Changed β€” anda_engine v0.12.29

  • Hardened OpenAI-compatible response parsing β€” Chat Completions and Responses now tolerate DeepSeek-compatible variants: nullable usage detail objects and counters, unknown service_tier strings, reasoning aliases for reasoning content, tool_call/tool_use/function_call finish 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.

[0.12.28] β€” 2026-06-04

Changed β€” anda_engine v0.12.28

  • Removed fallback model support β€” Removed the fallback_model registry slot from Models, the with_fallback_model() builder API, the fallback_model() accessor, fallback routing from get_model() and resolve(), and all fallback-related completion logic in CompletionRunner::step(). A fallback label 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_str with response.bytes().await + serde_json::from_slice across OpenAI, Anthropic, and Gemini completions. Error messages now use String::from_utf8_lossy for safer non-UTF-8 body display.
  • Improved subagent output lifecycle β€” Added with_session(), latest_output(), finalize_output(), and record_failed_output() helpers to SubSessionRunner for consistent session tracking and error reporting. Cancellation failures and compaction errors now properly record their output before returning errors. Fixed needs_compaction() panic on context_window == 0 by treating it as unlimited. Added 3 new tests for output lifecycle and edge cases.

Added β€” anda_core v0.12.6

  • utf8_text_from_bytes() and utf8_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 in ContentPart and Document conversions.
  • Refined blob-to-text conversion β€” ContentPart::TryFrom<Resource> and Document::From<&Resource> now use the new text-detection functions instead of naive String::from_utf8. Document conversion also uses ResourceRef to exclude blob from metadata and now correctly includes blob text content when detected as text.

[0.12.27] β€” 2026-06-01

Changed β€” anda_engine v0.12.27

  • ContentPart::Any handling unified across providers β€” Refactored the ContentPart::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-all Any variant so a round-trip through Any never 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.

Added β€” anda_engine v0.12.27

  • to_message_inputs_only_preserves_known_any_content_parts β€” New test in anda_engine/src/model/openai.rs asserts that ContentPart::Any JSON with a known image_url shape 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 in anda_engine/src/model/openai/types.rs covers both ContentItem and MessageItem deserialization paths: a known input_image stays in the user message content, a known web_search_call becomes its own MessageItem, and an unknown shape falls back to a text payload carrying the original JSON.
  • Anthropic ContentBlock::Any test coverage β€” Extended the existing content_part_into_preserves_anthropic_specific_variants test in anda_engine/src/model/anthropic/types.rs with three additional cases: raw text blocks round-trip as ContentBlock::Text, unknown type values fall back to text-wrapped JSON, and malformed tool_use shapes also fall back to text-wrapped JSON instead of erroring.
  • content_part_any_only_preserves_known_gemini_parts β€” New test in anda_engine/src/model/gemini/types.rs covers the same fallback contract for Gemini parts: raw text shapes become PartKind::Text, unknown shapes are wrapped as text, and malformed functionCall shapes fall back to text-wrapped JSON.

[0.12.26] β€” 2026-06-01

Changed β€” anda_engine v0.12.26

  • Models::resolve() simplified fallback chain β€” Removed the deprecated fallback_model() path and the arbitrary-first-model extraction from resolve(). 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 aligns resolve() with the well-tested get_model() behavior. Updated doc comment and test expectation accordingly.

[0.12.25] β€” 2026-05-31

Added β€” anda_core v0.12.5

  • ContentPart::any_from and any_into helpers β€” New typed constructors for ContentPart::Any: any_from::<T>(ty, val) builds a ContentPart::Any JSON value with a "type" field, and any_into::<T>(ty) deserializes it back, verifying the type tag and returning Err(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 in anda_core/src/model.rs validates round-trip serialization of Resource through ContentPart::Any, covering all fields including blob (base64), metadata, uri, and mime_type. Also verifies type mismatch returns Err and non-Resource input is rejected.

[0.12.25] β€” 2026-05-31

Added β€” anda_engine v0.12.25

  • Model-aware subagent manager descriptions β€” SubAgentManager now carries a models: Vec<String> field set via with_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.").
  • resources field in conversation storage β€” Conversation::to_fields() now persists the resources field alongside messages and artifacts. Previously, resources attached to a conversation were lost on save/load cycles.
  • ContentPart::Any Resource serde test β€” New test_content_part_any_supports_resource_serde validates round-trip serialization of Resource through ContentPart::Any, covering all fields including blob, metadata, uri, and mime_type.

Changed β€” anda_engine v0.12.25

  • Effort enum values: xhigh β†’ max β€” SubAgent and SubAgentManager function schemas now list "max" instead of "xhigh" in the effort enum, aligning with the ModelEffort rename in v0.12.24. Updated in three locations (subagent schema, manager schema, test assertion).
  • model_names() doc comment β€” Added documentation for the Models::model_names() method describing its return value.
  • ModelConfig.effort doc: xhigh β†’ max β€” Documentation string updated to reflect the renamed variant.

[0.12.24] β€” 2026-05-29

Added β€” anda_core v0.12.4

  • ModelEffort β€” provider-agnostic reasoning/thinking effort β€” New ModelEffort enum (minimal, low, medium, high, max) provides a single vocabulary for reasoning effort across all model providers. Includes as_str() and Display impl.
  • Per-request effort on CompletionRequest β€” CompletionRequest now carries an effort: Option<ModelEffort> field, enabling callers (including subagents) to select reasoning effort per completion rather than only at model-config time.

Added β€” anda_engine v0.12.24

  • Tool error propagation via is_error β€” ToolOutput and ContentPart::ToolOutput now carry is_error: Option<bool>. Agent runner sets is_error: Some(true) on tool/agent execution failures and tool-not-found paths. Memory tools signal Nexus errors through the same flag. Anthropic passes is_error through to ToolResult; Gemini routes errors to FunctionResponseValue.error instead of the normal output field.
  • Subagent catalog, operation routing, and runtime state preservation β€” SubAgentManager gains operation field ("upsert" or "list"), catalog() method returning all registered subagents with metadata, and preserve_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 ModelEffort with per-request application β€” ModelEffort (promoted from engine to core) maps to each provider's native reasoning level: Anthropic OutputEffort (saturates at Max), Gemini ThinkingLevel (saturates at High), OpenAI ReasoningEffort (now includes Max variant), and OpenAI Responses v2 Reasoning. All three providers now apply req.effort during completion(), not only model-config defaults.
  • Subagent model and effort selection β€” SubAgent and SubAgentArgs gain model: String and effort: 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. Both SubAgent and SubAgentManager function schemas expose model and effort parameters.
  • Model label resolution β€” AgentCtx now resolves models by label via models.resolve(label) instead of the previous name-only lookup. CompletionRunner gains set_model() and set_effort() methods for mid-run model/effort switching.
  • Subagent tests for model/effort selection β€” New subagent_run_allows_model_and_effort_selection test verifies end-to-end flow: subagent receives a model label, the correct CompletionRequest reaches the provider with the expected model and effort. New RecordingRequestCompleter mock captures CompletionRequest for assertion. All existing tests updated for new fields.

Changed β€” anda_engine v0.12.24

  • 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.

[0.12.23] β€” 2026-05-27

Changed β€” anda_engine v0.12.23

  • Shell foreground commands auto-move to background after 42 seconds β€” Previously native foreground commands were waited on with wait_with_output(), and if they exceeded SHELL_TIMEOUT_SECS (180s) the tool would return a timeout error. Now execute_command uses tokio::select! with SHELL_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. The RunningProcess struct now bundles child/stdout/stderr/readers immediately after spawn, and extracted finalize_process_output() is shared between the foreground-completed and background-completed paths. Added test execute_auto_moves_long_running_foreground_to_background verifying the auto-transition and hook delivery.
  • Comprehensive sub-session compaction tests β€” New mock infrastructure (UsageCompleter, RecordingCompactionCompleter, RecordingAgentHook, request_text helper) and three new tests: needs_compaction_respects_usage_threshold verifies compaction triggers at exactly 100,000 input tokens; needs_compaction_triggers_at_turn_limit verifies compaction after MAX_TURNS_TO_COMPACT turns with unbound runner; subsession_runner_compacts_context_and_continues_from_handoff exercises 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.

[0.12.21] β€” 2026-05-25

Changed β€” anda_engine v0.12.21

  • SubAgentManager now isolates subagent storage under subagents/ prefix β€” Previously store_list was called with no prefix filter (loading everything from root), and subagents were stored directly at the root level. Now both load() and save() use store_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 by SubAgentManager::load.
  • Fixed ContentPart::FileData conversion for non-remote URIs β€” In Anthropic, Gemini, and OpenAI (both to_message_inputs and message_into), ContentPart::FileData now guards on file_uri starting with data: or https:// before converting to provider-native blocks (Image, Document, File, Video, Audio). Non-remote URIs like file:// 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.

[0.12.20] β€” 2026-05-21

Changed β€” anda_engine v0.12.20

  • Removed model field from AgentCtx β€” The cached default model instance is no longer stored on the context. Instead models.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_compaction now 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.

[0.12.19] β€” 2026-05-21

Fixed β€” anda_core v0.12.3, anda_engine v0.12.19

  • normalize_schema_object now correctly handles ["object", "null"] types β€” The is_object check previously only matched "type": "object", missing nullable object schemas like ["object", "null"]. New schema_type_contains_object() helper checks both string and array type values. Also, additionalProperties: false is now set for all object schemas, not only those with properties β€” so {"type": ["object", "null"]} without properties also gets it. Added test test_normalize_strict_schema_handles_nullable_objects covering both cases.
  • OpenAI Responses v2: filter empty Reasoning and ItemReference from history β€” normalize_message_item now returns None for Reasoning items with encrypted_content: None (empty reasoning blocks) and ItemReference items (by-ID references with no content). Previously these passed through as-is and could cause provider errors. raw_history_into now chains .filter_map(normalize_message_item) on the core Message history path too, which previously bypassed normalization.

Changed β€” anda_engine v0.12.19

  • output_schema in 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. Added deserialize_optional_json_schema custom deserializer that accepts JSON strings (parsed inline) or null. New tests: subagents_manager_definition_uses_strict_safe_output_schema, subagents_manager_args_accept_json_encoded_output_schema.

[0.12.18] β€” 2026-05-21

Changed β€” anda_engine v0.12.18

  • Stripped non-essential JSON Schema keywords from tool definitions β€” Removed default, uniqueItems, minLength, minLength (input/description), pattern, maxLength, minimum, and maximum from all tool schemas. These keywords add unnecessary strictness in a world where required already lists every field; some providers may also fail to handle them correctly in strict function calling mode. Affected tools:
    • tools_search / tools_select: removed default and uniqueItems
    • skills_manager: removed pattern, maxLength
    • list_conversations / search_conversations: removed default, minimum, maximum
    • subagents_manager / subagent: removed minLength, uniqueItems, pattern

[0.12.17] β€” 2026-05-21

Changed β€” anda_engine v0.12.17

  • Removed automatic max_output_tokens injection β€” CompletionRunner no longer applies self.model.max_output as a default max_output_tokens when 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 set req.max_output_tokens = Some(self.model.max_output) for any request without an explicit limit.

[0.12.16] β€” 2026-05-21

Added β€” anda_core v0.12.2, anda_engine v0.12.16

  • Strict schema normalization β€” normalize_strict_schema() in anda_core::json recursively normalizes JSON schemas for strict function calling: rewrites required to contain every key in properties, defaults additionalProperties to false, and traverses nested schemas (items, $defs, allOf/anyOf/oneOf, if/then/else, not, etc.). New FunctionDefinition::normalize_strict_parameters() method calls this normalization when strict: true. All tool definitions (ToolDefinition::from, CompletionRequest tool-building) now go through this normalization path β€” ensuring consistent strict schemas regardless of provider.
  • Model-level stream control β€” Added stream: bool field to ModelConfig and with_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's CompletionModel::new() now reads self.stream and sets the default request accordingly.
  • OpenAI Responses v2 defaults to streaming β€” The v2 model now defaults to stream: true with store: false for stateless requests. The original reasoning default parameter was removed.
  • OpenAI Responses v2 message history normalization β€” New raw_history_into() converts raw Json history into MessageItem values with proper role-aware normalization: core Message history is unwrapped via message_into(), legacy Responses MessageItem::Message records are normalized through normalize_message_item(), and unrecognized values pass through as Any. Assistant messages in history now use output_text content type (required by Responses API) rather than plain text. Content-type filtering distinguishes input (text) from output (output_text) based on role.
  • OpenAI Responses v2 stream aggregation β€” responses_response_from_stream_events() now handles ResponseOutputItemDone events: when the final response has an empty output array, output items collected from done events fill in via output_index ordering.

Changed β€” anda_core v0.12.2, anda_engine v0.12.16

  • All tool schemas now declare complete required arrays β€” Every tool definition schema lists all property keys in required with additionalProperties: false, including optional fields. Previously many tools omitted optional fields from required, 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).
  • anyOf avoidance in tool schemas β€” note tool now uses "type": ["string", "null"] with inline enum containing null instead of anyOf-based union schemas. todo and subagents_manager tools similarly use ["array", "null"] and ["object", "null"] type arrays. These patterns avoid anyOf which 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 type enum 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.95 default from CompletionModel::new().
  • Subagent session description β€” 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 required arrays and additionalProperties: false.

[0.12.15] β€” 2026-05-21

Added β€” anda_engine v0.12.15

  • 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-stream chunks. Added generic read_sse_json_events<T>() in model.rs with proper line-buffering, UTF-8 validation, and multi-line data: event concatenation.
    • Anthropic: response_from_stream_events() reconstructs CreateMessageResponse from 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 request stream=true.
    • Gemini: response_from_stream_chunks() aggregates :streamGenerateContent?alt=sse SSE 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() reconstructs CompletionResponse from chat.completion.chunk SSE 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 via ToolCallStreamBuilder with per-index BTreeMap tracking.
    • OpenAI Responses: responses_response_from_stream_events() extracts the most recent response from events β€” picks up response.created/in_progress/completed/failed/incomplete. Parses output after reconstruction.
  • All streaming backends set Accept: text/event-stream header and conditionally dispatch to streaming vs non-streaming code paths based on request configuration.
  • Gemini: stream request flag β€” Added stream: bool field to GenerateContentRequest (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).

[0.12.14] β€” 2026-05-21

Added β€” anda_engine v0.12.14

  • 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.

Changed β€” anda_engine v0.12.14

  • Subagent tags description refined β€” Added concrete examples (image, text, audio) to the tags field description in the subagents_manager tool schema.

[0.12.13] β€” 2026-05-19

Changed β€” anda_engine v0.12.13

  • 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 use r.model consistently.

[0.12.12] β€” 2026-05-17

Fixed β€” anda_engine v0.12.12

  • OpenAI: Multi-tool-output splitting β€” to_message_input refactored to to_message_inputs, now returning Vec<MessageInput>. When a single Message contains multiple ToolOutput content parts, each output is emitted as a separate tool-role message with its own tool_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 β†’ Message conversion rewritten. The old text() filter that discarded non-text parts is replaced with chat_completion_content_into_parts / chat_completion_content_part_into / file_data_content_part pipeline. Image URLs, input audio, files, video URLs, and refusals now survive the round-trip conversion as proper ContentPart variants instead of being silently dropped.
  • MessageInput now carries name β€” name: msg.name.clone() is propagated to MessageInput during conversion.

[0.12.11] β€” 2026-05-17

Fixed β€” anda_engine v0.12.11

  • implicit_context injection 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_choice compatibility β€” Skip setting tool_choice for models whose name starts with "deepseek", as DeepSeek's API does not support this parameter and returns errors when it is present.

[0.12.10] β€” 2026-05-17

Changed β€” anda_engine v0.12.10

  • implicit_context on CompletionRunner β€” new implicit_context(&mut self, message: Message) method stores a context message that is automatically injected into the next request's chat_history and 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 of content.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: model field fix β€” CompletionModel now explicitly sets r.model = self.model.clone() on each request, fixing a bug where the model field in the cloned default_request was not being updated to the current model selection.

[0.12.8] β€” 2026-05-16

Changed β€” anda_core v0.12.1

  • Resource β†’ ContentPart conversion now uses TryFrom with MIME detection β€” impl TryFrom<Resource> for ContentPart replaces the infallible From impl. Binary blobs now use infer2 to detect the actual MIME type from bytes instead of defaulting to application/octet-stream. Resources with neither blob nor URI return Err(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 covering AgentInput, ToolInput, PromptCommand, AgentOutput::into_tool_output, data URL round-trips, ContentPart::try_from(Resource) edge cases, RequestMeta, Usage::accumulate overflow, FunctionDefinition, Document/Documents, and Message deserialization.

Changed β€” anda_engine v0.12.8

  • Anthropic: Extended API surface β€” Full support for the latest Anthropic Messages API: SystemPrompt enum (string or content blocks), CacheControlEphemeral for prompt caching, OutputConfig with OutputEffort and JsonOutputFormat, structured StopDetails::Refusal, ToolChoice constructors (auto(), any(), tool()), ThinkingDisplay and ThinkingType::Adaptive/Disabled, extended ContentBlock variants (document, search_result, server_tool_use, web_search/fetch results, code execution results, container_upload), ToolResultContent as text-or-blocks, CitationsConfig/TextCitation, UsageServiceTier/CacheCreation/ServerToolUsage on Usage, Container in responses.
  • Gemini: Extended API surface β€” SafetySetting/HarmBlockThreshold, cached_content/service_tier/store on GenerateContentRequest, ModelStatus, GroundingAttribution/GroundingMetadata with rich chunk types (web, images, maps, retrieved context), LogprobsResult, UrlContextMetadata, SpeechConfig/VoiceConfig, ImageConfig/MediaResolution, Modality enum, response_json_schema fields, seed, enable_enhanced_civic_answers, extended FinishReason variants. SatisfyRating renamed to SafetyRating with backward-compatible alias. Fixed satefy_ratings typo with serde alias.
  • OpenAI: Full ChatCompletionRequest type β€” Structured request builder replacing ad-hoc JSON construction. Supports audio, 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 β€” ChatCompletionMessageContent supports text, content parts (text/image_url/input_audio/file/refusal), and null deserialization. Refusal detection from both legacy refusal field and content-block refusals. MessageOutput now uses ChatCompletionMessageContent and provides has_output()/has_refusal() helpers.
  • OpenAI: Custom tool support β€” ToolDefinition as enum with Function and Custom variants. CustomToolDefinition with text/grammar format. ToolCallOutput supports both function and custom call types. CustomToolCall with raw input string.
  • OpenAI: Tool calls extracted to tool_calls field β€” Assistant messages now serialize tool calls in a top-level tool_calls array alongside content, matching the OpenAI API shape.
  • OpenAI: Usage details β€” CompletionTokensDetails (reasoning_tokens, audio_tokens, accepted/rejected_prediction_tokens) and PromptTokensDetails (audio_tokens).
  • OpenAI: Media type routing β€” File/image/audio/video ContentPart items 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 β€” StreamEvent enum with 11 event types. MessageItem expanded with file_search_call, computer_call, web_search_call, tool_search, compaction, image_generation, code_interpreter, shell calls, apply_patch, MCP calls, custom tools. ToolDefinition expanded with file_search, computer, web_search, MCP, code_interpreter, image_generation, local_shell, shell, custom, namespace, tool_search, apply_patch. ContextManagement and ResponseConversation for conversation state.
  • Model routing β€” OpenAI models starting with gpt now use completion_model_v2 (Chat Completions API), while non-gpt models use the standard Responses API path.
  • SubAgent: From β†’ TryFrom migration β€” SubAgent resource-to-ContentPart conversion updated to use the new TryFrom impl.

[0.12.7] β€” 2026-05-15

Changed

  • Case-insensitive model label lookup β€” Models::get(), Models::contains(), and Models::resolve() now normalize labels with to_ascii_lowercase() before lookup. Labels are stored lowercase in inner_set. This means get("GPT-4") and get("gpt-4") resolve to the same model.
  • Model names auto-registered as labels β€” inner_set() now appends model_name.to_ascii_lowercase() to the label set. A model with model_name = "primary" is now findable via get("primary"), removing the need for manual label aliasing.

[0.12.6] β€” 2026-05-12

Removed

  • CompletionRunner tools_select auto-loading β€” the completion loop no longer parses tools_select results 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, and ToolSelectorCompleter (~280 lines of test infrastructure) removed alongside the auto-loading behavior they tested.

Changed

  • Relaxed ToolsSelect/ToolsSearch parameter constraints β€” minLength, minItems, and minimum constraints removed from JSON schemas. These validations now happen at the implementation level, giving models more flexibility in parameter usage.
  • Optimized select_requested_names_with_model serialization β€” new ToolItemRef struct serializes only name + description when passing candidates to the selector model, instead of full FunctionDefinition (which includes large parameter schemas). Reduces token usage.

Added

  • 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.rs utilities β€” rand_number, rfc3339_datetime, json_set_unix_ms_timestamp, json_convert_rfc3339_timestamp, and local_date_hour now have comprehensive tests.

[0.12.5] β€” 2026-05-11

Features

  • with_caller() for context cloning β€” BaseCtx::with_caller(caller) and AgentCtx::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.

Changed

  • 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.

[0.12.4] β€” 2026-05-11

Features

  • 2D terminal emulation for shell progress β€” TerminalProgressState upgraded 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.

Refactors

  • Plain progress mode is line-buffered β€” non-rewrite output accumulates until \n, then emits complete lines via completed_lines buffer. No more mid-line fragmentation in plain-text progress.

[0.12.3] β€” 2026-05-11

Features

  • Background shell progress hooks β€” on_background_progress() hook delivers incremental stdout/stderr every ~3 seconds while a background command runs. TerminalProgressState normalizes rewritten terminal lines (\r, \b, ANSI ESC[K) to their latest visible text, so the model sees clean output instead of raw control characters. UTF-8 boundary-safe chunking via complete_utf8_prefix_len() prevents splitting multi-byte sequences across progress deliveries.
  • insecure() mode for NativeRuntime β€” builder option to skip env_clear(), allowing the shell to inherit host environment variables.

Refactors

  • execute_command() extracted as public method on NativeRuntime β€” takes a std::process::Command directly, enabling non-shell invocations through the native runtime. build_shell_command() now returns std::process::Command (decoupled from tokio).
  • ToolsSearch::NAME and ToolsSelect::NAME added as pub const β€” used in name() instead of raw constants for cleaner code.

Breaking Changes

  • Executor::temp_dir() removed from the trait β€” no longer part of the public executor interface. NativeRuntime gains a temp_dir() builder method instead.
  • Native executor name() changed from "native_shell" to "shell".
  • tools_search removed from DEFAULT_SKILL_TOOLS β€” skill agents no longer receive tools_search by default.

Housekeeping

  • Remove deprecated tests β€” test modules removed from extractor.rs (106 lines) and google.rs (52 lines), both already marked #[deprecated] since 0.12.0.

[0.12.2] β€” 2026-05-09

Features

  • Multi-workspace file tools β€” ReadFileTool, EditFileTool, WriteFileTool, and SearchFileTool now accept workspaces: Vec<PathBuf> instead of a single workspace. Context meta workspace/workspaces fields take precedence over defaults, with automatic fallback. SearchFileTool iterates all workspaces and merges results. New with_workspaces() constructor for multi-default setups.

Improvements

  • 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_LIMIT 8 KB β†’ 16 KB, NOTE_MATCH_PREVIEW_LIMIT 80 β†’ 120 chars.

Fixes

  • Skill cache now correctly updates on successful load (write().entry().insert_entry).

Deprecations

  • google, extractor modules marked #[deprecated(since = "0.12.0")] β€” will be removed in a future release.

[0.12.1] β€” 2026-05-08

Refactors

  • Unified ToolsOutput β€” ToolsSearchOutput and ToolsSelectOutput merged into single ToolsOutput struct with Vec<FunctionDefinition> (full tool definitions, not just name+description), so the model can invoke tools immediately after tools_search. Intermediate ToolsSearchItem struct removed; rank_search_items returns names directly, definitions resolved at output boundary. Default limits: search 0β†’10, select 0β†’5 with explicit MAX caps.

Fixes

  • Agent failures as errors β€” when an agent call fails, return a ToolOutput error instead of breaking the conversation, allowing the LLM to correct and recover.

Breaking Changes

  • workspace field removed from ExecArgs β€” shell tool no longer accepts a workspace parameter; commands always execute in the runtime's workspace directory. join_current_dir helper removed.

Fixes

  • Shell spawn failures now return structured ExecOutput errors instead of propagating as Rust errors, so the model can see and respond to command execution failures.

Internal

  • Native shell runtime renamed from "native" to "native_shell"; background shell tasks now return an immediate task_id output so callers can track long-running commands.

[0.12.0] β€” 2026-05-07

Breaking Changes

  • SubAgent extracted as top-level module β€” SubAgent/SubAgentSet/SubAgentManager moved from context::subagent to top-level crate::subagent. SubAgentManager is now an Agent (via AgentSet) instead of a Tool, enabling proper AgentHook callbacks for subagent lifecycle. Session-based background execution with SubSessions tracking, idle timeout (10 min), background task wait (1 hr), and automatic compaction at 81+ turns.
  • SkillManager reduced to read-only inspector β€” SkillManager no longer supports create/patch/edit/delete/write_file/remove_file disk management operations. Skills are now created and updated by editing files directly on disk via shell or file tools. SkillArgs simplified to {name} only (with deny_unknown_fields); output is now SkillContentOutput.
  • Rename work_dir β†’ workspace across all tools, runtimes, and context metadata. Shell output field changed from work_dir to workspace.
  • Drop legacy DeepSeek client β€” replaced by multi-label model support (labels: Vec<String> on ModelConfig).
  • Remove sandbox feature and boxlite dependency from engine crate. Shell extension now always uses NativeRuntime.
  • Rename Dyn traits β€” AgentDyn β†’ DynAgent, ToolDyn β†’ DynTool; internal storage switched from Box<dyn ...> to Arc<dyn ...>.
  • Rename step β†’ turns in CompletionRunner API.
  • Remove CompletionHook β€” superseded by AgentHook with background support.
  • Remove prune_raw_history pipeline β€” trait method, 4 provider impls, CompletionRunner method, and pruned_placeholder helper all deprecated since 0.11.0 have been removed.
  • WorkDir renamed to Workspace in ExecArgs, ExecOutput, and context metadata.
  • MAX_OUTPUT_BYTES increased from 128 KB to 256 KB.
  • ModelConfig::model() now returns Result instead of silently producing a not_implemented model.
  • Remove deprecated evaluate_tokens (anda_core) and build_model (anda_engine) β€” superseded by estimate_tokens and model().

Features

  • Downcast support β€” as_any()/into_any() on DynAgent and DynTool traits with downcast_ref/downcast convenience methods for type-safe concrete type recovery.
  • PromptCommand enum with From<String> for slash-command parsing (/ping, /command ..., plain text).
  • AgentOutput improvements β€” thinking β†’ thoughts rename; new session field; PartialAgentOutput struct; into_tool_output() method.
  • CustomEnv with auto-inject β€” shell tool environment variables can be marked as default for 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 models map, 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 β€” AgentOutput and CompletionRunner track per-tool Usage; ToolOutput.tools_usage field.
  • Multi-label model support β€” Models now supports HashMap<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_VARS whitelist 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 at AgentCtx::definitions().
  • CompletionRunner::unbound builder β€” enables unconstrained completion execution.
  • EngineRef for late binding β€” switch to Arc<Engine> throughout.
  • Per-agent storage and output_schema for SubAgent β€” refactored from single CBOR to individual files per agent.
  • Multiple steering/follow-up messages queue β€” Vec<String> and VecDeque<String> replace single Option<String>.
  • Batch conversation retrieval β€” batch_get_conversations for 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)).
  • Conversation extra field β€” extensible metadata for conversations.
  • Thoughts method on Message β€” extract reasoning content.
  • Idle status added to ConversationStatus.
  • CWT verification support in engine-server.

Refactors

  • 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_configs helper.
  • Remote dispatch β€” now checks function registration, prefers longest matching handle.
  • Native shell runtime β€” removed shell detection infrastructure; always uses sh on Unix, cmd.exe on Windows.
  • Defined prefix constants and centralized prefixing to outer layer.
  • BaseCtx passed as parameter instead of stored in SubAgentManager.
  • Tool calls deferred to next turn.
  • EngineBuilder::empty() made async for proper initialization.
  • Child contexts clone parent state instead of sharing Arc<RwLock>.
  • SkillFrontmatter metadata upgraded from BTreeMap<String, String> to BTreeMap<String, Json>.
  • Tool errors use JSON {"error": "..."} formatting instead of plain strings.

Fixes

  • Auto-fill max_output_tokens from 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_set fix β€” cache was deleted instead of updated after store_put.
  • null content in Message deserialization handled correctly.
  • select_resources preserves ordering with O(n) single-pass algorithm.
  • SO_REUSEADDR fallback for platforms without SO_REUSEPORT.
  • ToolsSelectOutput deserialization fixed for nested .content field.
  • All 4 model response parsers now consistently set output.thoughts.

Documentation

  • 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 Tool implementation example.

Internal / Chores

  • Upgrade infer β†’ infer2 (v0.21), boxlite β†’ 0.9 (crates.io).
  • Search file default limit set to 1000.
  • DEFAULT_SKILL_TOOLS expanded to include todo, tools_search, tools_select.
  • User name max length relaxed from 32 to 96 chars.
  • SkillFrontmatter gains extra field with serde(flatten) for forward compatibility.