Skip to main content
Deep Agents Code stores its configuration in the ~/.deepagents/ directory. The main config files are:
FileFormatPurpose
config.tomlTOMLModel defaults, provider settings, constructor params, profile overrides, themes, update settings
.envDotenvGlobal API keys, secrets, and other environment variables
hooks.jsonJSONExternal tool subscriptions to Deep Agents Code lifecycle events
.mcp.jsonJSONGlobal MCP server definitions
Files under ~/.deepagents/.state/ hold per-machine Deep Agents Code state and are managed automatically.

Provider credentials

Deep Agents Code needs an API key for each model provider you use. The recommended way to add one is the /auth credential manager. For non-interactive runs, set environment variables instead. If the same key is set in more than one place, see Key resolution order for which one wins. Open the credential manager from any session:
/auth
The manager lists the LLM providers available in your installation and marks the ones that already have a key set. Select a provider to add or replace its key, or remove one you have already stored. Keys you save here persist across sessions.
Each row shows the provider name followed by where its key comes from:
LabelMeaning
[stored]A key saved in this manager via /auth
[env: VARNAME]The key comes from environment variable VARNAME (the resolved name, such as DEEPAGENTS_CODE_OPENAI_API_KEY or OPENAI_API_KEY)
[missing]No key is stored and the env var is unset; select the row to paste one
The /auth prompt also has an optional base URL field. Leave it blank to use the provider’s default endpoint, or set a custom one to use with this key. The base URL is saved alongside the key. See Endpoints, keys, and gateways for how endpoints resolve, including with gateways.
A stored base URL is not a secret and may be logged; the key paired with it is never logged.
Keys are scoped to your user account on this machine — Deep Agents Code never transmits them anywhere except to the configured provider’s API.
/auth only manages LLM provider credentials. Tool credentials such as TAVILY_API_KEY (web search) and LANGSMITH_API_KEY (tracing) are read from the environment instead — set them in ~/.deepagents/.env or your shell.

Environment variables (CI and headless)

For non-interactive runs, CI/CD pipelines, or anywhere a TUI isn’t available, export the provider’s env var in your shell:
export ANTHROPIC_API_KEY="sk-ant-..."
export OPENAI_API_KEY="sk-..."

# Prefix with DEEPAGENTS_CODE_ to scope a key to Deep Agents Code only,
# leaving a shared key used by other CI steps untouched
export DEEPAGENTS_CODE_OPENAI_API_KEY="sk-..."
To keep keys in a file instead, define them in a .env file.

Key resolution order

When a provider’s key is set in more than one place, Deep Agents Code uses the first of these that is set:
  1. DEEPAGENTS_CODE_-prefixed env var — for example DEEPAGENTS_CODE_OPENAI_API_KEY as an inline shell export. The DEEPAGENTS_CODE_ prefix is the explicit “use this key in Deep Agents Code” override.
  2. App-stored key — entered in the /auth credential manager.
  3. Plain provider env var — for example OPENAI_API_KEY, from your shell or .env files.
An app-stored key wins over a plain env-var key for the same provider, but a DEEPAGENTS_CODE_-prefixed key wins over an app-stored key. The prefix is the way to override an already-stored key for a single run, without clearing it:
# With a key already stored via /auth, a plain env var does not override it.
# dcode still uses the app-stored key for this run:
OPENAI_API_KEY=sk-xxxx dcode -n "..."

# The DEEPAGENTS_CODE_ prefix does override it, for this run only:
DEEPAGENTS_CODE_OPENAI_API_KEY=sk-xxxx dcode -n "..."
This layering exists for the common case where your machine already exports a plain provider variable for some other purpose — a shared OPENAI_API_KEY used by other tools, scripts, or CI — that you do not want Deep Agents Code to reuse. An app-stored key or a DEEPAGENTS_CODE_-prefixed variable gives Deep Agents Code its own value while leaving the unprefixed one untouched for everything else, so the two never mix. Each provider’s API key and its endpoint (base_url) resolve as a pair from the same source. See Endpoints, keys, and gateways.

Enable web search with Tavily

