Textual UI application.
Per-run classifier_model value meaning "review with the main agent model".
An absent (or None) classifier_model only says the run carries no
preference, so the classifier keeps whatever the server resolved at startup
(--auto-classifier-model, DEEPAGENTS_CODE_AUTO_CLASSIFIER_MODEL,
[models].auto_classifier). /auto model clear needs the stronger statement
that reviews go back to the main agent model, which this sentinel carries.
It cannot collide with a real spec: create_model resolves provider:model
(or a bare model name) and has no provider or model named __dcode_.... A
control character such as a leading NUL would also be collision-proof, but this
value has to survive the trip to a remote deployment intact — the context is
serialized to JSON and may be persisted, and Postgres text/jsonb rejects NUL
outright. A stripped sentinel would silently read as "no preference" and leave a
startup classifier authorizing actions after the UI reported the clear, so the
sentinel stays plain ASCII.
Default agent / assistant identifier when no -a flag is given.
User-facing reconnect guidance shown for an MCP server that was optimistically re-enabled but whose agent has not yet reconnected.
Set as MCPServerInfo.error by app._apply_optimistic_disabled_state (alongside
pending_reconnect=True, which is what /tools actually keys off). Named here
so the producer and the tests asserting the message share one literal.
Default RubricMiddleware.max_iterations, shown without importing the SDK.
Hardcoded rather than read from deepagents.middleware.rubric.RubricMiddleware
because this module is dependency-free and importing the SDK for a display
string would violate the startup-performance rule (see AGENTS.md). This is a
hand-maintained duplicate that can rot if the SDK bumps its default, so
test_reliable_rubric.py::TestReliableRubricMiddleware::test_displayed_max_iterations_default_matches_sdk
is the drift guard that fails when the two diverge.
Maximum time to drain Hooks v2 SessionEnd during session teardown.
User-facing labels for UsageKind values.
Stable display order for per-type cost breakdowns.
URL for the full changelog.
URL for deepagents-code documentation.
Visual style for the chat input cursor (a block cell or an underline).
Whether a pending review creates a goal or amends the current one.
Lifecycle status of a TUI-owned goal.
active and blocked are unfinished working states, paused preserves the goal
without driving work, and complete is terminal. A blocked goal is still
considered actionable (active=True) by get_goal, whereas a paused goal is
unfinished but reports active=False.
Which flow opened an abort-capable prompt, selecting the abort wording.
Passed as the prompt's abort argument; None there means abort is not
offered. "resume" is the launch-time -r resume (abort starts a new
session); "thread_switch" is the in-session /threads switcher (abort keeps
the current thread). Its members are kept disjoint from CwdSwitchChoice's as a
naming convention -- not a type guarantee (these are distinct Literal types
used at distinct sites, so a checker already keeps them apart) -- so a mode token
is never mistaken for an outcome token in a log, test, or debugger.
test_abort_mode_tokens_disjoint_from_choice enforces it.
Outcome of the prompt: restart the server now or keep the current one.
Callers must also handle None, which Textual passes when a screen is
dismissed programmatically rather than by a user keypress. That is not a
choice, and callers deliberately stay quiet for it rather than narrating
an action the user did not take.
Outcome of the prompt: restart the server now or defer.
Valid DeferredAction.kind values for type-checked deduplication.
Read the current git branch from repository metadata.
Fall back to git rev-parse for unusual repository layouts.
Return the command name this process was launched with.
Cached: sys.argv[0] and the launch environment are fixed for the life of
the process. Tests that vary either must call invoked_name.cache_clear().
Close every request in a ledger that outlives its stream round.
A chunked request is left open between chunks so later ones can revise it.
That is only correct within one round: when a consumer reuses its ledger
across HITL resume passes, the replayed chunks of an already-recorded
request would otherwise merge a second time and double its tokens and cost.
Closing the ledger at each round boundary makes the replay indistinguishable
from the stray-chunk case record_message_usage already rejects.
Format an estimated USD cost for compact display.
Format a token count into a human-readable short string.
Record usage attached to one streamed model message.
A request is entered in recorded_requests only once usable token metadata
has been recorded for it. Callers retain that ledger across stream rounds so
one API call stays one row, however its usage arrives.
A streamed chunk and a completed message report usage differently, so they
are handled differently. A completed AIMessage carries the request's
whole usage and is idempotent: replaying it must not count twice. A chunk
carries whatever the provider chose to emit at that point in the stream --
Anthropic and OpenAI attach the full usage to one chunk, while Google emits
an incremental delta on every chunk, which the consumer is expected to sum.
Skipping a chunk whose ID was already seen would drop every Google chunk
after the first, losing most of the request's output tokens and cost.
Summing them as separate requests would be wrong in the other direction, so a later chunk instead revises the request already recorded: its earlier contribution is retracted and re-recorded with the running totals. One API call therefore counts once, with one per-model row, no matter how many chunks carried its usage. Google also names the model only on its final chunk, so the model is upgraded when a message supplies one and otherwise left alone -- without that, one call would straddle a fallback-model row and a real-model row.
Format a message timestamp for display.
Shows only the time of day for messages from the current local date and
prefixes the date otherwise. The 12- versus 24-hour clock follows the
system configuration (see uses_24_hour_clock).
Build a one-time goal continuation.
Build one canonical append-only goal/rubric state notice.
Return a stable digest for authoritative goal/rubric state.
Return whether state contains a goal or an active rubric.
Return whether a local or serialized message has the human role.
Return whether a message is hidden from user-facing session history.
Return the newest goal-state source index, including invalid messages.
Return the newest valid notice and its raw-history index.
Restore iTerm2 cursor guide when launch-time profile state required it.
Open url in a browser and toast on failure.
Runs webbrowser.open in a thread, catches the platform errors
that can arise when no browser backend is available, and posts a
warning toast containing the URL so the user can copy it manually
instead of the failure vanishing into a background worker log.
Return whether startup tips should be shown.
Persist theme preference to ~/.deepagents/config.toml.
Persist a [ui.terminal_themes][term_program] = name entry.
The write is atomic (temp file + Path.replace) to avoid corrupting
config.toml on crash or SIGINT. Mirrors save_theme_preference.
Run the Textual application.
When server_kwargs is provided (and agent is None), the app starts
immediately with a status-bar connection state and launches the server in
the background. Server cleanup is handled automatically after the app exits.
Client-facing builder for the per-run graph context payload.
Callers populate this and pass it via context= to astream/ainvoke.
ConfigurableModelMiddleware and the interrupt_on when predicate read
it from request.runtime.context. In-process LangGraph coerces it into
CLIContextSchema (the registered context_schema); over the API it stays
a plain dict — which is why consumers handle both shapes.
What a stream consumer last recorded for one request.
Held so a later chunk of the same request can retract that exact contribution and re-record the running totals, keeping one API call to one request and one per-model row.
Stats accumulated over a single agent turn (or full session).
Stable identifiers for notification actions dispatched by the app.
Typed payload for a missing-dependency notification.
One button/action row in the notification modal.
In-memory store of pending notifications.
Instance-scoped (one per app) so test apps don't pollute each other. Owns the bidirectional key-to-toast-identity binding so callers cannot accidentally desynchronize the click-routing indices.
A single notice waiting for user action.
Immutable value object: the registry owns the
key-to-toast-identity binding (see NotificationRegistry) so
external callers cannot corrupt click-routing indices by mutating
notifications after construction.
Typed payload for an update-available notification.
Chat input widget with prompt, multi-line text, autocomplete, and history.
Features:
config.newline_shortcut)Keep the current goal and lifecycle state visible above the input.
Animated loading indicator with status text and elapsed time.
Displays:
In-memory message data for virtualization.
This dataclass holds all information needed to recreate a message widget. It is designed to be lightweight so that thousands of messages can be stored without meaningful memory overhead.
Manages message data and widget window for virtualization.
This class stores all messages as data and manages a sliding window of widgets that are actually mounted in the DOM.
Types of messages in the chat.
Status of a tool call.
Widget displaying an app message.
Widget displaying an assistant message with markdown support.
Uses MarkdownStream for smoother streaming instead of re-rendering
the full content on each update. Once a stream finishes, the message
is re-rendered from the complete source via Markdown.update() to
work around Textualize/textual#6518: MarkdownFence._update_from_block
refreshes the visible Label but leaves _highlighted_code pinned to
the first chunk, so any later recompose (click, focus change, theme
update) re-yields the stale value and wrapped fenced-code bodies vanish.
A full re-parse rebuilds every fence with correct internal state.
Streamed tokens are coalesced in _pending_append and flushed to the
MarkdownStream on a throttled timer (_STREAM_FLUSH_INTERVAL). Writing
every token immediately forced a markdown re-parse per chunk on the UI
event loop, which starved keyboard input while the model streamed.
Batching the writes keeps the event loop free so typing stays responsive.
Widget displaying a diff with syntax highlighting.
Widget displaying an error message.
Widget displaying a queued (pending) user message in grey.
This is an ephemeral widget that gets removed when the message is dequeued.
Compact grader result with complete, scrollable details on demand.
Widget displaying a skill invocation with collapsible body.
Shows skill name, source badge, description, and user args as a compact
header. The full SKILL.md body (frontmatter stripped) is hidden behind a
preview/expand toggle (click or Ctrl+O). The expanded view renders
markdown via Rich's Markdown inside a single Static widget.
Visibility is driven by a CSS class (-expanded) toggled via a Textual
reactive var. Click handlers are scoped to the header and hint widgets
(_SkillToggle) so clicks on the rendered markdown body do not trigger
expansion toggles (preserving text selection, for instance).
Widget displaying a tool call with collapsible output.
Tool outputs are shown as a 3-line preview by default. Press Ctrl+O to expand/collapse the full output. Shows an animated "Running..." indicator while the tool is executing.
Collapsed one-line stand-in for an assistant step's tool calls.
Tools are hidden from the moment they start; this single line shows live
progress ("Running 1 shell command…") and flips to the fully past-tense
line ("Ran 1 shell command") once every tool finishes. While the step is
live, finished calls stay visible in the past tense next to the ones still
running in the present tense (e.g. "Ran 2 shell commands, running 1 agent…")
so the work already done in the step doesn't disappear. Failed, rejected,
and skipped tools are evicted to standalone rows (see _evict_failed) so
errors stay visible. Clicking the line or pressing Ctrl+O expands the
underlying tool rows (and their diffs).
Two modes:
add_member as
they mount, a spinner timer animates the line and re-renders present/past
tense, and failed tools are ejected back into view so errors stay visible.live=False, used for hydration/resume): a fixed set of
completed tools rendered straight to the past tense with no timer.Purely presentational — never tracked by the message store; it is re-derived from the mounted tool widgets on each stream boundary and on hydration.
Widget displaying a user message.
Very long messages are collapsed in the transcript by default (head+tail elision) to protect scrollback performance. The full text remains on the widget for copy/select, and the collapsed form is reversible via click or Ctrl+O.
One startup tip displayed above the chat input.
Status bar showing mode, cwd, tokens with cost, and the active model.
Docked two-pane panel visualizing js_eval subagent fan-out by phase.
Hidden until the first spawn event. Phases (one per js_eval) list on the
left and the selected phase's subagents render as a scrollable table on the
right. Focus the panel and use up/down to revisit finished phases. Expands
while any phase runs, collapses to the header when the turn goes idle, and
re-expands when a new phase starts.
Compact welcome banner shown at startup.
Renders a bordered box with the product title and optional version. A
(debug enabled) tag appears when DEEPAGENTS_CODE_DEBUG is enabled
(truthy), and an (experimental) tag appears when
DEEPAGENTS_CODE_EXPERIMENTAL is enabled (truthy), both even when the
version is hidden. A (local) tag appears for
editable installs only when the version is shown. Rows follow that appear
only when their data (and any env gate) is present. In render order: the
active model
(SPLASH_SHOW_MODEL, opt-in), working directory (SPLASH_SHOW_CWD,
opt-in), LangSmith tracing project and its replica (each clickable once its
URL resolves), thread ID (debug mode only; click to copy, with an
(open in langsmith) trace link once the project URL resolves), and the MCP
tool count. MCP server warnings and the editable-install path follow.
A question to ask the user.
Tool-approval policy selected for an interactive thread.
Manages a langgraph dev server subprocess.
Focuses on subprocess lifecycle (start, stop, restart) and health checking.
Env-var management for restarts (e.g. configuration changes requiring a full
restart) is handled by _scoped_env_overrides, keeping this class focused
on process management.
Client that talks to a LangGraph server over HTTP+SSE.
Wraps langgraph.pregel.remote.RemoteGraph which handles SSE parsing,
stream-mode negotiation (messages-tuple), namespace extraction, and
interrupt detection. This class adds streamed message-object conversion for
the Textual adapter and thread-ID normalization. State snapshots are
returned as provided by the server.
Result of creating a chat model, bundling the model with its metadata.
This separates model creation from settings mutation so callers can decide when to commit the metadata to global settings.
Source of external events for the Textual app.
Implementations must be safe to stop() even when start() failed
partway through; the app always invokes stop() from a finally block.
A transport-independent event delivered from outside the TUI.
A new proposal or a rejection-based regeneration.
feedback/previous_criteria are only present on a rejection retry.
Live client identity projected into every hook invocation.
Owns the Hooks v2 runtime, presenter, hook service, and transcripts.
The presenter is the manager's, not the runtime's: one instance is created once and handed to every runtime the manager loads, so a reload or a late UI attachment never leaves two presenters competing for the same output.
Reason a session-start event occurred.
Decides whether project-scoped hooks may run in a given directory.
Trust is a property of the workspace, not of the session, so it must be re-resolved every time the working directory moves. A session that starts in a trusted project and later moves into an untrusted one must not carry the original grant forward.
Callers hand this policy to HooksManager, which resolves it on load and on
every reload; nothing upstream needs to hold or reinterpret the decision.
Metadata for a configured MCP server and its tools.
Raised when a provider is selected but its LangChain package is not installed.
Subclasses ModelConfigError so existing except ModelConfigError blocks
keep working. Carries the provider name and the package to install so
callers can render targeted recovery hints (e.g., suggest /install fireworks
or the /model slash command) without string-matching on the formatted
exception message.
Result from plugin discovery.
A discovered plugin ready to feed dcode adapters.
Parsed plugin manifest.
Extended skill metadata for CLI display, adds source tracking.
Everything dcode tools list needs to render, in display order.
An MCP server that was discovered but currently exposes no tools.
Adapter for rendering agent output to Textual widgets.
This adapter provides an abstraction layer between the agent execution and the Textual UI, allowing streaming output to be rendered as widgets.
Approval menu using standard Textual patterns.
Key design decisions (following mistral-vibe reference):
Interactive widget for asking the user questions.
Supports text input and multiple choice questions. Multiple choice questions always include an "Other" option for free-form input.
Free-form answer input for ask-user questions.
Adds one behavior over the shared base: when the cursor is on the first or
last line of a multiple_choice question, Up/Down are handed back to the
enclosing choice list instead of moving the text cursor.
Modal that lists configured providers and lets the user manage keys.
Reachable via the /auth slash command. Always dismisses with None;
state changes are persisted by AuthPromptScreen and reflected by
re-rendering the option list when this screen is reopened or after a
save/delete completes.
Well-known providers whose integration package isn't installed yet are
surfaced greyed-out so they stay discoverable. Selecting one routes
through an install confirmation: on confirm the screen records the extra on
pending_install_extra and dismisses so the app can install it (mirroring
the model selector's install-on-select flow) and reopen the manager.
A single row in the console's session snapshot.
The four named fields keep the display strings and their interaction metadata
explicit at construction sites. copyable opts a row into click-to-copy, and
thread_id enables a resolvable (open in langsmith) trace link for the
thread row.
Inline review widget for generated goal acceptance criteria.
Text input that keeps goal-review edit keystrokes inside the editor.
Full-screen modal for model selection.
Displays available models grouped by provider with keyboard navigation and search filtering. Current model is highlighted.
Returns (model_spec, provider) tuple on selection, or None on cancel.
Posted for an action that opens a follow-up modal in place.
Some actions (those in IN_PLACE_ACTIONS, currently ENTER_API_KEY)
push another modal, such as the API-key prompt, on top of the
still-open center. Dismissing the center first would drop that stack,
so Esc in the follow-up modal would fall through to the base screen
instead of returning here. The app handles this message by dispatching
the action while the center stays mounted, then reloading it with the
refreshed registry snapshot.
Posted when the user picks SUPPRESS from a notification's detail modal.
The center does not dismiss on SUPPRESS because the remaining
notifications should still be reachable in place. The app handles
this message by running the suppress dispatch and calling
NotificationCenterScreen.reload with the refreshed registry
snapshot.
Modal that shows self-update progress and a bounded log tail.
Represents a queued user message awaiting processing.
Textual message carrying an external prompt or command.
An action deferred until the current busy state resolves.
Session state for the Textual app.
Main Textual application for deepagents-code.
Result from running the Textual application.
Valid spinner display states, or None to hide.
Discriminated union for the ask_user widget Future result.