![]()
Open your agent's config file and count how many times you wrote temperature = 0.3.
If the answer is more than one, you already know the problem. Every AI coding tool grows the same way: a model here, a sampling knob there, a special override for the summarizer, another for the sub-agent, each added for a good reason, none of them related to each other. Six months later changing your default model means a find-and-replace session across your own settings, and you're never quite sure which of the five copies actually won.
0.45 was the release where we deleted mechanisms the benchmark couldn't defend. 0.48.0 is the release where we deleted configuration — and then spent the rest of the cycle on the other end of the timeline: what an agent should remember after the session ends, and what it should have the discipline to forget.
Two numbers frame the release. Between 0.47.0 (August 26) and 0.48.0 (August 31) — five days — production code went net −9,789 lines. Tests went +82,624.
First, the three releases you didn't get a letter about
The last release post covered 0.45.2. Three releases shipped between it and today, and each closed a real gap:
0.45.3 and 0.46 hardened the verify gate — the evidence-demanding "done" check 0.44 introduced. The gate got a reworked verification prompt, recovery tracking for flaky verifiers, and explicit mutation-readiness contracts. Then 0.46.1 added the piece we're proudest of: a second-opinion refutation pass. Before the gate rejects the agent's claim of completion, a separate pass tries to refute the rejection — because a gate that cries wolf trains everyone to ignore it, and false "not done" verdicts burn exactly the tokens the gate exists to save.
0.46 also brought scheduled routines — agents that run on a cron schedule and report back — which got their own post.
0.47 laid infrastructure you'll meet twice in this post: message attachments across the WebSocket and MCP surfaces, background shell-job tracking with a real lifecycle, media capability validation (a text-only model tells you it can't see your screenshot instead of silently dropping it), and compression that folds in the background with amortized decisions — so a long session pays for compaction gradually instead of freezing at the worst moment. The attachments story — images and voice in every session — is big enough that it gets its own post this week.
Which brings us to 0.48.0, and what five days of deletion bought you.
octomind is the whole command now
Smallest change first, because it's the one your fingers will notice today:
octomindThat's it. No subcommand. Bare octomind starts an interactive session with your default agent, exactly as if you had typed octomind run. Piped input works the same way:
git diff | octomindrun still exists with all its flags — --model, --resume-recent, --sandbox, --schema, --daemon — and you'll still type it when you need them. But the tool's name is now also its most common invocation, which is how it always should have worked.
One [model] block to rule them all
This is the headline breaking change, and the reason the config version jumps to 12.
Before 0.48, model settings were archaeology. The main model was a loose top-level model = "..." key. max_tokens, max_retries, retry_timeout, request_timeout_seconds and reasoning_effort floated nearby as independent top-level settings, interleaved with UI options. Every role in the template then repeated its own model, temperature, top_p, top_k by hand. Taps and workflow steps had their own mapping syntax. Internally, eight different "model purposes" each carried their own resolution path.
In 0.48 there's one baseline:
# Complete baseline model profile. Every role, tap, workflow step, dynamic
# agent, and internal model profile inherits fields it does not override.
[model]
name = "octohub:auto"
reasoning_effort = "medium"
max_tokens = 32768
temperature = 0.3
top_p = 0.7
top_k = 20
max_retries = 1
retry_timeout = 30
request_timeout_seconds = 300Everything else is an override, and every override is partial. A role that only needs a different model says so and inherits the rest:
[[roles]]
name = "reviewer"
[roles.model]
name = "anthropic:claude-sonnet-5"A tap mapping is just a name — it swaps the model and deliberately keeps your main parameters:
[taps]
"developer:general" = "ollama:glm-5"Resolution is one chain you can hold in your head, highest priority first:
runtime override (--model, CLI flags)
> role model profile
> tap name mapping
> [model]The eight internal model purposes collapsed to three — main, supervisor, compression — and the supervisor's entire control plane (gate, plan, condense, learning) now shares a single [supervisor.model] profile, which itself inherits anything it doesn't set from [model].
The payoff is the operation you actually perform: switching your default model. That used to mean auditing every role, every tap mapping, every subsystem override to see which stale copy was about to sabotage you. Now it's one line — name = "..." under [model] — and every role, tap, workflow step, and internal call that didn't explicitly override the name follows. The overrides you did write are all the ones you meant.
Validation got stricter to match: every resolved profile must end up with a non-empty, provider-qualified name, and out-of-range sampling values fail at load, not mid-session. A companion fix (dd317df2) makes sure follow-up, learning and supervisor calls all actually route through the resolved profiles rather than legacy flat settings — so the config you read is the config that runs.
You do not migrate anything by hand. On first load, 0.48 upgrades your existing config through the version 12 schema, writes a backup to config.toml.backup, and preserves your settings — including old scalar spellings, which remain accepted as name-only shorthand. The one thing that genuinely broke: workflow step overrides no longer accept nested profile tables; they take a model name.
A memory built for the long run
You corrected the agent on Tuesday. It's Thursday, and it's making the same mistake, and you're typing the same correction, and somewhere in the back of your mind you're keeping a list of everything it can't be trusted to remember. That list is you doing the agent's job.
Every coding agent now claims to "learn from your sessions". Most mean one of two things: a notes file the model appends to until it's noise, or a vector store that hoards everything forever and retrieves with a shrug. Both fail the same way — not at week one, at month six. Week one, any memory helps. Month six, the notes file is 40KB of stale contradictions injected into every prompt, and the vector store confidently retrieves the lesson you reversed in April. The hard problems in agent memory are retention, verification, and honest forgetting. That's what 0.48 rebuilds.
This release makes cross-session learning file-backed, evidence-gated, budgeted, and lifecycle-managed. It's the largest single piece of the release, so here it is from the top.
Memories are files you can read
The MCP learning backend is gone. Every memory is now a markdown file with YAML frontmatter on your disk, one file per record, organized by project and role:
~/.local/share/octomind/learning/
├── octofs/developer/ # scoped: {project}/{role}
│ ├── 20260405143000-bearer-auth-required.md
│ └── 20260405143001-custom-error-types.md
└── _/ # global: cross-project, cross-role
└── 20260405150000-always-single-pr.mdWe scope each lesson conservatively. Tell the agent "always open a single PR" and that's about how you work everywhere — it becomes a global lesson in _/. Tell it "in this repo, all API endpoints require bearer auth" and that's this project's business — scoped, filed under octofs/developer/. Each file is plain frontmatter plus content:
---
title: 'Bearer token auth required for all API endpoints'
memory_type: learning
importance: 0.9
confidence: high
scope: scoped
outcome: unknown
use_count: 0
---Open one in your editor. Edit it. Delete it. Move a file from the cold archive back into the hot directory and the system treats that as a promotion. There's no opaque database between you and what your agent believes about your project.
Nothing gets in without evidence
We only extract a lesson when it can quote you verbatim — a correction you made, a rule you stated. No quote, no lesson, silently dropped. Orientation records (the agent's descriptive understanding of how your system works) now require valid citations of real user or tool messages; missing, duplicate or fabricated references are rejected at extraction (648f3bbd).
New in 0.48, long trajectories can produce experience records: 150–600 word structured write-ups with an objective, the durable knowledge, the outcome, and reuse conditions. Each one carries addressable session:// evidence handles and an honest outcome label — verified, failed, or unknown. A separate verifier checks the candidate against the transcript before we store it; a rejected candidate gets one repair attempt, then fails closed.
That failed label matters more than it looks. An agent that only remembers successes will happily walk you into the same wall twice. Octomind keeps failure-labelled experiences and recalls them as warnings.
A bounded pack per turn, not an ever-growing preamble
Before each genuine user turn, we pack relevant memories into an Active Memory Pack with a hard 2,000-token budget (global rules capped at 512 of those). The pack replaces the previous one — it's materialized only around the provider request, never appended to your session log, never accumulated. Tool follow-up rounds reuse it without another retrieval. If context headroom is too tight, the pack is dropped for that turn rather than crowding out your actual task.
And credit is earned, not assumed: each packed item gets an ID, the specialist reports which items materially affected its work, and only those get their usage counters bumped — with the verify gate's outcome reinforcing or weakening exactly the items that were used. A memory that keeps getting retrieved but never helps doesn't accumulate false authority.
Hot and cold: forgetting as a feature
Memory stores that only grow become retrieval noise. 0.48 gives each memory type a fixed token budget in the hot store:
| Memory type | Scoped budget | Global budget |
|---|---|---|
| Short user-backed rules | 16,000 tokens | 4,000 tokens |
| Orientation | 24,000 tokens | 8,000 tokens |
| Experience | 48,000 tokens | 16,000 tokens |
Cross the watermark and maintenance kicks in — after extraction, never in your request path. At most one pair of similar records becomes a consolidation candidate, and the merged replacement must pass a verifier proving it adds no claim, hides no contradiction, and keeps every non-duplicate constraint. Your own stated rules are never machine-merged at all: a generated paraphrase would break the quote-first contract, so they only change through explicit supersession.
Everything else ages by a utility score combining importance, confidence, demonstrated use, and recency — and the lowest-utility records move to a cold archive. Nothing is auto-deleted, ever. Cold records keep a searchable catalog entry, exact lexical matches can page them back into a request, and a cold record that proves materially useful is automatically promoted back to hot. The only destructive operations are the ones you type yourself: /learning delete and /learning clear.
Opt-in evolution: memories that compile into behavior
The most forward-looking piece, off by default:
[supervisor.learning.evolution]
enabled = trueWith evolution on, the detached learner may compile one highest-value grounded memory per extraction into a local skill or guardrail candidate. Candidates don't touch anything: they move through shadow evaluation, a bounded trial, and only then activation — with rollback tracked the whole way, and generated behavior never overwriting your authored skills or .agents/guardrails.toml. You stay in the loop with explicit controls:
/learning evolution # list evolved behavior for this project
/learning evolution show <id> # provenance, artifact, trials, history
/learning evolution approve <id> # or reject, or rollbackThis is the long game: an agent whose corrections become checkable, versioned, revertible behavior — not vibes in a prompt.
And retrieval is now benchmarked, not vibed
We reworked the ranking — hybrid sparse+dense fusion with correction-conflict handling, chunked late-interaction scoring so a small fact inside a long memory doesn't get diluted, a cosine noise floor, and safe fallbacks when the query rewrite misbehaves. More importantly, it ships with benchmark suites — including LongMemEval-style recall, staleness and abstention contracts — so the next retrieval argument is settled with a number, the same way 0.45 settled the supervisor argument.
/status: everything running, one view
Octomind sessions stopped being one-question-one-answer a while ago: background agents, command monitors, scheduled routines, and now MCP servers that kick off long jobs. What we had for visibility was /agents for some of it and /monitor for another slice, and neither showed MCP jobs at all.
Both commands are gone. There's one surface now — this is the real thing, idle state and all:
╭ /status · 0 active
│ No active agents, MCP jobs, or command monitors.
│
│ Full views: /status agents · /status monitors · /status jobs
╰ ✓ /status · 0 active| Command | Shows |
|---|---|
/status | Concise active-only view: agents, MCP background jobs, command monitors |
/status agents | Full agent view with recent completed/failed runs, tokens, cache, cost |
/status agents <id> | Detailed card for one run |
/status monitors | Configuration and elapsed time for command monitors |
/status jobs | Live status and bounded output for MCP resource-backed jobs |
States, durations, costs, and the actions available for each item — in one place, with one command to remember.
Behind /status jobs sits a real protocol feature: MCP resource-link watching (69137fa9). When an MCP tool returns a resource link — "your build started, here's the handle" — Octomind now subscribes to it, watches it through push or subscription updates, and delivers the result into your session when it completes. Async tools stop being fire-and-forget.
Nothing gets dropped on the floor
Watching jobs is only half the contract; the other half is not losing them. 0.48 closes an entire class of async lifecycle bugs:
- A session no longer exits — and ACP mode no longer shuts down on EOF — while background jobs, monitors, or inbox work are still pending (
642d1aad). Your editor closing the pipe doesn't orphan a running task. - Pending jobs survive result delivery without duplication (
c5aaf50b), pending handbacks are no longer eaten by recursive continuations (a9072a9a), and background tap-runs survive conversation compression with their labels intact (9009b6de). - Tap runs are now always asynchronous (
1d70aa75) — consistent lifecycle, consistent visibility in/status. - Rescheduling a routine preserves its entry id (
2fd154cc), so editing a schedule doesn't silently fork it.
None of these is a feature you'd put on a landing page. Together they're the difference between an agent you can hand a 40-minute job and one you have to babysit.
Honest numbers
Session info now reports real timing metrics: completed and total turn durations, last and average turn time, and model/request/API timing split out (6cdf62be) — routed through JSONL mode too, so automation sees the same numbers you do. Per-session and per-request spending thresholds are enforced alongside them.
And one small fix with a moral: throughput now counts reasoning tokens (edd110ec). Thinking models were billing you for reasoning all along; now the tokens-per-second you see includes them, so the number is the one you're actually paying for. Every non-interactive run still ends with the exact ledger:
· $0.02483 · input $0.02343 · output $0.00017That's a real footer from a real 0.48.0 session, not a mockup.
Build your own agents: octomind tap init
Taps — the registries that ship agents like developer:general — used to be something you consumed. Now you can ship your own:
$ octomind tap init acme/team --agent legal:contracts
╭ tap · init
│ name acme/team
│ dir ./octomind-team
│ agent legal:contracts
│ next octomind run legal:contracts
╰ ✓ tap · acme/teamOne command renders the scaffold from the default tap, validates it (unresolved template tokens are a hard error, and the generated repo must pass its own scripts/check.sh), runs git init, registers the directory as a local tap, and prints the run command for your new starter agent. What you get is a complete, working repository — a starter agent, capability configs, templates for every artifact type, a validation script, and a CI workflow that runs it:
octomind-team/
├── agents/legal/contracts.toml # your starter agent, runnable now
├── capabilities/core/… # capability configs
├── templates/ # agent, capability, skill, dep, workflow
├── scripts/check.sh # the same validation CI runs
├── .github/workflows/validate.yml
└── AGENTS.md · README.md · LICENSEoctomind run legal:contracts # works immediately — it's a registered local tapYour team's agents, versioned in your repo, installable by anyone with octomind tap — with the same lifecycle as the built-in ones.
The supervisor keeps your constraints in mind
Two refinements to the supervisor, both in the spirit of 0.45's "earn your line count" rule:
Your constraints survive compression. Every agent user knows this failure by feel. Message 3: "refactor the billing module, but don't touch the migration files." Message 47, two compactions later: the agent — working from a summary of a summary — cheerfully edits a migration, because the task survived compression and the "but" didn't. In 0.48 those constraints are captured as first-class data, bounded, carried through plan resolution and execution, and fed into goal recitation — so the "don't" is still binding at message 60 (2b4e7199). Recall is re-armed whenever compression changes the active task, so the agent re-orients instead of coasting on a stale memory pack.
Condense can now tune itself — if you ask. Condensation narrows oversized tool outputs with one cheap-model call per round. New opt-in adaptivity learns a per-session trigger from realized savings, bounded to 0.5×–2× of your configured threshold, and re-probes candidates it previously skipped when the threshold drifts back down (1599f965):
[supervisor.condense]
enabled = true
adaptive = true # default false — fixed threshold unless you opt in
tokens_threshold = 5000And a guard that should have been there from day one: we hard-cap results before condensation, and only individually oversized results ever reach the condenser — small results pass through untouched, byte for byte (bdaed34f).
−9,789 lines of product, +82,624 lines of proof
The number from the top of the post deserves its breakdown. In five days, production source went +9,836/−19,625 — the model-purpose collapse, the learning backend removal, and the override machinery all coming out. Test code went +82,624 lines across 247 files: behavioral coverage for the session loop, supervisor, config loading and guardrails, MCP client/server/health-monitoring, agent orchestration, workflow execution, skills, caching, and the CLI — plus cross-platform stabilization so the suite means the same thing on macOS, Linux and Windows.
This is the same bet 0.45 made, doubled down: you can only delete confidently at the rate you can prove behavior. The tests are what let a five-day cycle remove ten thousand lines from an agent runtime without holding our breath on release day.
The reliability sweep that rode along:
- Windows: dependency script paths normalized (
cabbd83f), platform-specific path handling fixed across the runtime (77a899f4). - Security:
/sharebridge tokens are now generated from UUIDv4 randomness (7d768d76). - ACP: child-process exit during request writes no longer wedges the connection (
e66c4512). - MCP: stale local tool mappings are detected and healed (
4acaa9ec); tool metadata and arguments are preserved through the runtime, and blank arguments normalize to empty objects instead of confusing servers (ff5e3843). - Compression: raw messages are snapshotted before folding so learning extraction sees the real conversation, and assistant thinking survives into attention packets (
686560e9). - CLI: an explicit role argument now reliably wins over the default (
38de5b1a).
Breaking changes and upgrading
# Homebrew (macOS / Linux)
brew install muvon/tap/octomind
# or from crates.io
cargo install octomindPrebuilt binaries for every platform are on the releases page.
What to expect on first launch:
- Config auto-migrates to version 12, with a backup at
config.toml.backup. Your scattered model settings become the layered profile shape with values preserved; old scalar tap/workflow spellings keep working as name-only shorthand. The only manual step: workflow step overrides that used nested profile tables must become model names. /agentsand/monitorare gone. Everything they showed — and the MCP jobs they didn't — is under/status. Muscle memory is the only casualty.- Learning is file-backed only. The MCP learning backend and its config are removed. Existing file-backed lessons carry over untouched and pick up the new retention lifecycle automatically.
- Evolution is off by default. Nothing synthesizes behavior from your memories unless you set
[supervisor.learning.evolution] enabled = true— and even then, everything is trialed, approvable, and revertible.
The model hub is open to everyone — octomind login, or /login from inside a session, and you're routing.
There's a shape to the last four releases. 0.44 made "done" a verdict evidence has to earn. 0.45 applied that standard to our own mechanisms and deleted the ones that couldn't pay. 0.47 gave sessions eyes and a voice — that one gets its own post. 0.48 applies the evidence standard to the two things that compound: your configuration, which should shrink as a tool matures, and your agent's memory, which is only worth having if it's grounded, budgeted, and honest about failure.
An agent you use for a week needs to be smart. An agent you use for a year needs to be simple where you touch it and disciplined about what it carries forward. 0.48.0 is built for the year.