The built-in web_search tool uses Tavily. Deep Agents Code shows a “Web search disabled — TAVILY_API_KEY is not set” notification on startup until you provide a key. Unlike model provider keys, Tavily is not managed by /auth; it is read directly from the environment.
1

Get a key

Sign up at tavily.com and copy the key (it starts with tvly-). The free tier is sufficient for most Deep Agents Code usage.
2

Add it to your environment

Add the key to ~/.deepagents/.env so every session picks it up:
~/.deepagents/.env
TAVILY_API_KEY=tvly-...
Or export it in your shell for a one-off session:
export TAVILY_API_KEY=tvly-...
Shell exports take precedence over .env values (see Loading order and precedence). To scope a key to Deep Agents Code only without affecting other tools that read TAVILY_API_KEY, use the DEEPAGENTS_CODE_ prefix: DEEPAGENTS_CODE_TAVILY_API_KEY=tvly-....
3

Reload or restart

In an existing session, run /reload to re-read .env files. On the next launch, the “Web search disabled” notification goes away and the agent can call web_search.

Environment variables

In addition to shell exports, Deep Agents Code reads environment variables from dotenv files, so you can keep API keys out of your shell profile and avoid duplicating .env files across projects.
~/.deepagents/.env
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...

Loading order and precedence

At startup, Deep Agents Code reads the nearest project .env, found by searching the directory you launch from and walking up through its parents (the first .env found wins), then ~/.deepagents/.env as a global fallback for all projects. A project .env wins over the global one, and neither overrides a value already set in your shell. Running /reload re-reads both .env files so you can change keys without restarting, with shell values still taking precedence. This applies to every variable Deep Agents Code reads (for example, TAVILY_API_KEY or the DEEPAGENTS_CODE_* settings). Provider API keys have additional resolution rules; see Provider credentials.

DEEPAGENTS_CODE_ prefix

All Deep Agents Code-specific environment variables use a DEEPAGENTS_CODE_ prefix (e.g., DEEPAGENTS_CODE_AUTO_UPDATE, DEEPAGENTS_CODE_DEBUG). See the environment variable reference for the full list. The prefix also works as an override mechanism for any environment variable Deep Agents Code reads, including third-party credentials. Deep Agents Code checks DEEPAGENTS_CODE_{NAME} first, then falls back to {NAME}:
~/.deepagents/.env
# Give Deep Agents Code its own value, without affecting other tools
DEEPAGENTS_CODE_OPENAI_API_KEY=sk-cli-only

# Or set it empty so Deep Agents Code ignores a key exported in your shell
DEEPAGENTS_CODE_ANTHROPIC_API_KEY=

Config file

~/.deepagents/config.toml lets you customize model providers, set defaults, and pass extra parameters to model constructors. This section covers:

Default and recent model

[models]
default = "ollama:qwen3:4b"             # your intentional long-term preference
recent = "google_genai:gemini-3.5-flash"   # last /model switch (written automatically)
[models].default always takes priority over [models].recent. The /model command only writes to [models].recent, so your configured default is never overwritten by mid-session switches. To remove the default, use /model --default --clear or delete the default key from the config file.

Default and recent agent

[agents]
default = "backend-dev"  # your intentional long-term preference (Ctrl+S in /agents picker)
recent = "frontend-dev"  # last /agents switch (written automatically)
[agents].default always takes priority over [agents].recent. Selecting an agent in the /agents picker with Enter writes to recent; pressing Ctrl+S on the highlighted row pins it as default. Pressing Ctrl+S again on the same row clears the default. Explicit -a/--agent always overrides both, and -r/--resume bypasses both so the thread’s original agent is restored. See Command reference for related flags.

Provider configuration

Each provider is a TOML table under [models.providers]:
[models.providers.<name>]
models = ["gpt-4o"]
api_key_env = "OPENAI_API_KEY"
base_url = "https://api.openai.com/v1"
class_path = "my_package.models:MyChatModel"
enabled = true

[models.providers.<name>.params]
temperature = 0
max_tokens = 4096

