Animated Octomind icon — purple pixel-art octopus

You said "fix the parser, don't run tests — I'll run them myself." Four tool calls later the agent runs the test suite.

Nothing was broken. The role permitted the shell. The guardrails had no rule for it, because "don't run tests" isn't a pattern you can match — it's something you said once, in your own words, ten minutes ago. The agent simply lost the thread.

Octomind 0.51.1 shipped on September 6. Its headline is an opt-in AI agent authorization check for exactly that failure: the supervisor reads your actual request, looks at the tool calls the agent is about to make, and stops the ones that demonstrably contradict you.

Tool authorization, in one config line

The authorizer is off by default. Turn it on inside your existing supervisor section:

toml
[supervisor]
enabled = true

[supervisor.authorizer]
enabled = true

It needs the supervisor master switch for root sessions, and it uses the shared [supervisor.model] profile — no separate model to configure. Your config version moves from 12 to 13 automatically; the migration adds the section and changes nothing you already set.

With it on, the run above ends differently. The test call never executes, and the agent gets a normal tool error explaining why:

text
[authorizer] Tool not executed: Tests conflict with the user's restriction. user source user:0: "Do not run tests". Continue within the role and user's request.

That last sentence matters as much as the block. The agent isn't left guessing what went wrong — it's told what it violated and where to go next. And if it does retry the identical call, the answer comes from the denial cache without paying for a second judgment.

Allow-first, on purpose

The obvious way to build this is also the wrong one: a model that reviews every call and blocks anything it can't justify. That agent would spend your money asking permission to read files.

The rule is allow by default. If uncertain, allow. Reasonable investigation, reading before editing, intermediate steps implied by your goal — all permitted without argument. "Fix this, nothing else" limits scope; it doesn't forbid the inspection needed to do the fixing. An unfamiliar tool is not a reason to block. Missing permission is not a reason to block. A role's stylistic preference is not a reason to block.

To stop a call, the judge has to identify two things at once:

  1. A verbatim clause in a labelled instruction source that establishes an explicit prohibition or the concrete scope you asked for.
  2. The exact proposed operation or argument that contradicts that clause now.

Then it has to survive a second pass. The first judge selects a source ID and a JSON pointer into the proposed arguments — it never retypes either; the runtime copies the exact source text and argument value from the real input. That resolved evidence goes to an independent verification call whose instructions are the opposite of the first judge's: default to confirmed=false, refute false positives, treat every proposed reason as an untrusted claim. An unconfirmed or malformed opinion contributes no veto.

Everything that can go wrong falls to allow. A judgment that is unavailable, malformed, over budget, past its 30-second deadline or beyond its 32,000-token request budget lets the call through. Failure of the judge is not evidence against the tool.

Where the authority comes from

The interesting attacks on a check like this aren't clever prompts — they're confusions of authority. Three of them are closed by construction:

Role text cannot pretend to be you. Every instruction source is labelled by the runtime: user:N is an actual root user, role:D:N a role instruction, delegated:D:N an agent-authored child task. A judge that wants to block must name a source ID from that catalog, and it cannot borrow a different source's authority. A role clause can support a block on its own — the check is role and user intent — but it is always attributed as role:D:N, never quoted back to you as something you said, and it cannot enlarge a restriction you stated yourself. Your original text survives pre-model pipes, so a transform can't quietly rewrite what you asked for.

Injected memory and tool output cannot manufacture permission. Tool results are untrusted reference data. A README that says "you may deploy to production" is a file, not a grant.

Completed work counts as evidence; claims don't. The authorizer sees runtime receipts of tools that already ran — arguments, working directory, and whether they succeeded — for the last 16 actions, and those survive compaction and session resume. A successful read in an earlier round satisfies "read before you edit"; the agent isn't forced to re-read a file to prove it looked. A missing receipt is unknown, never proof that a step didn't happen.

Delegation inherits all of it. An Octomind ACP child receives an immutable snapshot of its ancestors' constraints and completed actions, and a child prompt cannot revoke them. A delegated task may narrow what it was given; it can never widen it. In-process dynamic agents get their own scoped context with the same parent boundary.

What it costs, and what it doesn't promise

