Repository navigation
mentra v0.10.0 — agentic-core hardening
mentra v0.10.0 / mentra-provider v0.4.0
Mentra 0.10 hardens the agentic core while preserving its architectural
contract: Mentra remains the sole owner of the model/tool loop, new host seams
stay generic, and default behavior changes only where explicitly documented
below.
This combined release covers the five hardening workstreams and the Proposed
coding-tools,
reasoning-preservation,
and host-orchestration
design records.
Highlights
Runtime hygiene and shell validation
- Remove the orphaned 5,924-line provider source tree. The live adapters remain
inmentra/src/provider.rsand continue to delegate tomentra-provider. - Add opt-in
ShellValidationMode::{Off, Warn, Enforce}classification for
builtin foreground and background shell execution. - Validation enriches authorization previews and reuses the existing
allow/prompt/deny vocabulary and hooks.Offremains the default. - Shell classification is defense-in-depth and permission UX, not a security
boundary; runtime roots, environment isolation, and process controls remain
the enforcement boundary.
Universal tool-result limits
- Apply one output boundary to successful and error results from builtin,
custom, and MCP tools, including parallel batches. - Defaults are 2,000 lines and 50 KiB per result. Truncation retains complete
lines and appends an actionable notice; under-limit output is byte-identical. - Oversized structured JSON is never sliced. When spill succeeds, it is written
whole and replaced by text pointing to the artifact; otherwise the text
notice explains why the full output was not saved. - Volatile stores suppress disk spill to preserve their no-durable-trace
contract. The existing shell stream-capture cap remains separate and applies
before this result boundary.
Model-conventional coding tools
- Add opt-in
read,ls,grep,glob,write, andedittools over the
existing transactionalWorkspaceEditor. FileToolProfile::{Batched, Split, Both}controls the advertised surface;
Batchedremains the default. Read operations use the parallel lane, while
write and edit use exclusive local mutation.- Grep gains file filters, literal and case-insensitive modes, context,
multiline regex, and a Unicode-safe 500-character cap per rendered physical
line. - Split
editvalidates all matches against the original content, rejects
ambiguity, overlaps, and no-ops, preserves BOM/CRLF, and supports an
NFKC/smart-punctuation fallback. - Diffs, patches, and
first_changed_lineremain localToolOutput::details;
providers receive only a short summary. - Recursive list, search, and glob traversal now reject descendant symlinks
that escape configured read roots. This is an intentional default-path
policy fix.
move and delete remain batched-only. Historical files.replace keeps its
exact-match semantics and does not gain fuzzy editing.
Thinking and reasoning continuity
- Add externally tagged
ContentBlock::Thinkingplus streaming start/delta
variants and persistence/replay support. - Capture and replay Anthropic signed/redacted thinking and OpenAI Responses
reasoning items, summaries, IDs, and encrypted content. - Emit reasoning text separately as
AgentEvent::ReasoningDeltaand
SessionEvent::AssistantReasoningDelta; signatures and ciphertext never
enter host text deltas. - Replay opaque reasoning only when registered provider, requested model, and
provenance format match exactly. Missing/empty metadata and cross-model or
cross-provider history downgrade to ordinary text instead of being dropped
or rejected. - Responses requests with reasoning enabled include
reasoning.encrypted_content; Azure-compatible late ciphertext is backfilled
from terminal output. - Thinking is persisted and may contain sensitive provider material. Local
compaction summarizer inputs explicitly exclude it.
Gemini thought/signature fidelity remains incomplete because signatures can
also attach to tool-use and text parts; that phase is deferred.
Steering and host orchestration
- Add agent-scoped in-memory steer and would-stop follow-up queues with
QueueMode::{OneAtATime, All}. Enqueueing never auto-starts an idle agent;
hosts callAgent::run_queuedexplicitly. - Steering wins over a user
RoundStrategyat a boundary without consuming
the strategy slot. Next-request context order is stable:
steering, then team inbox, then background notifications. - Drained steering is inflight-tracked and requeued on runner or finalization
errors. - Add typed
Runtime::task_boardandAgent::task_boardfaçades over the
existing access, transition, and DAG-validation path. - Add object-safe
TaskStore::mutate. SQLite, Volatile, and Hybrid stores
serialize mutations transactionally; custom stores retain a source-compatible
load/replace fallback. - Add
Agent::run_to_output<T>through an owner-scoped terminal tool and exact
tool_use_iddetail extraction. Provider-nativeresponse_formatremains
out of scope. - Add owned, generation-aware wait helpers for snapshots, idle completion, and
teammate replies. Seeexamples/host_orchestration.rsfor the end-to-end
host-scripted flow. - Make opaque team-protocol request IDs collision-resistant across
cross-second request interleavings.
Nous migration — read before upgrading
Upgrade both direct dependencies together:
[dependencies]
mentra = "0.10"
mentra-provider = "0.4"Nous imports provider-core types directly. Retaining mentra-provider 0.3
allows two provider-core versions into the graph and creates incompatible
types against Mentra's ContentBlock and tool-result surface.
Nous should also opt out of the generic result cap until its observation
protocol becomes truncation-aware:
use mentra::{Runtime, RuntimePolicy};
let policy = RuntimePolicy::default()
.with_max_tool_result_bytes(usize::MAX)
.with_max_tool_result_lines(usize::MAX)
.spill_full_tool_output(false);
let builder = Runtime::empty_builder().with_policy(policy);Why this is required today:
- Nous serializes each
ToolObservationas one physical JSON line. - Above 50 KiB, no complete line fits, so Mentra replaces the observation with
a truncation notice. - Nous cannot deserialize that notice and excludes the observation from its
reconstructed evidence. - Nous uses
VolatileRuntimeStore, which correctly suppresses disk artifacts,
so there is no spill file to recover. - Disabling spill alone is insufficient; both result limits must also be
raised. Add a regression using serialized output larger than 51,200 bytes.
Additional integration guidance:
- Treat Responses
tool_use_idvalues as opaque. Reasoning-bearing turns may
use localcall_id|function_item_idIDs; provider outputs still project the
raw call ID. Exact-equality pairing remains correct. - Existing wildcard
AgentEventandContentBlockmatches continue to
compile. HandleReasoningDeltaexplicitly only if live reasoning should be
surfaced. - Replay-only Responses mode benefits from preserved reasoning. A
gather-to-synthesis model switch safely downgrades incompatible opaque
reasoning to text. - Run a live Responses/xipe smoke after upgrading because reasoning-enabled
requests now ask providers for encrypted reasoning content.
Compatibility and source notes
- Old persisted transcripts, agent memory, and snapshots deserialize
unchanged. New optional fields are serde-defaulted and omitted when absent. - Under-limit tool results remain byte-identical. Oversized text intentionally
changes, and oversized structured results become textual truncation notices
that include an artifact pointer only when spill succeeds. - Exhaustive matchers must add cases for
ContentBlock,ContentBlockStart,
ContentBlockDelta,AgentEvent,SessionEvent, and
mentra_provider::responses::model::ResponsesInputItemreasoning variants. - Exhaustive
AgentSnapshotliterals must addrun_generation; patterns
should use... - Shell authorization preview JSON gains a
validationobject. InWarn
mode, the reusedAuthorizationDeniedhook is advisory; inspect its action
and outcome rather than treating the event name as proof execution stopped. - Existing custom
AgentStoreimplementations compile through the default
allows_disk_artifacts() == true; no-durable-trace stores should override it
to returnfalse. - Existing custom
TaskStoreimplementations compile through default
mutate, but that fallback is not atomic and should be overridden for
concurrent writers. - Default agents have empty steering queues, shell validation remains
Off,
and file tools remainBatched. - Public
time = "0.3"andurl = "2.5"requirements remain compatible with
downstream resolution. Resolver v3 and lockless Rust 1.85 CI enforce the
repository MSRV without exact public pins.
Operational caveats
TaskBoardreads are live, but façade mutations refresh an agent's cached
task snapshot only at the next normal refresh or run boundary.- The pre-existing full-board failed-run restore can overwrite a concurrent
successful task mutation that occurred after the snapshot was captured. - Agent memory, agent records, and run checkpoints do not share one atomic
store transaction. Steering/team/background input is requeued after late
errors, but earlier finalization writes may already have committed. wait_for_teammate_replyconsumes inbox delivery and must not race the agent
loop's inbox read. The host owns and must route the returned content.- An unsatisfied snapshot wait can remain pending while runtime observers
retain watch-channel senders.
Crate versions and publication order
- Publish
mentra-provider 0.4.0. - Publish
mentra 0.10.0, which depends on provider 0.4. - Create the single repository tag and GitHub release
v0.10.0.
Verification
This release is expected to pass:
cargo fmt --all --checkcargo clippy --workspace --all-targets -- -D warningscargo test --workspace- a fresh lockless
cargo +1.85.0 check --workspace --all-targets