[models.providers.<name>.params."gpt-4o"]
temperature = 0.7
Providers have the following configuration options:
models
string[]
optional
A list of model names to show in the interactive /model switcher for the provider defined as <name>. For providers that already ship with model profiles, any names you add here appear in addition to bundled ones (useful for newly released models that haven’t been added to the package yet). For arbitrary providers, this list is the only source of models in the switcher.Models listed here bypass any applied profile-based filtering criteria, always appearing in the switcher. This makes it the recommended way to surface models that are excluded because their profile lacks tool_calling support or doesn’t exist yet.This key is optional. You can always pass any model name directly to /model or --model regardless of whether it appears in the switcher; the provider validates the name at request time.
api_key_env
string
optional
The name of the environment variable that holds the API key (e.g., "OPENAI_API_KEY"). Deep Agents Code reads the credential from this env var at startup to verify access before creating the model.Most chat model packages read from a default env var automatically. See the Provider reference table for which variable name each built-in provider checks. For a provider not in that table, set api_key_env to its variable name (see Arbitrary providers).
base_url
string
optional
Override the base URL used by the provider, if supported. Refer to your provider packages’ reference docs for more info.See Compatible APIs for pointing a built-in provider at a wire-compatible endpoint, or Arbitrary providers for one configured via class_path.
base_url_env
string
optional
Name of the environment variable that holds this provider’s base URL, parallel to api_key_env. Reach for this instead of base_url when the endpoint comes from the environment rather than a fixed value — for example a gateway URL that differs by machine or CI job — so it can change without editing config.toml and can take part in endpoint resolution and key/endpoint pairing (see Endpoints, keys, and gateways). It also extends those to providers outside the built-in set; see Arbitrary providers.If both are set, the static base_url wins:
[models.providers.example]
base_url = "https://fixed.example/v1"   # used
base_url_env = "EXAMPLE_BASE_URL"        # ignored while base_url is set
params
object
optional
Extra keyword arguments forwarded to the model constructor. Flat keys (e.g., temperature = 0) apply to every model from this provider. Model-keyed sub-tables (e.g., [params."gpt-4o"]) override individual values for that model only; the merge is shallow (model wins on conflict).Do not put credentials (e.g., api_key) in params. Use api_key_env to point at an environment variable instead.
profile
object
optional
(Advanced) Override fields in the model’s runtime profile (e.g., max_input_tokens). Flat keys apply to every model from this provider. Model-keyed sub-tables (e.g., [profile."claude-sonnet-4-5"]) override individual values for that model only; the merge is shallow (model wins on conflict). These overrides are applied after the model is created, so they take effect for context-limit display, auto-summarization, and any other feature that reads the profile. See Profile overrides for examples and the --profile-override flag.
class_path
string
optional
Used for arbitrary model providers. Fully-qualified Python class in module.path:ClassName format. When set, Deep Agents Code imports and instantiates this class directly for provider <name>. The class must be a BaseChatModel subclass.
enabled
boolean
default:"true"
optional
Whether this provider appears in the /model selector. Set to false to hide a provider that was auto-discovered from an installed package (e.g., a transitive dependency you don’t want cluttering the model switcher). You can still use a disabled provider directly via /model provider:model or --model.

Model constructor params

The params field forwards extra arguments to the model constructor. To give one model different values, add a model-keyed sub-table so you do not have to duplicate the whole provider config:
[models.providers.ollama]
models = ["qwen3:4b", "llama3"]

[models.providers.ollama.params]
temperature = 0
num_ctx = 8192

[models.providers.ollama.params."qwen3:4b"]
temperature = 0.5
num_ctx = 4000
With this configuration:
  • ollama:qwen3:4b gets {temperature: 0.5, num_ctx: 4000} — model overrides win.
  • ollama:llama3 gets {temperature: 0, num_ctx: 8192} — no override, provider-level params only.
The merge is shallow: any key present in the model sub-table replaces the same key from the provider-level params, while keys only at the provider level are preserved.
For one-off adjustments without editing config.toml, pass a JSON object via --model-params at launch or mid-session with /model. CLI flags take highest priority over the config file. See Model parameters on the providers page for syntax and provider-specific examples.

Profile overrides (Advanced)