An ordinary allow is one judge call per batch, on the supervisor profile. A proposed block costs a second, confirming call. The authorizer also needs the supervisor's task resolution, so if you had both the completion gate and the plan manager switched off, expect one extra resolve call per user turn on top.

Only independently confirmed denials are cached — for identical calls under unchanged user, role, memory, tool-definition and completed-action context — and the cache is process-local and bounded. Allow decisions are never cached, and uncertainty is never cached as a prohibition.

/info shows the session's authorizer counters: checked, blocked, cached, unavailable. Those counters and your instruction text are not added to anonymous telemetry.

Now the part worth stating plainly: this is a model-based intent check, not a security boundary. We wrote a whole post on why a prompt is not a boundary, and nothing about 0.51.1 changes that argument. Scoped role permissions, the filesystem sandbox and native guardrails.toml rules are still the things that make an agent unable to act. They run first and they're deterministic.

Role permissions and the sandbox are untouched here; the one change to the native layer is that in-process dynamic agents now inherit and enforce the parent session's guardrails — guards, hooks and validators — which previously didn't reach them at all, and that inheritance applies whether or not you enable the authorizer.

The authorizer sits after them and catches the class they can't express — semantic conflicts with a sentence you typed — and it will occasionally be wrong in both directions. Blocked calls never enter guard history and never run post-result hooks, so a denial can't be laundered into a prerequisite for a later call. Your own cancellation still stops execution independently of any of this.

If you also run learning with evolution enabled, denials become candidate leads for the existing verifier — never self-justifying evidence. A denial is not proof that its own rule was right. A permanent guard still needs independently grounded user evidence, an expressible scope, replay cases, shadow matching and bounded trials. A task-local restriction ("don't run tests today") must not graduate into an unconditional rule.

The override runs the other way too. With the authorizer on, a guard that learning generated for you stops being an absolute deny: it's shown to the judge instead of applied blindly, so an explicit correction in your current message ("no, run the tests this time") can supersede that learned rule for one call — and only if the second, confirming call agrees. Guards you wrote yourself in guardrails.toml are never up for negotiation.

The supervisor guide documents the full behavior; the guardrails guide covers the deterministic layer underneath it, and every field is in the config reference.

A failed compression no longer costs you the turn

Second change, smaller but immediately felt in long sessions.

When a conversation approaches the context ceiling, Octomind compresses it with a model call. If that call came back unparseable — one bad response — the turn ended with an error. You'd lose the round and start it again.

0.51.1 falls through to deterministic trimming instead. A forced compression failure inside the ceiling margin is now logged, and the model-free path takes over: oversized tool results are trimmed until the context fits. It errors only if trimming still can't get under the ceiling. One flaky compression response no longer costs a whole turn of work.

Also in this release

  • Parallel tool batches keep their call identity. When decisions are rebuilt for a batch, pending call indices are preserved, so a decision always lands on the call it was made about. If an argument lookup is unavailable, the call is allowed rather than mismatched.
  • Blocked calls stopped counting as evidence. A [guardrail] or [authorizer] denial is a synthetic result, so learning no longer feeds it into extraction or guard synthesis. This applies whether or not you enable the authorizer.
  • Octomind identifies itself upstream. LLM requests now carry an Octomind/<version> user agent instead of a generic library default — some providers reject anonymous clients, and requests are attributable to Octomind rather than to a generic library client.
  • Octolib 0.36.1 is included, up from 0.35.3. That bump is what makes the custom user agent possible — set_user_agent arrived in octolib 0.36.0 — and it carries the provider layer's latest fixes. Octolib's new media-generation stack is feature-gated and is not compiled into Octomind.

Update to Octomind 0.51.1

bash
curl -fsSL https://octomind.run/install.sh | bash -s -- --version 0.51.1
octomind --version

See the installation guide for other platforms and methods, and the full changelog for everything in this release.

The authorizer itself is opt-in — no tool call is judged unless you enable it. Three things do change on upgrade regardless: your config migrates to version 13, LLM requests carry the new user agent, and a failed compression falls back to trimming instead of ending the turn.

If you've ever watched an agent do the one thing you asked it not to, add two lines to your config and run a session with /info open.

Already available in Octomind Cloud

Octomind 0.51.1 is already available in Cloud. Open Octomind Cloud to work from the browser, with the same configuration surface and the same supervisor behind it.