Files
deepagents/libs/code/README.md
T
Mason Daugherty dd8b8ad84a feat(code): enforce configured model allowlists (#5649)
`dcode` can now be restricted to an approved set of models. Add a
`models.allowed` list to `config.toml` (or push it via managed config)
and every model the app tries to use — the launch default, `--model`
flag, `/model` selections, saved defaults, and subagent model
declarations — is checked against it. Entries are exact `provider:model`
specs or `provider:*` wildcards that admit a whole provider's lineup.
Unset means all models are allowed; an empty list means none are.

---

**Why:** organizations rolling out `dcode` need to guarantee agents only
run on approved models (cost, compliance, data routing). Previously any
model with a credential was usable, and there was no enforcement point.

**How:**

- A new `models.allowed` config key (user or managed layer) parses into
an ordered, deduplicated allowlist of exact `provider:model` specs and
`provider:*` wildcards. A malformed list fails closed (deny-all) with an
error naming the defect, so a typo can't silently disable the guardrail.
- Every model-resolution path is gated: CLI flags, the model selector
(blocked entries are hidden from recommendations; typing a blocked spec
explains the policy and lists what's allowed), saved defaults (a blocked
stored default is ignored with a log, not advertised as `(default)`),
and subagent declarations (the error names the declaring file).
- Bare model names are canonicalized the same way `create_model`
resolves them before the policy check, so `gpt-5.6-terra` matches
`openai:gpt-5.6-terra` — or an `openai:*` wildcard — instead of being
spuriously rejected; a name whose provider can't be established stays
unmatchable, so inference is never a bypass.
- A `provider:*` wildcard admits that provider's whole lineup where an
exact list would prune unlisted models. The wildcard names no model
itself, so default resolution expands it to the provider's discovered
models — the registry profile lineup merged with any configured list —
rather than selecting it literally; a wildcard for a provider with no
discovered or configured models fails closed with a "no discoverable
models" message instead of a phantom credential prompt.
- Blocked selections raise `ModelNotAllowedError`, whose message names
the policy layer (user file vs. administrator-managed), quotes the
offending spec, and lists the allowed entries — instead of the previous
generic I/O-style failure. When the allowlist is active but no allowed
model has credentials, a distinct `NoAllowedModelCredentialsError` keeps
`/auth` from accepting a key and appearing to do nothing.

Edge cases covered: remote no-auth providers (e.g., a LAN Ollama
endpoint) remain valid allowlist fallback candidates; bare Bedrock IDs
must be written `bedrock:<id>` in the list since they otherwise split at
the version colon and could never match; `dcode tools list` skips
enforcement because it only compiles graphs it never invokes.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Kimi K3 <kimi-k3@moonshot.ai>
2026-08-24 12:49:01 -04:00

10 KiB

🧠🤖 Deep Agents Code

PyPI - Version PyPI - License PyPI - Downloads Twitter

Deep Agents Code

Quick Install

curl -LsSf https://langch.in/dcode | bash
# With model provider extras
# OpenAI, Anthropic, and Gemini are included by default
DEEPAGENTS_CODE_EXTRAS="nvidia,ollama" curl -LsSf https://langch.in/dcode | bash

Run:

dcode

🤔 What is this?

The fastest way to start using Deep Agents. deepagents-code is a pre-built coding agent in your terminal — similar to Claude Code or Cursor — powered by any LLM that supports tool calling. One install command and you're up and running, no code required.

What deepagents-code adds on top of the SDK:

  • Interactive TUI — rich terminal interface with streaming responses
  • Conversation resume — pick up where you left off across sessions
  • Web search — ground responses in live information
  • Remote sandboxes — run code in isolated environments (LangSmith, AgentCore, Daytona, Modal, Runloop, & more)
  • Persistent memory — agent remembers context across conversations
  • Custom skills — extend the agent with your own slash commands
  • Headless mode — run non-interactively for scripting and CI
  • Human-in-the-loop — approve or reject tool calls before execution

🔒 Security model

By default, dcode trusts the directory you run it in. Human-in-the-loop approval gates model-requested tool calls, but project artifacts are read before any approval prompt.

Do not run dcode in a directory you do not trust without a sandbox backend. For untrusted repositories, use a remote sandbox so execution is isolated from your machine. Running dcode in a directory lets that directory's files shape execution. See THREAT_MODEL.md for details.

Managed configuration

Administrators can enforce any supported config.toml setting with a read-only managed_config.toml using the same TOML schema:

  • macOS: /Library/Application Support/dcode/managed_config.toml
  • Windows: the ProgramData directory reported by the registry, usually C:\ProgramData\dcode\managed_config.toml. The %ProgramData% environment variable is ignored, because any user can change it.
  • Linux and other supported POSIX systems: /etc/dcode/managed_config.toml

Managed values override two lower layers: the DEEPAGENTS_CODE_ and compatibility environment variables, and ~/.deepagents/config.toml.

For an agent launch, managed values also override these CLI flags: the model, the auto-classifier model, the interpreter toggle, the programmatic tool-calling list, the recursion limit, the shell allow list, and the startup mode. A managed [sandboxes].default names the backend of a launch that is already sandboxed; it does not sandbox a launch that asked for no sandbox. A managed startup mode only revokes --auto-approve and --yolo; the mode itself reaches the runtime through the merged configuration, so a headless launch still works. Subcommand display flags such as dcode threads --relative are not overridden.

Tables merge recursively. Deny lists are unioned. An explicitly managed allow or trust list replaces lower-precedence grants. An empty managed list removes every lower-precedence grant.

A managed value whose type contradicts the manifest is ignored, and the lower-precedence value stays in effect. Two exceptions:

  • The enforced keys (startup.mode, startup.yolo_switcher, shell.allow_list, skills.extra_allowed_dirs, interpreter.enable_interpreter, interpreter.ptc, interpreter.ptc_acknowledge_unsafe, models.allowed, models.auto_classifier, runtime.recursion_limit, sandboxes.default, tracing.langsmith_redact) stop every command except config, doctor, auth path, and the help screens. If one is ignored, the user's flag or environment variable stays in force. This grants the escalation, or it removes the boundary that the policy declared. Three cases stop the launch and block /reload: a value the manifest rejects, a runtime.recursion_limit outside its bounds, and a key shadowed by a scalar ancestor (startup = "manual" in place of [startup] and mode). A managed [sandboxes].default that names an unavailable backend stops a sandboxed launch; a launch that asked for no sandbox is unaffected. A scalar at any known configuration section (for example, threads = "bad" instead of [threads]) also stops launch and reload rather than replacing the user's whole section.
  • Inside structured tables ([models.providers], [themes], [async_subagents], [sandboxes.providers]) the dedicated typed reader validates instead, so a wrong-typed managed leaf can displace a valid user leaf. The reader then falls back to the built-in default. A managed scalar still replaces a colliding user table there, the same as on the top-level merge.

[shell].allow_list is read from ~/.deepagents/config.toml and from DEEPAGENTS_CODE_SHELL_ALLOW_LIST, so a managed file can enforce it. A user can also grant themselves shell auto-approval from their own config file. An empty managed list removes every lower-precedence grant.

[models].allowed narrows dcode to exact provider:model specifications:

[models]
allowed = ["acme:production", "acme:production-fast"]

The rules:

  • A missing key preserves unrestricted model selection.
  • An empty list blocks all local model use.
  • A list that dcode cannot parse also blocks all local model use. A typo must not silently remove the restriction the user asked for. Fix or remove the key.
  • Matching is exact and splits only the first colon, so model identifiers may contain additional colons. Matching is also case-sensitive.
  • Entries must be fully qualified. A bare model name that you type at a prompt is first resolved to provider:model, the same as at construction, so gpt-5.6-terra matches an openai:gpt-5.6-terra entry. A bare name whose provider dcode cannot infer never matches.
  • Write a Bedrock model as bedrock:<id>. A bare Bedrock ID is rejected, because its version colon would make it a specification that nothing matches.
  • The list filters model discovery and the selector. Construction-time checks are authoritative for CLI flags, runtime switches, Auto classifiers, rubric graders, and explicit local subagent models.
  • A managed list replaces a user list. Managed default, recent, and Auto-classifier models must also appear in it.
  • [models.providers.<name>].models registers models additively. It does not grant permission, so a custom model may need both registration and an allowlist entry.

dcode never writes the managed file. Users can still save a preference. The theme, terminal-mapping, UI-toggle, and MCP-server screens, and the --auto-update flag, report when a managed value keeps a saved preference from taking effect. The model-default, recent-model, and Auto-classifier writers refuse a value outside the effective allowlist, and report the policy as the reason. Other save paths do not check the allowlist.

A missing managed file applies no policy. If one exists but is unreadable, not UTF-8, or invalid TOML, every command fails closed except the ones needed to diagnose it: --help, --version, help, config, doctor, and auth path. A managed file that becomes unusable later also blocks /reload: the session keeps the policy that was in force, and the reload reports that it kept it. Use dcode config path and dcode doctor to inspect its fixed path and parse health; dcode config also warns when the file exists but could not be parsed, or parses and declares a value that cannot be enforced.

A deny list that cannot be read denies everything rather than nothing. This covers a managed [mcp].disabled_servers that is neither an array of names nor a comma-separated string, and a [mcp] section that is not a table. A managed [mcp].enabled_project_server_approvals that is not an array is treated the same way: the key is present, so policy means to narrow access, and reading its presence as absence would leave both the user's approvals and the DEEPAGENTS_CODE_DANGEROUSLY_ENABLE_PROJECT_MCP_SERVERS bypass in force.

A corrupt ~/.deepagents/config.toml does not disable managed policy: the user file is ignored and managed values still apply.

Deployment tooling must create and protect this file with administrator or root permissions. dcode does not validate the file owner or mode. dcode provides no privileged writer. Deployment and sudo policy are the administrator's responsibility.

📖 Resources

📕 Releases & Versioning

See our Releases and Versioning policies.

💁 Contributing

As an open-source project in a rapidly developing field, we are extremely open to contributions, whether it be in the form of a new feature, improved infrastructure, or better documentation.

For detailed information on how to contribute, see the Contributing Guide.

🤝 Acknowledgements

This project was primarily inspired by Claude Code, and initially was largely an attempt to see what made Claude Code general purpose, and make it even more so.