Override fields in the model’s runtime profile to change how Deep Agents Code interprets model capabilities. See ModelProfile for the full list of overridable fields. The most common use case is lowering max_input_tokens to trigger auto-summarization earlier — useful for testing or for constraining context usage:
# Apply to all models from this provider
[models.providers.anthropic.profile]
max_input_tokens = 4096
Per-model sub-tables work the same way as params — the model-level value wins on conflict:
[models.providers.anthropic.profile]
max_input_tokens = 4096

# This model gets a higher limit
[models.providers.anthropic.profile."claude-sonnet-4-5"]
max_input_tokens = 8192
Profile overrides are merged into the model’s profile after creation. Any feature that reads the profile — context-limit display in the status bar, auto-summarization thresholds, capability checks — will see the overridden values.
To override model profile fields at runtime without editing the config file, pass a JSON object via --profile-override:
dcode --profile-override '{"max_input_tokens": 4096}'

# Combine with --model
dcode --model google_genai:gemini-3.5-flash --profile-override '{"max_input_tokens": 4096}'

# In non-interactive mode
dcode -n "Summarize this repo" --profile-override '{"max_input_tokens": 4096}'
These are merged on top of config file profile overrides (CLI wins). The priority chain is: model default < config.toml profile < CLI --profile-override.--profile-override values persist across mid-session /model hot-swaps — switching models re-applies the override to the new model.

Adding models to the interactive switcher

Some providers (e.g. langchain-ollama) don’t bundle model profile data (see Provider reference for full listing). When this is the case, the interactive /model switcher won’t list models for that provider. You can fill in the gap by defining a models list in your config file for the provider:
[models.providers.ollama]
models = ["gemma4", "qwen3.6", "granite4.1:3b"]
The /model switcher will now include an Ollama section with these models listed. This is entirely optional. You can always switch to any model by specifying its full name directly:
/model ollama:qwen3.6:27b
When langchain-ollama is installed and the daemon is reachable, Deep Agents Code auto-discovers locally pulled models and merges them into the switcher—no models list required. Run /reload to refresh after pulling new models, or set DEEPAGENTS_CODE_OLLAMA_DISCOVERY=0 to opt out.

Custom base URL

Some provider packages accept a base_url to override the default endpoint. For example, langchain-ollama defaults to http://localhost:11434 via the underlying ollama client. To point it elsewhere, set base_url in your configuration:
[models.providers.ollama]
base_url = "http://your-host-here:port"
Refer to your provider’s reference documentation for compatibility information and additional considerations.

Compatible APIs

For providers that expose APIs that are wire-compatible with OpenAI or Anthropic, you can use the existing langchain-openai or langchain-anthropic packages by pointing base_url at the provider’s endpoint:
[models.providers.openai]
base_url = "https://api.example.com/v1"
api_key_env = "EXAMPLE_API_KEY"
models = ["my-model"]
[models.providers.anthropic]
base_url = "https://api.example.com"
api_key_env = "EXAMPLE_API_KEY"
models = ["my-model"]
Any features added on top of the official spec by the provider will not be captured. If the provider offers a dedicated LangChain integration package, prefer that instead.
The OpenAI provider defaults to the Responses API, which most OpenAI-compatible gateways do not implement. If your provider only supports the Chat Completions API, invocation will likely fail. Disable the Responses API explicitly:
[models.providers.openai.params]
use_responses_api = false

Arbitrary providers

Deep Agents Code works with any tool calling LLM available as a LangChain BaseChatModel. The built-in providers work out of the box; a less common or in-house model takes a little more setup. Point class_path at its BaseChatModel subclass and Deep Agents Code imports and instantiates the class directly.
[models.providers.my_custom]
class_path = "my_package.models:MyChatModel"
api_key_env = "MY_API_KEY"
base_url = "https://my-endpoint.example.com"

[models.providers.my_custom.params]
temperature = 0
max_tokens = 4096
api_key_env and base_url are optional. To read the endpoint from an environment variable instead of hardcoding base_url, use base_url_env; it then resolves and pairs with the key the same way as for the built-in providers (see Endpoints, keys, and gateways). class_path providers are expected to handle their own authentication internally — useful when your model uses custom auth (JWT tokens, proprietary headers, mTLS, etc.) rather than a standard API key:
[models.providers.xyz]
class_path = "abc.integrations.deepagents:DeepAgentsXYZChat"
models = ["abc-xyz-1"]

