This document describes the current anda_engine runtime architecture from the source code. It replaces the older deployment-first narrative that treated ICP, TEE, and IC-TEE as mandatory runtime blocks.
In the current engine, those integrations are optional backing capabilities. The practical center of the system is the Engine runtime: it validates callers, creates scoped contexts, dispatches agents and tools, routes model calls, handles tool-call loops, and exposes selected functions to callers.
Preview note: use markdown-viewer/markdown-viewer-extension to preview this document with the embedded HTML architecture diagram and PlantUML sequence diagram rendered.
Source map:
engine.rs: top-levelEngine,EngineBuilder, exported APIs, management checks, hooks, challenge signing.context/agent.rs:AgentCtx, local and subagent routing.context/runner.rs:CompletionRunner,CompletionStream, queued input and compaction.context/compaction.rs: agent-initiated compaction:SelfCompaction, the intrinsiccompact_contextcall, admission and context-usage notes.context/base.rs:BaseCtx, scoped state, cache, store, keys, HTTP, signed RPC, cancellation.context/tool.rs: built-in discovery agents:tools_groups,tools_search, andtools_select.model.rs:Modelslabel router, provider adapters, retry and streaming helpers.subagent.rs: reusable subagents, background sessions, compaction and handoff.memory.rs: conversation/resource storage and KIP/Cognitive Nexus tools.extension: built-in tool libraries such as filesystem, shell, fetch, skills, notes, todos, and memory.
TodoSession is a bounded, shared in-memory task list seeded on the runner's
parent context. Handoff preserves its active tasks in a separate bounded
neutral-history message. Optional NoteContextConfig loads an agent-scoped note
index once per runner/window, gated by the request offering the local note tool
and the callable allowlist. The index is request-only context, replayed after a
model switch but not added to neutral history. These extensions use existing
tool hooks and object storage; they do not introduce a separate event service.
See notes and tasks.
CLI, HTTP server, bot runtime, or embedded Rust application call the engine API.
agent_runtool_callinformationchallenge
Clients can discover exported functions and call them through signed RPC.
Completion APIs are reached only through registered model adapters.
Can be a TEE client, a Web3 client, or a not-implemented placeholder.
Fetch and other outbound calls use the context HTTP/signed-RPC traits.
AndaDB and Cognitive Nexus are used when memory tools are registered.
@startuml
title Anda Engine agent_run and completion loop
skinparam backgroundColor #FFFFFF
skinparam sequenceArrowColor #334155
skinparam sequenceLifeLineBorderColor #CBD5E1
skinparam sequenceParticipantBorderColor #CBD5E1
skinparam sequenceParticipantBackgroundColor #F8FAFC
skinparam sequenceGroupBorderColor #94A3B8
skinparam sequenceGroupBackgroundColor #F8FAFC
skinparam noteBackgroundColor #FFF7ED
skinparam noteBorderColor #FDBA74
actor Caller
participant "Host API\n(server / CLI / app)" as Host
participant "Engine" as Engine
participant "Management\n+ Hooks" as Guard
participant "AgentCtx\n+ BaseCtx" as Ctx
participant "Agent" as Agent
participant "CompletionRunner" as Runner
participant "Models\nlabel router" as Models
participant "Model adapter\nOpenAI / Anthropic / Gemini" as Model
participant "Tool / Agent\nregistries" as Registry
participant "Local Tool\nor Agent" as Local
participant "SubAgent\nsession runner" as SubSession
Caller -> Host : submit AgentInput
Host -> Engine : agent_run(caller, input)
Engine -> Engine : validate RequestMeta\nnormalize default agent
Engine -> Guard : check_visibility(caller)
Guard --> Engine : visibility accepted
Engine -> Ctx : ctx_with(caller, agent, label, meta)
Engine -> Guard : on_agent_start(ctx, agent)
Engine -> Agent : run(ctx, prompt, resources)
Agent -> Ctx : completion(req, resources)
Ctx -> Runner : completion_iter(req, resources)
loop until final output, failure, or cancellation
Runner -> Models : resolve(req.model or ctx.label)
Models --> Runner : Model
Runner -> Model : completion(CompletionRequest)
Model --> Runner : AgentOutput\ncontent, usage, raw_history, tool_calls
alt model returned tool_calls
Runner -> Registry : resolve each tool_call name\nlocal or SA_
par each resolved call
alt local tool
Runner -> Ctx : child_base(tool)
Ctx -> Local : Tool::call(BaseCtx, args, selected resources)
Local --> Runner : ToolOutput
else local agent or subagent without session
Runner -> Ctx : child(agent)
Ctx -> Local : Agent::run(AgentCtx, prompt, resources)
Local --> Runner : AgentOutput as ToolOutput
else subagent session mode
Runner -> Local : SubAgent::run(session args)
Local -> SubSession : claim session and spawn background runner
Local --> Runner : ack with session id
SubSession -> Guard : on_background_start
SubSession -> Runner : unbound completion loop
SubSession -> Guard : on_background_progress / on_background_end
end
end
Runner -> Runner : accumulate usage, artifacts, tools_usage\nappend tool outputs to next request
else no tool calls
Runner -> Runner : final_output()
end
opt steering or follow-up queued
Runner -> Runner : insert user content at safe boundary\nprune unanswered tool raw history if needed
end
end
Runner --> Agent : AgentOutput
Agent --> Engine : AgentOutput
Engine -> Guard : on_agent_end(ctx, agent, output)
Guard --> Engine : transformed output
Engine -> Engine : clear provider raw_history
Engine --> Host : AgentOutput
Host --> Caller : response
@endumlEngineis the public runtime boundary. It enforces exported agent/tool lists for non-manager callers and always exports the default agent.anda_engine_servervalidates registry keys against engine identities, authenticates RPC bodies, and releases encoded parameters before awaiting execution. Discovery negotiates JSON/CBOR throughAccept; caller-bearing responses are not cached. Public engine cards include current exported tool definitions. See the HTTP and authentication contracts.EngineBuilderstarts with in-memory storage, no implemented Web3 client, no external model, and built-in discovery/subagent control agents.AgentCtxis the main scheduling surface. It exposes local tools, dynamic tool providers, local agents, and subagents.CompletionRunneris iterative. A model turn can return tool calls; the runner executes them and feeds tool outputs into the next model turn. Long-running runners can compact oversized history into a continuation handoff and resume from that summary. Pending tool outputs are summarized with the history; pending request input, queued follow-up/steering, and the implicit context move to the replacement runner instead. A summary is committed only after validation, so failure leaves the original runner available for retry. When the compaction interrupted work the model had not finished (tool outputs folded into the summary) and nothing else is queued, the replacement runner resumes withCOMPACTION_CONTINUE_PROMPT. Streams deliver queued input before committing normal completion and end after a failed step.- Agent-initiated compaction (
set_self_compaction) splits the decision from the mechanism. The model decides: from usage notes the runner adds once usage passesnotice_percent, and from what the latest request still needs, it calls the runner-intrinsiccompact_contextwith a handoff it writes itself. The runner admits it: only as the sole call of its response and abovemin_percent; otherwise the reason goes back as a tool error. The host executes it: an admitted request makesneeds_compaction_withtrue, andhandoffuses the model's text instead of a summary turn, re-delivering the latest user input verbatim when the model compacted before acting on it. The call is intercepted rather than registered as aTool, because tools see onlyBaseCtx, whose state nested runners share. The usage note is a trailing request-history message, so it reaches the raw history but never the neutralchat_history. The forced 80% threshold and host overflow recovery remain the backstop. tools_groups,tools_search, andtools_selectare agents, not side channels.tools_groupsreturns a compact directory of visible capability bundles;tools_selectcan expand one group into schemas, and discovered schemas stay in tool-output context while repeated payloads are compacted from conversation context.BaseCtxcreates namespace-scoped child contexts. Agent paths usea_<agent>, tool paths uset_<tool>, and all store/cache operations are resolved under that path.Modelsroutes by label first and then falls back to the primary/default model. Provider-specific names stay inside adapter configuration.- Conversation history has two views, and the distinction is load-bearing.
chat_history(Vec<Message>ofContentPart) is the provider-neutral view that gets persisted and crosses the RPC boundary.raw_history(Vec<Json>) holds the provider's own message JSON verbatim and exists so per-turn opaque state — Anthropicthinking.signature, GeminithoughtSignature— survives a reasoning round without being modelled inContentPart. Each turn the runner appends the response'sraw_historyonto the request's and clearschat_history; every adapter sendsraw_historybefore the convertedchat_history. The engine clears it at the RPC boundary, so it is scoped to one in-process round. A resumed conversation replays fromchat_historyalone and legitimately has no provider intermediate state — adapters must tolerate that rather than emit a block the provider rejects. A runner withset_transient_inline_data(true)treats inline attachment bytes as task-scoped: they never enterchat_history, andCompletionFeaturesDyn::prune_inline_mediareplaces them inraw_historywith a text note when the runner goes idle or the task is stopped. - Completion adapters reject unfinished streams before exposing text or tool calls to the runner; the shared request retry policy handles these failures. Gemini preserves signed Part boundaries in native history while joining adjacent text fragments in the neutral view. OpenAI and Anthropic assign paired IDs to ID-less neutral tool histories before conversion, leaving existing IDs and native history unchanged. Provider parameter and schema checks happen before sending the request; see the adapter behavior.
SubAgentManagerturns persisted or temporarySubAgentdefinitions into callableSA_<name>agents. Long-running subagent sessions are keyed by caller principal, host-created root scope, and session ID. Runtime identities retain root/parent ownership; per-turn completion events are separate from session closure. Root-scoped admission and budgets are shared through context state. Optional idle checkpoints restore neutral history under current host capabilities; audit recording remains separate. See subagent lifecycle. They use hooks with consistent task IDs to push progress and final output; priority stop/cancel controls interrupt pending work.- Memory is an extension layer. Conversation/resource storage uses AndaDB collections, and persistent knowledge operations are exposed as KIP tools backed by Cognitive Nexus.
- Web3, TEE, ICP, and IC-COSE integrations are implementation choices behind
Web3SDK,HttpFeatures,KeysFeatures,CanisterCaller, orObjectStore. They are not mandatory architecture layers for the engine itself. anda_web3_clientsupplies optionalclientandteebackends. The generic client needs an explicit identity or root secret; identity-only clients cannot derive keys. Its default IC agent verifies queries, and an injected agent must share the client principal. Both backends useanda_core::cbor_rpcfor bounded signed-RPC responses while retaining their own signing mechanisms. See the client configuration.
extension::workspace exposes opt-in registration bundles. coding_tools combines
ShellCommandTool, ShellSessionTool, and ApplyPatchTool for the same local
workspace. Read/search/list operations remain shell commands in this bundle.
readonly_file_tools and file_tools support hosts that do not grant execution.
Existing ShellTool, filesystem tools and provider-neutral core contracts remain
available; no default engine export or management permission is broadened.
Session execution is an optional capability on shell::Executor. NativeRuntime
owns admission, scoped lookup, output buffers, a capped raw log, process deadlines,
and retention. Host-installed ShellSessionScope capabilities propagate through
context state; RPC metadata cannot create one. Lookups additionally verify engine,
caller and agent. Input and polling on one process are serialized; different
sessions can run concurrently. As with the legacy shell, hooks publish start,
progress and completion only for commands still running after the foreground
wait, with bounded hook waits so supervision stays responsive. Scoped
cancellation and runtime teardown stop processes. On Unix, session completion
terminates descendants remaining in the launched shell's process group before
draining output. The legacy shell path keeps its previous background lifecycle
for compatibility.
An optional immutable SandboxPolicy transforms both legacy and session launches
into platform-sandboxed commands. macOS uses Seatbelt and Linux uses bubblewrap;
unsupported or unavailable backends fail closed. Platform runtime files, declared
roots and networking are explicit host policy. Tool arguments cannot grant more
permissions. Application approval UX, secret configuration and remote execution
remain outside this extension. PTY allocation is opt-in on Unix; the native Windows
executor uses pipes, and Windows process isolation requires a custom executor.
Filesystem reads use opened, validated regular-file handles and bound actual bytes. Cooperating edit/write/patch calls share bounded lock stripes. Unix writes use a pinned directory descriptor for temporary creation and atomic rename; Windows pins directory handles and validates reparse points and link counts. Patch parsing, path resolution, exact matching, encoding, size limits and optional SHA-256 preconditions are validated before commit. All involved stripes are acquired in stable order. Each file replacement is atomic; multi-file operations can partially succeed and report which operations committed. Neither these locks nor optimistic version checks are a transaction against external editors or shell commands.
extension::skill::SkillManager owns a single published registry: immutable catalog
entries and their unambiguous delegated callables are replaced under one lock. Root/path
identities are distinct from frontmatter names and callable names. Host filters apply
before precedence and conflict resolution. Publication retains sessions only for the same
winning identity; renames, deletions and vanished roots remove old registrations.
A serialized refresh scans bounded directory trees, reads files through the shared
no-follow file access layer, parses frontmatter and optional agents/openai.yaml, then
publishes a generation with bounded diagnostics. No unbounded parse cache or per-read
whole-tree scan is retained. Hosts explicitly reload or invalidate after file notifications;
known reads validate the selected document and sidecar, and missing names trigger reload.
Incomplete scans publish verified entries and expose errors/truncation to the host.
The static skills tool group contains the compatible name reader skills_manager,
pageable skills_list, and package-contained skills_read. Resource cursors bind identity,
metadata and content fingerprints and always recheck current admission. Responses are
bounded after JSON serialization; resident catalogs shorten descriptions before omitting
entries. Tools use normal discovery, schema merging, typed hooks and cancellation, without
adding special completion history or a dynamic tool provider. Host snapshots expose
presentation, explicit-only selection policy and dependency preflight metadata. None of
these metadata fields expands the caller's execution permissions or installs providers.
See the engine skill guide for limits, refresh semantics and integration details.
Each MCP registration owns connection/refresh locks, catalog authority, call concurrency, and cancellation. Fetches retain their registration identity until publication, which checks both registration and session identity. A cancelled fetch restores dirty state. Published raw tool metadata is retained alongside model definitions; local names remain stable for the registration lifetime. Calls refresh dirty catalogs, revalidate the captured route, and hold catalog authority through execution. Removal cancels old work; disconnect retires queued work while allowing requests already sent to finish.
MCP transport/lifecycle and protocol decoding remain in rmcp. Byte readers and
an HTTP backend bound input before decoding; pagination and model presentations
have separate budgets. The runner writes ToolOutput.model_output into the
existing neutral tool-output history envelope while retaining full raw results
for callers. Adapters preserve tool IDs and project supported media within the
tool response, with text fallback for unsupported API/model combinations.
Application callbacks supply optional elicitation; resource methods use explicit server/URI selection. Approval UX, secret expansion, persistent credential stores and launch policy remain outside the MCP protocol layer. No full conversation history is sent to MCP servers. See MCP integration.