Deep Agents Code - Interactive AI coding assistant.
Configure runtime log level and optional file logging for target.
Intended to be called once on the deepagents_code package logger; child
module loggers reach the same handlers via propagation, so individual modules
do not configure logging themselves.
DEEPAGENTS_CODE_LOG_LEVEL controls the package logger level independently of
file logging. If it is unset, DEEPAGENTS_CODE_DEBUG=1 defaults to DEBUG;
otherwise the runtime level defaults to INFO.
When DEEPAGENTS_CODE_DEBUG is truthy, a file handler is attached. The log
file defaults to DEFAULT_DEBUG_FILE but can be overridden with
DEEPAGENTS_CODE_DEBUG_FILE. The handler appends (mode='a') so logs are
preserved across separate process runs. Calling this again with the same
resolved path does not stack duplicate handlers: the existing tagged handler
is reused and its level re-applied. If the resolved path changes, the stale
handler is closed and replaced.
Attach the in-memory buffer handler to target (idempotent).
Lowers target's level to at most INFO so the console shows a useful tail
even when DEEPAGENTS_CODE_DEBUG is off; never raises the level. In
__init__.py this runs before configure_debug_logging, which then sets
the final level (honoring DEEPAGENTS_CODE_DEBUG and
DEEPAGENTS_CODE_LOG_LEVEL) over this INFO floor — so any startup warnings
configure_debug_logging emits are captured by the already-installed buffer.
On a fresh NOTSET logger the NOTSET branch forces INFO without
consulting DEEPAGENTS_CODE_LOG_LEVEL; the > INFO and no env branch only
matters on reconfiguration (e.g. an importlib.reload), where it preserves
an explicit DEEPAGENTS_CODE_LOG_LEVEL rather than clobbering it with INFO.
Lowering the level does not spill log output onto the terminal: because this
handler is present in the propagation chain, Logger.callHandlers finds a
handler (found > 0) and Python's lastResort stderr handler is never
consulted. The exception is an embedding process that attaches its own
INFO-or-lower handler to the root logger, which would then see the
propagated records.
Note: this runs as an import-time side effect (see __init__.py), so every
import deepagents_code attaches the handler and may lower the package
logger's level to INFO for the lifetime of the process.
Unified slash-command registry.
Every slash command is declared once as a SlashCommand entry in COMMANDS.
Bypass-tier frozensets and autocomplete entries are derived automatically — no
other file should hard-code command metadata.
LangChain brand colors and semantic constants for the app.
Single source of truth for color values used in Python code (Rich markup,
Content.styled, Content.from_markup). CSS-side styling should reference
Textual CSS variables: built-in variables
($primary, $background, $text-muted, $error-muted, etc.) are set via
register_theme() in DeepAgentsApp.__init__, while the few app-specific
variables ($mode-bash, $mode-command, $mode-incognito, $skill,
$skill-hover, $tool, $tool-hover) are backed by these constants via
App.get_theme_variable_defaults().
Code that needs custom CSS variable values should call
get_css_variable_defaults(dark=...). For the full semantic color palette, look
up the ThemeColors instance via get_registry().
Users can define custom themes in ~/.deepagents/config.toml under
[themes.<name>] sections. Each new theme section must include label (str);
dark (bool) defaults to False if omitted (set to True for dark themes).
Color fields are optional and fall back to the built-in dark/light palette based
on the dark flag. Sections whose name matches a built-in theme override its
colors without replacing it. See _load_user_themes() for details.
Main entry point and loop.
Validation and environment-variable expansion for MCP server config.
Resolves ${VAR} and ${VAR:-default} references in the supported
configuration fields (command, url, args, env, headers) and
validates their types. A ${VAR:-default} reference falls back to
default when VAR is unset or empty (POSIX :- semantics).
Classifier-backed approval policy for the local interactive TUI.
Utilities for handling image and video media from clipboard and files.
Server-side helpers for drafting acceptance criteria from goal objectives.
UI-agnostic interaction interface for MCP OAuth login.
The OAuth login flow needs to ask the user a few things during the
handshake — open or display the authorize URL, accept a pasted callback
URL when the provider has no loopback redirect, show RFC 8628 device-code
instructions, and report success or failure. The CLI uses print and
input; a TUI surface needs in-app widgets instead. OAuthInteraction is
the small Protocol both implementations satisfy, and CliOAuthInteraction
is the existing CLI behavior preserved as one implementation of that
interface.
Important: implementations must never embed access or refresh tokens in user-facing messages. The interaction surface only ever sees authorize URLs, callback URLs, device codes, and short status strings, so leaks come from misuse, not from this interface's shape.
Textual UI application.
Ask user middleware for interactive question-answering during agent execution.
Agent management and creation.
Shared recursive types for JSON-compatible data.
External editor support for composing prompts.
Machine-readable JSON output helpers for CLI subcommands.
This module deliberately stays stdlib-only so it can be imported from CLI startup paths without pulling in unnecessary dependency trees.
Storage paths for offloaded conversation history.
Update lifecycle for deepagents-code.
Handles version checking against PyPI (with caching), install-method detection, auto-upgrade execution, config-driven opt-in/out, notification throttling, and "what's new" tracking.
Most public entry points absorb errors and return sentinel values.
set_auto_update raises on write failures so callers can surface
actionable feedback.
Schema and middleware for per-checkpoint state restored when resuming.
ResumeState declares several checkpointed, schema-private channels. They fall
into two groups with different write paths:
Written from inside the graph on successful model turns:
_context_tokens — total context tokens from the latest
AIMessage.usage_metadata, written by ResumeStateMiddleware.after_model.
Powers /tokens and the status bar._model_spec / _model_params — the model and invocation params effectively
in use for the turn, written by ConfigurableModelMiddleware after a
successful model call. Lets dcode -r restore the model the resumed thread
was actually using instead of falling back to the user's global default.Written through the main graph or by the TUI client via aupdate_state (see
DeepAgentsApp._persist_goal_rubric_state) — these are user/agent-owned. Their
write sites are called out below:
_goal_objective / _goal_status / _goal_rubric / _goal_status_note —
the accepted goal and its lifecycle status. _goal_objective/_goal_rubric
are client-only, but _goal_status/_goal_status_note are also written
from inside the graph by the agent's update_goal tool._pending_goal_completion_note — optional agent-provided completion evidence
awaiting the post-turn rubric result._sticky_rubric — the TUI-owned persistent rubric. This is separate from
the public rubric graph input so one-shot rubric turns can be checkpointed
without being restored as sticky state._pending_goal_objective / _pending_goal_rubric / _pending_goal_kind /
_pending_goal_request_id — a proposed goal or amendment and its originating
request, written by GoalCriteriaMiddleware inside the main graph, then
cleared by the TUI when the user accepts or rejects it.All of these are facts the CLI reads back from state_values on thread resume
so it can rehydrate the session without replaying or re-tokenizing history.
The model-turn channels are persisted from inside the graph (rather than via a
separate client-side aupdate_state call) so the write rides the same checkpoint
as the model response and avoids creating a standalone UpdateState run in
LangSmith. Because they are versioned channel state, resuming a specific
checkpoint yields the values as of that checkpoint — not a thread-level
aggregate. Accepted goal/rubric state is client-written because the user sets it
outside any model turn; pending criteria proposals and agent-driven status
updates are graph-written. Both paths work identically against local and remote
(HTTP) graphs.
Reasoning effort support for /effort.
Supported levels and defaults come from LangChain model profiles. Provider
integrations translate the standard reasoning_effort constructor parameter
into their native request shapes.
Goal tools exposed to the agent for persisted TUI goals.
Input handling utilities including image/video tracking and file mention parsing.
Approval-mode state shared by the Textual client and agent server.
Configuration, constants, and model creation.
MCP (Model Context Protocol) tools loader.
This module provides async functions to load and manage MCP servers using
langchain-mcp-adapters, supporting Claude Desktop style JSON configs.
It also supports automatic discovery of .mcp.json files from user-level
and project-level locations.
Server-side graph entry point for langgraph dev.
This module is referenced by the generated langgraph.json and exposes a graph
factory that the LangGraph server can load and serve.
The graph is created by make_graph(), which reads configuration from
ServerConfig.from_env() — the same dataclass the CLI uses to write the
configuration via ServerConfig.to_env(). This shared schema ensures the two
sides stay in sync.
User-level credential storage for model providers.
Persists API keys (and, in the future, OAuth tokens) under
~/.deepagents/.state/auth.json (file mode 0600, parent 0700) so users can
enter credentials directly in the TUI rather than exporting environment
variables before launch.
Security notes:
ApiKeyCredential.key) must never be logged, formatted
via %r/!r, or interpolated into exception messages — every helper here
reports only structural facts ("set credential for provider X").O_EXCL | 0o600 to a temp path, then atomically
replaced. A second chmod 0600 runs on the final path so filesystems that
ignore the create-mode argument still end up with private perms. Permission
failures are reported back to the caller in WriteOutcome.warnings so the
UI can surface them to the user — logger.warning alone is invisible
inside a Textual TUI session.Persistent store of MCP server names the user has disabled.
Disabled servers are skipped at config merge time so their tools never
reach the agent and no connection is attempted. State lives under
[mcp].disabled_servers in ~/.deepagents/config.toml, alongside the
user's other MCP configuration.
The store keys on server name alone. Two configs that both declare a
github server will both be disabled by a single entry — intentional,
since the agent cannot distinguish overlapping names at runtime anyway
(later configs in the merge order win).
Canonical manifest and resolver for every user-tunable scalar config option.
This module is the single source of truth for the configuration surface: the
set of options, their types, typed defaults, env-var names, and config.toml
locations. The typed defaults for config-file-only options (notably the
[interpreter] section) live here as module constants, and Settings derives
its dataclass defaults from them — so a default is defined in exactly one place.
resolve_scalar is the shared resolution engine used both by the runtime
(Settings.from_environment) and by the config CLI command, so introspection
can never drift from what the app actually reads. Resolution precedence mirrors
the loaders: a DEEPAGENTS_CODE_-prefixed env var beats the canonical name,
env beats config.toml, and the typed default is the final fallback. A
malformed numeric/list/PTC value, an unrecognized boolean token, or a
wrong-typed TOML value is logged and falls back to the next layer rather than
raising, so a bad config never blocks startup.
Structured, user-defined config is not a flat scalar option and is parsed by
dedicated typed loaders elsewhere. The manifest references [threads].columns
and [warnings].suppress as STRUCTURED options for discovery; other tables
such as [models.providers.*] and [themes.*] are handled entirely by their
own loaders and the manifest does not enumerate them at all.
Import discipline: the module top level stays stdlib + _env_vars only (both
light) so it is safe to import from config.py at class-definition time without
pulling the heavy model_config/agent runtime onto the startup fast path.
Anything needing model_config (provider credentials, the config path, env-var
prefix resolution) is imported lazily inside functions.
Shared unified-diff helpers.
Every diff passing through this module is "\n"-joined from lines that came
from splitlines() or split("\n"), so no element can contain a line boundary.
That is what makes split_diff_lines the exact inverse and splitlines() wrong
here — see its docstring for what breaks. Check any helper added to this module,
and any new producer of a diff it reads, against that invariant.
Protect machine-managed memory blocks from agent edits.
The onboarding flow writes the user's preferred name into the user AGENTS.md
inside a marker-delimited block (see onboarding.ONBOARDING_NAME_MEMORY_START /
ONBOARDING_NAME_MEMORY_END). MemoryMiddleware strips HTML comments before
injecting memory, so the model never sees those markers and has no way to know
the region is off-limits. Since the same prompt tells the model to edit_file
that file to persist learnings, nothing stops it from rewriting the managed
block.
This middleware intercepts write_file/edit_file calls targeting the guarded
file(s), and delete calls that would remove them. When a write or edit would
change or remove the managed block, the model's other edits are kept (though
surrounding whitespace may be normalized, and a fully removed block is
re-appended rather than restored in place) while the managed block is restored,
and an error is returned so the model learns the region is machine-managed. A
delete call that would remove an existing managed block is rejected before the
tool runs; a delete of a guarded file that exists but cannot be read is also
rejected, failing closed rather than removing a file we cannot inspect. When the
block was altered but the restore could not be completed, an error is still
returned so the failure is never silent.
Canonical internal messages for goal state and work continuation.
Model configuration management.
Handles loading and saving model configuration from TOML files, providing a structured way to define available models and providers.
External event ingress for the Textual app.
Exposes a small EventSource protocol plus a Unix-domain-socket implementation
that lets local processes push commands, prompts, and signals into a running
session over a newline-delimited JSON wire protocol.
The wire format and configuration env vars may change without semver guarantees while this surface stabilizes.
Help screens and argparse utilities for the app.
This module is imported at app startup to wire -h actions into the
argparse tree. It must stay lightweight — no SDK or langchain imports.
Terminal capability detection.
Detect optional terminal features without reading from stdin.
The app only uses kitty-keyboard-protocol support to choose a user-facing newline shortcut label. To keep startup safe on remote or high-latency PTYs, detection is conservative and relies on side-effect-free terminal identity signals plus an explicit environment-variable override.
UI-agnostic helpers for resolving an MCP login target.
The MCP login flow historically inlined config discovery, trust gating,
shape validation, and print()-based error reporting. The TUI cannot
consume those print statements, so this module extracts the same logic
into pure functions that return structured results (ConfigResolution,
ServerSelection) plus a typed ConfigResolutionError. Callers decide
how to render those results.
No print() calls live in this module. No imports happen at module
top level beyond dataclasses/typing/pathlib so the CLI fast path
stays cheap; the actual config loaders are imported inside the
functions that need them.
Middleware for runtime model selection via LangGraph runtime context.
Allows switching the model per invocation by passing a CLIContext via
context= on agent.astream() / agent.invoke() without recompiling
the graph.
Estimate and persist cumulative model cost for each thread.
The graph owns the durable total. CostTrackingMiddleware is the only writer of
_session_cost_usd, so each cost update rides the model checkpoint and works for
local, headless, and remote graph execution without a client-side state update.
The client is a reader: it renders the streamed total and never maintains its own
lifetime figure.
Coverage is not limited to the agent's own model node. Offload/summarization and
the Auto mode classifier invoke a model directly, outside after_model, and
subagents run their own graph. _SessionCostRecorder — a callback handler
installed process-wide for every model request (see _install_recorder) —
collects one record per completed request, keyed by thread, and
CostTrackingMiddleware drains and prices those records on the main agent's
checkpoint path. New side invokes are covered with no extra wiring.
The recorder only collects; the middleware alone prices and writes. The agent's own response is still priced from state, but only when the recorder did not already charge that message ID, so a request is never counted twice. That fallback keeps main-agent cost correct even for a model that never fires callbacks.
Nested agents first checkpoint their own spend on the same private channel. That makes a completed model call durable before a later tool approval can interrupt the subgraph. When the subagent finishes, its middleware transfers the accumulated delta through an owner-scoped state entry. The subagent tool checkpoints that entry on the parent graph even when a sibling interrupts, while the private total itself remains isolated between graphs.
Every caller uses estimate_cost, the only function that imports or calls
genai-prices. The import is lazy so the package and its bundled pricing data
stay off the CLI startup path. On that first successful import a daemon-thread
updater starts refreshing the catalog from upstream hourly (see
_start_price_updater); DEEPAGENTS_CODE_PRICES_AUTO_UPDATE=0 or
[update].prices_auto_update = false in config.toml opts out, and
DEEPAGENTS_CODE_OFFLINE suppresses it along with every other network fetch.
When the active genai-prices catalog -- the bundled data, or the auto-updated
snapshot once one is installed -- has no rates for a model, a local override
catalog is consulted as a fallback-on-miss (see _override_price): the user's
own ~/.deepagents/prices.json first, then a maintainer-curated file shipped
as package data. PRICING.md documents the former for users and
bundled_prices.README.md the latter for maintainers. Unsupported models and
malformed usage return None; pricing must never interrupt a model turn.
Best-effort writer for terminal escape/control sequences.
Centralizes the "fire and forget" pattern the app uses for cosmetic terminal
control (OSC 9;4 taskbar progress today; eventually OSC 52 clipboard and the
iTerm2 cursor guide). Writes prefer /dev/tty so output reaches the terminal
even when stdout/stderr are redirected, fall back to sys.__stderr__, and
never raise — cosmetic control output must not crash the app.
Set DEEPAGENTS_CODE_NO_TERMINAL_ESCAPE=1 to disable all output (useful for
unsupported terminals or noisy logs).
Registry of pending actionable notifications.
Stores plain data for notices the user can act on from a dedicated modal screen. The registry is deliberately UI-agnostic: UI routing (toast click, keybinds) lives in the app layer.
Custom tools for the agent.
iTerm2 cursor guide workaround for Textual alternate-screen rendering.
CLI-specific conversation compaction middleware.
Auto-install pinned upstream binaries for optional tools.
Today this only manages ripgrep. The SDK shells out to rg via PATH,
so installing into ~/.deepagents/bin/ and prepending that directory to
os.environ["PATH"] is sufficient — no SDK change required.
The pinned RIPGREP_VERSION and RIPGREP_ASSETS table is the single
source of truth for what gets downloaded and verified. When bumping the
version, refresh both the version and the SHA-256 entries together.
Large paste collapsing for the chat input.
When the user pastes text exceeding a size or line threshold, the full text
is stored off-screen and a compact [Pasted text #N +M lines] placeholder
is inserted into the input box instead. At submission time the placeholder
is expanded back to the original content so the agent receives the full text.
This mirrors the behavior of Claude Code's paste-collapsing system.
Lightweight text-formatting helpers.
Keep this module free of heavy dependencies so it can be imported anywhere in the app without pulling in large frameworks.
One-time migration of legacy state files into ~/.deepagents/.state/.
Earlier versions wrote internal state directly under ~/.deepagents/,
mixing it with user-facing agent directories (so e.g. mcp-tokens/
showed up in deepagents agents list). State now lives in a dedicated
.state/ subdirectory; this module moves any legacy files into place
on startup.
The migration is best-effort and idempotent: it skips entries whose destination already exists, logs and continues on per-entry failures, and never blocks startup on I/O errors.
The dcode doctor command: report install health and diagnostics.
Inspired by claude doctor, this prints a grouped, tree-style summary of the
running install, update status, and configuration locations so the output is
safe to paste into a bug report. It stays offline: the update section reads
only the local cache and never contacts PyPI.
Help rendering for dcode doctor -h is served by ui.show_doctor_help, which
does not import this module, so the help path stays light.
Enumerate the tools available to the agent.
Backs two entry points: the dcode tools list CLI command (_run_tools_list)
and the interactive /tools slash command (app._handle_tools_command).
The tool set is read from the real tool objects the agent binds rather than a hand-maintained catalog, so names and descriptions never drift from what the model actually sees. Built-in tools are collected by compiling the agent with a throwaway offline chat model (no credentials, no network) and reading the bound tool node; MCP tools are discovered via the same path the app and server use.
The collection functions here lazily import the heavy agent stack (agent
compilation, MCP discovery) inside their bodies. Only the fake-model base is
imported at module top, so importing this module is cheap relative to the agent
stack — and this module is itself imported lazily by both entry points
(_run_tools_list and _handle_tools_command), never on the startup hot path.
Helpers for tracking file operations and computing diffs for display.
First-run onboarding state for the interactive TUI.
Utilities for project root detection and project-specific configuration.
Rubric middleware retries for transient grader transport failures.
Thread management using LangGraph's built-in checkpoint persistence.
Subagent loader for app.
Loads custom subagent definitions from the filesystem. Subagents are defined as markdown files with YAML frontmatter in the agents/ directory.
Clipboard utilities.
Inspect optional-dependency install status for the running distribution.
Reads Requires-Dist metadata to report which packages declared under
[project.optional-dependencies] are installed, and renders that status
in either plain text (for stdout) or markdown (for rich UI contexts).
Unicode security helpers for deceptive text and URL checks.
This module is intentionally lightweight so it can be imported in display and approval paths without affecting startup performance.
Shared provider auth status formatting.
OAuth login flow and token storage for MCP servers.
Note: mcp.shared.auth.OAuthToken is a pydantic model whose default
repr includes the access and refresh token strings verbatim. Never
log one via %r, str(), f-string interpolation, or
logger.exception/exc_info on an exception that wraps one — the
tokens will land in stdout, log files, and error-reporting
pipelines. Pass only structural facts ("refreshed token for
server X") rather than the token itself.
Formatting utilities for tool call display in the app.
This module handles rendering tool calls and tool messages for the TUI.
Imported at module level by textual_adapter (itself deferred from the startup
path). Heavy SDK dependencies (e.g., backends) are deferred to function bodies.
Middleware for injecting local context into system prompt.
Detects git state, project structure, package managers, runtimes, and directory layout by running a bash script via the backend. Because the script executes inside the backend (local shell or remote sandbox), the same detection logic works regardless of where the agent runs.
Hook contracts and compatibility dispatch.
This package contains two hook systems: Hooks v2 (current) and legacy hooks (deprecated, removal September 1, 2026).
To write a new hook integration, use the v2 config format — see
deepagents_code.hooks.loading for file locations and precedence, and
deepagents_code.hooks.models.config + deepagents_code.hooks.models.wire
for the schema and stdin payload shapes.
deepagents_code.hooks.legacy exists only for backward compatibility; new
integrations should not target it.
Built-in skills that ship with the Deep Agents Code.
These skills are always available at the lowest precedence level. User and project skills with the same name will override them.
Plugin support for dcode.
Skills module for Deep Agents Code.
Public API:
All other components are internal implementation details.
Textual user interface package for deepagents-code.
Provider-specific MCP OAuth dispatch.
resolve_provider(url) returns the registered policy whose matches
predicate fires for url, with GenericProvider as the fallback.
Integrations for external systems used by the Deep Agents Code.
Client-side transport and headless execution for Deep Agents Code.