[models.providers.xyz.params]
bypass_auth = true
temperature = 0
With this config, switch to the model with /model xyz:abc-xyz-1 or --model xyz:abc-xyz-1.
Deep Agents Code requires tool calling support. If your custom model supports tool calling but Deep Agents Code doesn’t know about it, declare it in the provider profile:
[models.providers.xyz.profile]
tool_calling = true
max_input_tokens = 128000
Although optional, setting max_input_tokens to your model’s context window is strongly encouraged. Without it, Deep Agents Code cannot show how full the context is, and auto-summarization falls back to a fixed trigger (around 170,000 tokens) instead of a fraction of your model’s window. For a model with a smaller window, summarization may not run before you reach the model’s hard limit, so requests start failing once the conversation grows.
Because Deep Agents Code imports the class_path class at startup, the package that defines it must be importable from the same environment that runs dcode. Built-in providers ship as install extras, but a custom or in-house package is not one. Install it into the dcode environment with the --package flag:
dcode --install my_package --package
In a session, run /install my_package --package --force. Both install the package alongside dcode. If the package is missing or cannot be imported, Deep Agents Code skips the provider and its models do not appear in /model. When you switch to my_custom:my-model-v1 (via /model or --model), the model name (my-model-v1) is passed as the model kwarg:
MyChatModel(model="my-model-v1", base_url="...", api_key="...", temperature=0, max_tokens=4096)
class_path executes arbitrary Python code from your config file. This has the same trust model as pyproject.toml build scripts — you control your own machine.
Your provider package may optionally provide model profiles at a _PROFILES dict in <package>.data._profiles in lieu of defining them under the models key. See LangChain model profiles for more info.

Endpoints, keys, and gateways

An API key and the endpoint it is sent to have to match: the endpoint has to accept that key, or the request will likely fail. Deep Agents Code resolves the key and endpoint together, so overriding one updates the other to match. For example, if you replace a gateway-provisioned key with your own, Deep Agents Code also drops the gateway endpoint, so your key goes to the provider directly instead of to a gateway that would reject it.

How base_url resolves

Deep Agents Code resolves a provider’s endpoint in this order (first match wins):
  1. base_url in config.toml for the provider.
  2. The DEEPAGENTS_CODE_-prefixed endpoint variable.
  3. The plain endpoint variable in the environment (for example, OPENAI_BASE_URL).
  4. The endpoint saved with a /auth credential. This step applies the saved endpoint for a provider that has no endpoint variable—such as a provider you add without declaring base_url_env. Steps 2-3 have no variable to read for these, so the saved endpoint is used directly here. For a provider that does have an endpoint variable, the saved endpoint already took effect at step 2 or 3 (it is written to that variable), so this step changes nothing. Either way, an endpoint entered in /auth applies.
  5. The provider SDK’s own default endpoint, when none of the above is set.
Resolved endpoints are delivered to the model as the base_url constructor argument.
As with API keys, the DEEPAGENTS_CODE_ prefix scopes the endpoint to Deep Agents Code without affecting other tools. For any other provider, declare the name with base_url_env and the endpoint resolves and pairs the same way:
[models.providers.myprovider]
api_key_env = "MYPROVIDER_API_KEY"
base_url_env = "MYPROVIDER_BASE_URL"
models = ["my-model"]
A literal base_url wins over base_url_env, so set only the one you need:
[models.providers.myprovider]
base_url = "https://fixed.example/v1"   # used
base_url_env = "MYPROVIDER_BASE_URL"    # ignored while base_url is set

Overrides keep the pair together

When you store a key with /auth, the endpoint you enter (or the provider’s default, if left blank) is applied together with the key. Storing a key with a blank base URL also clears any endpoint already set in your environment (for example, a gateway OPENAI_BASE_URL your shell exports), so your key goes to the provider’s default endpoint instead of to that gateway.
Scope both the key and the endpoint to Deep Agents Code
DEEPAGENTS_CODE_OPENAI_API_KEY=sk-cli-only
DEEPAGENTS_CODE_OPENAI_BASE_URL=https://api.openai.com/v1

Managed gateways

