feat(code): selectable model for the Auto approval classifier - #5205
Merged
Conversation
Auto mode's authorization classifier reused the main agent model for every gated action batch, so each review paid frontier price and latency on the critical path. It can now be pointed at a separate model via `--auto-classifier-model`, `DEEPAGENTS_CODE_AUTO_CLASSIFIER_MODEL`, `[models].auto_classifier`, or `/auto model`, with the per-run context taking effect without a server restart. The classifier is an authorization control, so the default is unchanged (inherit the main model), a spec that cannot be built fails closed to human approval instead of silently reverting to the main model, and primary-model settings are not forwarded to a distinct classifier. Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Mason Daugherty (mdrxy)
marked this pull request as ready for review
July 30, 2026 20:13
Review follow-ups on the selectable Auto classifier model. `/auto model clear` sent a bare `None` on the run context, which the middleware could not tell apart from "this run has no preference", so a session started with `--auto-classifier-model`, the env var, or `[models].auto_classifier` kept authorizing actions with that model after the UI said reviews had moved back to the main agent model. The context now carries an explicit inherit marker, and the client no longer treats a locally unset classifier as proof that the server has none either. The classifier model selector also started its worker and refocused the chat input inside the modal's own dismiss callback; both now wait for the refresh that removes the modal, per the package's Textual contract. Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
The Auto approval classifier authorizes gated tool calls, so choosing which model performs that review is a user-level security decision. `DEEPAGENTS_CODE_AUTO_CLASSIFIER_MODEL` is now denied from a *project* `.env` (`_PROJECT_DOTENV_DENIED_ENV_KEYS`), matching the treatment of the project-MCP trust vars. Without it, a cloned repo could commit a `.env` that silently pointed the review at a weaker model — weakening the control, including its resistance to prompt injection in the untrusted material it reads. Shell exports, the global `~/.deepagents/.env`, `config.toml`, `--auto-classifier- model`, and `/auto model` are unaffected. A blank spec now means "inherit" at every tier. `_classifier_spec` previously passed a blank construction-time value through to `create_model`, which treats an empty spec as `[models].default` and would have reviewed with a model nobody selected, while labelling it `inherited` in traces. `--auto-classifier-model` is rejected under `--sandbox`, where Auto is disabled, for the same reason the headless form is rejected. When a classifier failure coincides with a failed counter write, both are now reported. Control state tends to recover on its own; a misconfigured spec never does, so collapsing the two sent the user back for another round after fixing the disk. Invocation failures on a distinct classifier also name the spec, so a cached model built against a rotated credential points at a restart rather than another `/auth`. A blank configured value is logged instead of being dropped silently. `INHERIT_CLASSIFIER_MODEL` changes from a NUL-prefixed sentinel to plain ASCII. The context is serialized to JSON and may be persisted, and Postgres text/jsonb rejects NUL; a stripped sentinel reads as "no preference", which would leave a startup classifier authorizing actions after the UI reported the clear. Docs corrected to match behavior: an unbuildable classifier denies the batch and escalates to human approval only after `_CONSECUTIVE_UNAVAILABLE_FALLBACK` consecutive failures, rather than routing to approval immediately. Precedence is documented consistently as env then `config.toml`, and the settings-browser summary carries the review-quality caveat. Adds coverage for the project/global `.env` split, blank-spec inheritance, argument-over-env precedence, per-run context delivery for both set and clear, cache recency, instance labelling, the combined diagnostic, the unavailable log label, and the sandbox guard.
A misconfigured Auto classifier previously degraded in ways the user could neither see nor act on. Six fixes, all on the paths that run when the configured classifier cannot review a batch. Escalate permanently-broken classifiers instead of oscillating. Approving a human fallback resets `consecutive_unavailable`, so counting construction failures left a bad spec denying two batches for every one it asked about, indefinitely. Construction faults now latch the failing spec in `AutoModeCounters.classifier_config_failed_spec`: the first failure still denies, and every later batch escalates to human approval. Construction is still retried each batch, so fixing the setting clears the latch on the next successful review without a restart. The prompt names the spec and the commands that fix it — the approval prompt renders the batch-level `fallback_reason`, not each decision's own reason, so the diagnostic is carried there too. Report a configured-but-unusable classifier. A blank or wrong-typed `[models].auto_classifier` silently reverted authorization review to the main agent model — the agent grading its own actions — with only a debug-log line, or nothing at all for the non-string case (`resolve_scalar` coerces it to the option default, making it indistinguishable from absent). `resolve_auto_classifier_model_with_problem` returns the reason alongside the spec and the launch path prints it. Startup specs also get the cheap half of what `/auto model` validates — spec parse plus provider credentials, no `create_model`, so LangChain stays off the launch path. Say which model reviews actions, and which role it plays. The first-enable notice called the classifier the "active model", which reads as the model writing the code; with `--auto-classifier-model` the two differ, and this modal is the only place the disclosure appears. The copy now distinguishes a separate classifier from an inherited one. The interpolation anchor is a named constant and the builder raises rather than silently dropping the disclosure. Give classifier construction its own deadline. Building shared the 20s inference budget, so a cold provider import made the first review the likeliest to be denied and reported it as "the classifier did not respond" about a model that was never built. Recover from a revoked credential in-session. Construction succeeds once and the model is cached, so an invoke-time failure repeated forever — `/auth` runs in the client and cannot reach the cache. Failing models are now evicted, and `/auto model <same spec>` revalidates instead of short-circuiting. Normalize a blank `--auto-classifier-model` at parse time so the ACP, headless, and sandbox guards and the value passed to the TUI all agree it means "inherit". Also: correct two docstrings that stated the opposite of the code (a blank runtime-context spec means "no preference", not "inherit"; a blank string is not equivalent to `None` in `create_cli_agent`); log unexpected detached-construction failures instead of discarding them; hoist markdown escaping to `_markdown.py` rather than keeping a second copy in the notice widget; drop an unreachable `getattr` fallback that would have labelled classifier rows from the main-model catalog; add T14 to the THREAT_MODEL input-coverage tables and revision history; and note that `/auto model` is session-scoped. Tests cover the invoke-time diagnostic, cache eviction, the latch and its survival across a real approval, headless flag rejection, `/auto`'s queue-bypass tier on both sides, blank-flag normalization, and the notice copy. Mutation testing confirms the fail-closed path, the latch, and the prompt plumbing are each caught by a failing test. `test_live_tail_appends_new_records_incrementally` read the process-wide log buffer, so it depended on how many records earlier test files happened to emit: once retained records cross `_RECORD_LIMIT`, every poll takes the prune-and-re-render path instead of appending. It now drives a controlled buffer.
The remediation text enumerated three ways out — `/auto model`, `/auto model clear`, and `/auth` — which is too long for a message that appears inline in an approval prompt the user reads while deciding whether to allow an action. Keep only how to switch the classifier; the spec is already named, and the other routes are discoverable from `/auto`.
…ier-model # Conflicts: # libs/code/THREAT_MODEL.md
…assifier An explicit blank flag means "review with the main agent model", but parse_args collapsed it to `None`, which downstream reads as "no flag — consult `DEEPAGENTS_CODE_AUTO_CLASSIFIER_MODEL` / `[models].auto_classifier`". A user with a configured separate classifier launching with `--auto-classifier-model ""` kept the weaker configured classifier authorizing actions instead of inheriting the main model as documented. Preserve blank-vs-absent from argparse through launch resolution by mapping the explicit blank to `INHERIT_CLASSIFIER_MODEL` — the sentinel `/auto model clear` already uses — instead of `None`: - run_textual_cli_async resolves a blank flag to the sentinel so it beats env/TOML without falling into resolve_auto_classifier_model_with_problem; a bare "" can't be used because ServerConfig.from_env collapses empty strings to None, which would re-enable a configured classifier in the server subprocess. - AutoModeHITLMiddleware maps the sentinel to inherit at construction time too (previously only per-run), so it never tries to build a model named __dcode_inherit_classifier__. - The TUI treats the sentinel as inherit in labels, the model selector, the Auto-activation notification, and the per-run context value so the raw sentinel never surfaces to the user.
Mason Daugherty (mdrxy)
added a commit
to langchain-ai/docs
that referenced
this pull request
Aug 4, 2026
…de (#5271) Fixes DOC-1474 ## Summary - `approval-modes.mdx`: adds a new **"Select a classifier model"** section under "How Auto works", documenting all four configuration sources (`/auto model`, `--auto-classifier-model`, `DEEPAGENTS_CODE_AUTO_CLASSIFIER_MODEL`, `[models].auto_classifier`), precedence order, fail-closed error behavior, the project `.env` restriction, and the prompt-injection security tradeoff (T14) - `cli-reference.mdx`: adds `--auto-classifier-model` to the command-line flag table - `config-file.mdx`: documents the `[models].auto_classifier` key in the `[models]` table with a link to the full precedence description ## Links - Linear: https://linear.app/langchain/issue/DOC-1474/document-selectable-auto-approval-classifier-model-for-deep-agents - Slack: https://langchain.slack.com/archives/C09G1T60QV9/p1785791397422779 - Source PR: langchain-ai/deepagents#5205 ## Verification Not run; docs-only content change. ## Reviewers Requested review from: @mdrxy, @npentrel --------- Co-authored-by: Docs Writer Bot <brace@langchain.dev> Co-authored-by: Mason Daugherty <mason@langchain.dev> Co-authored-by: Naomi Pentrel <5212232+npentrel@users.noreply.github.com>
Johannes du Plessis (johannes117)
pushed a commit
that referenced
this pull request
Aug 4, 2026
> [!CAUTION] > Merging this PR will automatically publish to **PyPI** and create a **GitHub release**. For the full release process, see [`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md). --- _Release notes preview: keep this section in sync with the package `CHANGELOG.md`. Publish reads the merged CHANGELOG via `release.yml`, not this PR description — keep them aligned anyway so the PR stays an accurate historical record for reviewers and anyone returning later._ --- ## [0.1.52](deepagents-code==0.1.51...deepagents-code==0.1.52) (2026-08-04) ### Features - Hooks v2 is now generally available, with support for loading hooks from installed plugins. ([#5307](#5307), [#5198](#5198)) - Auto approval classifier configuration now supports selecting the classifier model and setting a review timeout. ([#5205](#5205), [#5302](#5302)) - HITL rejection reasons are now framed for the model, and the approval menu makes reject-with-feedback easier to discover. ([#5259](#5259), [#5260](#5260)) - Added a tri-state `DEEPAGENTS_CODE_ONBOARDING` environment variable. ([#5301](#5301)) - The `/model` footer Ctrl+N hint now follows the current display mode. ([#5247](#5247)) - The price catalog now refreshes hourly in the background. ([#5264](#5264)) - Updated recommendations to include DeepSeek V4 Flash 0731. ([#5244](#5244)) ### Bug Fixes - Fixed several Hooks v2 lifecycle issues: session-end teardown is now bounded, hooks refresh after cwd switches, malformed hook resumes are handled, hook stops surface without agent errors, and unused `SessionEndCause` members were removed. ([#5248](#5248), [#5249](#5249), [#5233](#5233), [#5276](#5276), [#5240](#5240)) - `PreCompact` now fires before auto-compaction. ([#5277](#5277)) _End release notes preview._ --- > [!NOTE] > A **New Contributors** section is appended to the GitHub release notes automatically at publish time (see [Release Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline), step 2). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: langchain-oss-automated-triage[bot] <248757908+langchain-oss-automated-triage[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Auto approval mode can now use a separate, cheaper model to review actions instead of always reusing the main agent model. Set it with
--auto-classifier-model,DEEPAGENTS_CODE_AUTO_CLASSIFIER_MODEL,[models].auto_classifier, or/auto modelin the TUI.In Auto mode, any gated tool call that deterministic policy can't clear is reviewed by an LLM classifier. That review used the main agent model, so every batch paid frontier-model price and latency for a short verdict, in the middle of the turn.
The classifier decides what runs without asking you, so the behavior around it is conservative:
/auto model→ launch flag → env var →config.toml→ inherit. A blank value anywhere means "inherit"..envcan't set the env var, so a cloned repo can't quietly point your review at a weaker model. Shell exports, the global~/.deepagents/.env,config.toml, the flag, and/auto modelall work as usual./auto modelvalidates a spec before accepting it./auto model, including one set by env var orconfig.toml.--model-paramsare provider-specific, so they only travel when the classifier is the main model.--auto-classifier-modelonly works where Auto runs — interactive TUI, no sandbox — and errors out otherwise instead of being quietly ignored.THREAT_MODEL.mdgains T14 for the tradeoff: a weaker classifier means a weaker review, including against prompt injection in the content it reads.Not included: a recommended cheap model (there's no accuracy eval suite yet), classifier-specific invocation params, a configurable timeout, and cache invalidation when credentials rotate (a rotated key shows up as a named classifier failure).
Test plan
Unit tests cover precedence, blank specs, the project/global
.envsplit, model caching, settings hygiene, fail-closed resolution and its deadline, logging and trace fields, the per-run context for both setting and clearing, the sandbox and headless guards, and the/auto modelpaths.Manual: launch with
--auto-classifier-model <spec>, confirm reviews use it, then/auto model clearto go back.Made by Open SWE
References