On a machine provisioned with a model gateway (for example, the LangSmith gateway), the gateway typically exports a gateway key and the matching endpoint variable (OPENAI_BASE_URL, ANTHROPIC_BASE_URL, or GOOGLE_GEMINI_BASE_URL) together. Deep Agents Code uses that pair by default, so no configuration is needed. To use your own key instead, store it with /auth (leave the base URL blank for the provider default, or set it explicitly), or set the DEEPAGENTS_CODE_ prefixed key and endpoint. Both override the gateway pair without leaving a mismatched endpoint behind.

Skill directory allowlist

By default, when Deep Agents Code loads skills it validates that a resolved skill file path stays inside one of the standard skill directories. This prevents symlinks inside skill directories from reading arbitrary files outside those roots. If you store shared skill assets in a non-standard location and use symlinks from a standard skill directory to reference them, you can add that location to the containment allowlist. This does not add a new skill discovery location: skills are still only discovered from the standard directories.
extra_allowed_dirs
string[]
optional
Paths added to the skill containment allowlist. Supports ~ expansion.
[skills]
extra_allowed_dirs = [
    "~/shared-skills",
    "/opt/team-skills",
]
Alternatively, set the DEEPAGENTS_CODE_EXTRA_SKILLS_DIRS environment variable as a colon-separated list:
export DEEPAGENTS_CODE_EXTRA_SKILLS_DIRS="~/shared-skills:/opt/team-skills"
When the environment variable is set, it takes precedence over the config file value. Changes take effect on /reload.

Themes

Use /theme to open an interactive theme selector. Navigate the list to preview themes in real-time, press Enter to persist your choice to config.toml. Deep Agents Code ships with many built-in themes. The default theme is langchain, a dark theme with LangChain-branded colors. The selected theme is persisted under [ui]:
[ui]
theme = "langchain-dark"

User-defined themes

Define custom themes under [themes.<name>] sections in config.toml. Each section requires label (str). dark (bool) defaults to false if omitted — set to true for dark themes. All color fields are optional — omitted fields fall back to the built-in dark or light palette based on the dark flag.
[themes.my-solarized]
label = "My Solarized"
dark = true
primary = "#268BD2"
warning = "#B58900"

# Theme names with spaces require TOML quoting
[themes."ocean breeze"]
label = "Ocean Breeze"
primary = "#0077B6"
background = "#CAF0F8"
User-defined themes appear alongside built-in themes in the /theme selector.

Override built-in theme colors

To tweak a built-in theme’s colors without creating a new theme, use a [themes.<builtin-name>] section. Only color fields are read — label and dark are inherited from the built-in:
[themes.langchain]
primary = "#FF5500"
Omitted color fields retain the existing built-in values. Changes to [themes.*] sections take effect on /reload.

Map themes to terminals

If you switch between terminals with different color schemes (for example, a dark iTerm and a light Apple Terminal), map each one to a theme under [ui.terminal_themes]. Deep Agents Code matches the shell’s TERM_PROGRAM and applies the mapped theme automatically:
[ui.terminal_themes]
"Apple_Terminal" = "langchain-light"
"iTerm.app" = "langchain"
Press T in the /theme picker to save the highlighted theme for the current terminal, or run echo $TERM_PROGRAM to find your terminal’s identifier and add it by hand.

Picker shortcuts

In the /theme selector:
  • N toggles between display labels and canonical registry keys—the keys are what [ui] theme and [ui.terminal_themes] accept.
  • T saves the highlighted theme into [ui.terminal_themes] for the current TERM_PROGRAM. The mapped theme is badged (default) in the picker.

Common TERM_PROGRAM values

Keys are matched verbatim against the environment variable—quote them in TOML when they contain dots or special characters.
TerminalTERM_PROGRAM
Apple TerminalApple_Terminal
iTerm2iTerm.app
WezTermWezTerm
VS Code integrated terminalvscode
Ghosttyghostty

Resolution order

Deep Agents Code resolves a theme on every launch using this precedence:
  1. DEEPAGENTS_CODE_THEME environment variable (explicit override).
  2. [ui.terminal_themes] mapping for the current TERM_PROGRAM.
  3. [ui] theme saved preference (set by /theme).
  4. The built-in default (langchain).

Auto-update

Deep Agents Code can automatically check for and install updates.
[update]
auto_update = true
The environment variable takes precedence over the config file. When enabled, Deep Agents Code checks PyPI for a newer version at session start and automatically upgrades using the detected install method (uv, Homebrew, or pip). When disabled (default), Deep Agents Code shows an update hint with the appropriate install command instead. You can also check for and install updates manually at any time with the /update slash command, which bypasses the cache and reports success or failure inline. After an upgrade, Deep Agents Code shows a “what’s new” banner on the next launch with a link to the changelog. At session exit, if a newer version was detected during the session, an update banner is displayed as a reminder.

Uninstall

To remove the dcode and deepagents-code binaries and the isolated tool environment, run:
uv tool uninstall deepagents-code
The uninstall command does not remove user configuration or session data. Deep Agents Code stores those files under ~/.deepagents/, including config.toml, hooks.json, the global .env, and .state/ contents such as saved sessions and credentials. To delete that data as well, run:
rm -rf ~/.deepagents

Managed deployments

The install script supports running as root, targeting macOS MDM tools (Kandji, Jamf, etc.) that execute scripts in a minimal root environment. When id -u is 0, the script:
  1. Resolves the real console user’s HOME (via /dev/console or a /Users directory scan)
  2. chowns all created files back to the target user after each install step
Non-root installs are unaffected: all root-specific code paths short-circuit when not running as root. To pre-configure auto-update for managed installs, set DEEPAGENTS_CODE_AUTO_UPDATE=1 in the user’s shell profile or deploy a config.toml with [update] auto_update = true to ~/.deepagents/config.toml. To suppress automatic updates and update checks entirely, set DEEPAGENTS_CODE_NO_UPDATE_CHECK=1. To route every user’s model traffic through a managed gateway (provisioning a gateway key and base URL fleet-wide), see Managed gateways.

Environment variable reference

All Deep Agents Code-specific environment variables use the DEEPAGENTS_CODE_ prefix. See DEEPAGENTS_CODE_ prefix for how the prefix also works as an override for third-party credentials.
DEEPAGENTS_CODE_AUTO_UPDATE
string
optional
Enable automatic Deep Agents Code updates (1, true, or yes).
DEEPAGENTS_CODE_DEBUG
string
optional
Enable verbose debug logging to a file. Accepts 1, true, yes, on (case-insensitive) as enabled; 0, false, no, off, empty string, or unset disables it. When enabled, the per-session server log file is preserved on shutdown and its path is printed to stderr for triage.
DEEPAGENTS_CODE_DEBUG_FILE
string
default:"/tmp/deepagents_debug.log"
optional
Path for the debug log file.
DEEPAGENTS_CODE_EXTRA_SKILLS_DIRS
string
optional
Colon-separated paths added to the skill containment allowlist.
DEEPAGENTS_CODE_LANGSMITH_PROJECT
string
optional
Override the LangSmith project name for agent traces. See Trace with LangSmith.
DEEPAGENTS_CODE_NO_UPDATE_CHECK
string
optional
Disable automatic update checking when set.
DEEPAGENTS_CODE_SHELL_ALLOW_LIST
string
optional
Comma-separated shell commands to allow (or recommended / all).
DEEPAGENTS_CODE_USER_ID
string
optional
Attach a user identifier to LangSmith trace metadata.

External editor

Press Ctrl+X or type /editor to compose prompts in an external editor. Deep Agents Code checks $VISUAL, then $EDITOR, then falls back to vi (macOS/Linux) or notepad (Windows). GUI editors (VS Code, Cursor, Zed, Sublime Text, Windsurf) automatically receive a --wait flag so Deep Agents Code blocks until you close the file.
# Set in your shell profile (~/.zshrc, ~/.bashrc, etc.)
export VISUAL="code"    # GUI editor (--wait auto-injected)
export EDITOR="nvim"    # Terminal fallback

Hooks

Hooks let external programs react to Deep Agents Code lifecycle events. Configure commands in ~/.deepagents/hooks.json and it pipes a JSON payload to each matching command’s stdin whenever an event fires. Hooks run fire-and-forget in a background thread — they never block Deep Agents Code and failures are logged without interrupting your session.

Setup

Create ~/.deepagents/hooks.json:
{
  "hooks": [
    {
      "command": ["bash", "-c", "cat >> ~/deepagents-events.log"],
      "events": ["session.start", "session.end"]
    }
  ]
}
Now every time a session starts or ends, Deep Agents Code appends the event payload to ~/deepagents-events.log.

Hook configuration

The config file contains a single hooks array. Each entry has:
command
list[str]
required
Command and arguments to run. No shell expansion: use ["bash", "-c", "..."] if needed.
events
list[str]
optional
Event names to subscribe to. Omit or leave empty to receive all events.
{
  "hooks": [
    {
      "command": ["python3", "my_handler.py"],
      "events": ["session.start", "task.complete"]
    },
    {
      "command": ["bash", "log_everything.sh"]
    }
  ]
}
The second hook above has no events filter, so it receives every event Deep Agents Code emits.

Payload format

Each hook command receives a JSON object on stdin with an "event" key plus event-specific fields:
{
  "event": "session.start",
  "thread_id": "abc123"
}

Events reference

session.start

Fired when an agent session begins (both interactive and non-interactive modes).
thread_id
string
required
The session thread identifier.

session.end

Fired when a session exits.
thread_id
string
required
The session thread identifier.

user.prompt

Fired in interactive mode when the user submits a chat message. No additional fields.

input.required

Fired when the agent requires human input (human-in-the-loop interrupt). No additional fields.

permission.request

Fired before the approval dialog when one or more tool calls need user permission.
tool_names
list[str]
required
Names of the tools requesting approval.

tool.error

Fired when a tool call returns an error.
tool_names
list[str]
required
Names of the tool(s) that errored.

task.complete

Fired when the agent finishes its current task (the streaming loop ends without further interrupts).
thread_id
string
required
The session thread identifier.

context.compact

Fired before Deep Agents Code compacts (summarizes) the conversation context. No additional fields.

Execution model

  • Background thread: Hook subprocesses run in a thread via asyncio.to_thread so the main event loop is never blocked.
  • Concurrent dispatch: When multiple hooks match an event, they run concurrently in a thread pool.
  • 5-second timeout: Each command has a 5-second timeout. Commands that exceed this are killed.
  • Fire-and-forget: Errors are caught per-hook and logged at debug/warning level. A failing hook never crashes or stalls Deep Agents Code.
  • Lazy loading: The config file is read once on the first event dispatch and cached for the rest of the session.
  • No shell expansion: Commands are executed directly (not through a shell). Wrap in ["bash", "-c", "..."] if you need shell features like pipes or variable expansion.

Hook examples

{
  "hooks": [
    {
      "command": ["bash", "-c", "jq -c . >> ~/.deepagents/hook-events.jsonl"],
      "events": []
    }
  ]
}
{
  "hooks": [
    {
      "command": [
        "bash", "-c",
        "osascript -e 'display notification \"Agent finished\" with title \"Deep Agents\"'"
      ],
      "events": ["task.complete"]
    }
  ]
}
Write a handler script that reads the JSON payload from stdin:
my_handler.py
import json
import sys

payload = json.load(sys.stdin)
event = payload["event"]

if event == "session.start":
    print(f"Session started: {payload['thread_id']}", file=sys.stderr)
elif event == "permission.request":
    print(f"Approval needed for: {payload['tool_names']}", file=sys.stderr)
~/.deepagents/hooks.json
{
  "hooks": [
    {
      "command": ["python3", "my_handler.py"],
      "events": ["session.start", "permission.request"]
    }
  ]
}

Security considerations

Hooks follow the same trust model as Git hooks or shell aliases — any user who can write to ~/.deepagents/hooks.json can execute arbitrary commands. This is by design:
  • No command injection: Payload data flows only to stdin as JSON, never to command-line arguments. json.dumps handles escaping.
  • No shell by default: Commands run with shell=False, preventing shell injection.
  • Malformed config: Invalid JSON or unexpected types produce logged warnings, not security issues.
Only add hooks from sources you trust. A hook has the same permissions as your user account.