DDextDocs
Single-binary Rust coding agent

The agent that lives in your terminal.

Dext keeps project-scoped session state and gives the model a curated set of native tools for filesystem operations, search, shell execution, HTTP, Git, and task tracking — source-first, no external service required.

18
Provider tools
7
Providers
3
Sandbox profiles
1
Rust binary
~/Dext — zsh
$cargo install --path . --force --locked
$dext auth login chatgpt
$dext "summarize this repo"
▸ streaming response
Source-firstOne auditable Rust binary

Prompts, policy, providers, state, and the TUI live in this repository.

RecoverableGit-native checkpoints

Preview, inspect, and undo agent file changes without hiding recovery state.

BoundedSafety at every layer

Approval profiles, sandbox controls, credential scrubbing, and side-effect fencing.

PortableProvider-neutral workflows

Cloud and local model profiles share a compact native tool and pack system.

Dext — Technical Documentation

Dext is a single-binary Rust coding agent that runs from your terminal. It keeps project-scoped session state and gives the model a curated set of native tools for filesystem operations, search, shell execution, data processing, HTTP requests, Git operations, and task tracking.

Dext is source-first: prompts, runtime state, tool policies, provider wiring, and the TUI all live in the repository with no external service required beyond your chosen model provider.

Policy-governed continuity: By default, Dext autosaves session state under a project-specific key, but restores it only when you use --resume or a session command. recall.md is bounded agent working memory: project DEXT.md policy decides whether the agent maintains it, and every native write stays inside normal tool approval, mutation preview, checkpoint, 4 KiB size, and privacy-redaction controls. Seat summaries are user-authored context; --no-session disables durable session/log writes.
Canonical documentation: this GitHub Pages site is the main technical reference and is deployed from docs/ after each reviewed change to main. Focused Markdown guides remain supplemental. Open non-documentation risks are tracked in the risk register.
Developer verification: run cargo fmt --all -- --check, Clippy with warnings denied, cargo audit --deny warnings, cargo deny check licenses, the vendored ratatui-core tests, release build/tests, and the PTY smoke suite. Reinstall with cargo install --path . --force --locked so the binary on PATH matches the source. Renderer-specific requirements are documented in docs/TUI.md.
Default runtime: a no-argument run is equivalent to dext --sandbox-profile danger-full-access --approval always. Dext adds no confinement and uses ambient authority inside the current host, container, VM, namespace, WSL environment, CI worker, remote shell, or service account; it neither requires nor escapes those boundaries. Optional workspace-write, read-only, and prompting approval profiles remain available.

Architecturally, Dext uses Agent in main.rs as a facade around focused modules: provider request shaping and auth (provider.rs), bounded stream assembly (streaming.rs), tool-round execution and side-effect fencing (tool_round.rs, tool_journal.rs), tool schemas and policy (tools.rs, tool_policy.rs), project/session state (session.rs), interactive rendering (tui.rs), safety/recovery primitives (git_checkpoints.rs, mutation_preview.rs, sandbox.rs), and extension metadata/runtime (packs.rs, shelves.rs, orchestrator.rs).

Quick Start

Install on Linux or macOS

curl --proto '=https' --tlsv1.2 -LsSf https://raw.githubusercontent.com/SiliconState/Dext/main/scripts/install.sh | sh

Install on Windows PowerShell 5.1 or PowerShell 7

irm https://raw.githubusercontent.com/SiliconState/Dext/main/scripts/install.ps1 | iex

The Windows installer derives the native architecture from Windows environment values rather than nullable modern-.NET runtime metadata, so the same in-memory script works under Windows PowerShell 5.1 and PowerShell 7. The installers require an exact vX.Y.Z tag, select the matching release archive, verify its SHA-256 checksum, reject an existing destination that is not a regular non-link/reparse file, and validate that the candidate starts and reports the selected version before replacing the current-user binary from a same-directory staged file. Unix uses an atomic same-filesystem rename. Windows uses File.Replace when supported; an explicitly unsupported operation falls back to same-directory renames, restores the previous binary if installing the staged candidate fails, and retains a recovery backup named in the error if rollback itself fails. They can additionally require GitHub provenance verification with DEXT_REQUIRE_ATTESTATION=1. Dext v0.1.0 is published, so the default installers download prebuilt archives and do not require Rust. If no tagged release is available, both installers can resolve and pin the current main commit before running a locked Cargo build; set DEXT_SOURCE_FALLBACK=0 to refuse that fallback. Because source builds have no release attestation, attestation-required mode also disables source fallback. Review the scripts locally before running them if you prefer a review-first install; use the release guide for provenance and SBOM verification.

Authenticate

dext auth providers           # list available providers
dext auth login chatgpt       # ChatGPT/Codex OAuth
dext auth login glm           # paste the ZAI key at Dext's prompt
dext auth login openai        # paste the OpenAI key at Dext's prompt
dext auth login kimi          # paste the Kimi key at Dext's prompt

Run

dext                          # interactive session
dext "summarize this repo"    # one-shot
dext --frugal --effort off    # low-token mode

Project Layout

FilePurpose
src/main.rsAgent loop, provider HTTP, permissions, slash commands, CLI entry, eval
src/main_tests.rsUnit and integration tests for main.rs
src/tui.rsRatatui inline TUI, permission prompts, transcript rendering
src/provider.rsProvider catalog, API-key and provider-specific OAuth auth, request shaping, model normalization
src/claude_subscription.rsVersion-pinned Claude subscription billing/checksum/identity wire compatibility for official Anthropic OAuth requests
src/sse.rsInput-buffer-bounded SSE frame decoder shared by runtime and benchmarks
src/streaming.rsProvider event validation and stream/tool-call assembly
src/tool_round.rsTool planning, approval, checkpoint/journal boundaries, dispatch, result normalization
src/tool_journal.rsOwner-private side-effect start/terminal records and resume reconciliation
src/session.rsSession/log persistence, project state locks, atomic I/O
src/seats.rsProject-scoped durable agent identities and seat-specific latest-session lookup
src/sandbox.rsOptional OS confinement, profile write roots, and private scratch; full access follows a direct ambient-authority path
src/tools.rsTool definitions, permission metadata, parallel-safe classification
src/orchestrator.rsWork phases, similarity guards, objective tracking, adaptive caps
src/git_checkpoints.rsGit-native recovery refs, /undo, sidecar files, pruning
src/mutation_preview.rsIn-memory line-level previews before applying file mutations
src/packs.rsPack discovery, invocation, conversational pack inference
src/shelves.rsTyped shelf registry with ability metadata, signals, and effects
src/tool_policy.rsCommand risk classification, input validation, bash guardrails
src/privacy.rsPrivacy policy, sensitive-path/search-scope denial, secret/PII redaction
src/usage.rsToken usage normalization, per-model pricing, budget caps
src/events.rsAgentEvent stream and the EventSink front-end trait
src/crash.rsPanic hook, redacted owner-only crash snapshots, event breadcrumbs
src/process_tree.rsChild session detachment and whole-tree teardown
src/secret_redactor.rsStreaming credential scrubbing for child-process output
scripts/install.sh / install.ps1Per-user checksum-verifying release installers with an exact-revision locked source fallback when no tagged release is available
scripts/test_install.sh / test_install.ps1Offline platform-native installer regressions for replacement, no-clobber failures, strict tags, safe destinations, source pinning/fallback disablement, malformed API state, version mismatch, and attestation-required paths; Windows PowerShell 5.1 and PowerShell 7 both execute the complete harness and an in-memory Invoke-Expression install matching irm | iex, plus forced unsupported replacement, rollback, and retained-backup recovery
deny.tomlDependency-license allowlist enforced by security and release workflows
benches/dext_bench.rsCriterion performance harness
tests/tui_smoke.rsUnix PTY-backed launch, multiline input, streaming, and resize regression tests
tests/tui_smoke_windows.rsWindows ConPTY-backed harness self-checks plus real-binary launch, input, default-policy status, exit, and terminal-restoration smoke test
vendor/ratatui-core/Exact upstream source plus Dext's narrow inline-terminal compatibility patch

Architecture

Dext follows a single-binary architecture with no runtime plugin service. The Agent facade composes prompts and state while focused modules assemble provider streams, execute tool rounds, enforce policy, render the terminal, and persist recovery metadata.

High-Level Data Flow

User Input → Agent Loop → Provider HTTP (streaming SSE)
                                    ↓
                            Response Blocks (text/thinking/tool_use)
                                    ↓
                       Tool Dispatch → Execute → Result
                                    ↓
                       Append to History → Next Turn
                                    ↓
                       Compaction (if context pressure)

System Diagrams

This section documents executable runtime behavior with HTML-native SVG diagrams. The shapes map to Rust modules and runtime checkpoints instead of being merely illustrative.

Turn Loop Sequence — user input to next turn

User/TUI Agent main.rs Provider Builder Provider SSE State/Session prompt or steering system + DEXT.md + optional recall + tools HTTP request stream blocks: text / thinking / tool_use Parse blocksappend text/thinking Tool pathvalidate + approve Persist JSONL/logusage, ledger, health next provider call after tool_result, or final response
Safe boundary: steering is injected between stream/tool cycles, not mid-mutation.
Compaction: history compacts after safe checkpoints when context pressure is high.
Evidence: session headers persist provider/tool/runtime provenance.

Tool Execution Sequence — validation, preview, approval, checkpoint, result

tool_usename + JSON args Validaterequired fields ClassifyRead / Write / Danger Previewdirect file mutations Permissionprofile + sandbox Checkpointwrite-risk call in a Git repo Executenative or subprocess tool_resultcapped output denied, invalid, or interrupted → error tool_result
Preview first: write_file, edit_file, and multi_edit render their diff before the approval prompt, and the approved executor reuses that same prepared mutation.
Parallelism: only all-read batches (read_file, rg, git_diff, etc.) parallelize; checkpoints stay on the sequential dispatch boundary.
HTTP: GET/HEAD/OPTIONS are read, POST/PUT/PATCH/DELETE are danger, and a nominal read carrying a body is danger too.
Auth guard: outputs are scanned for 401/403/invalid-key markers.

Checkpoint Lifecycle — Git recovery before mutation

Approved mutationwrite or danger risk Resolve Git rootcached per session Dirty snapshotgit stash create Create refrefs/dext/checkpoints Untracked sidecarsSHA-256 blobs, bounded Manifestappend, then retention Run mutationref preserved
Retention: 20 checkpoints for seven days; runtime manifest reads are capped at 16 MiB.
Capture bounds: arbitrary-command checkpoints inventory at most 500 untracked paths within 8 MiB per file and 32 MiB per checkpoint.
Ordering: each call is checkpointed at its own sequential dispatch boundary, so later calls in a round include earlier mutations.

Turn Loop Flowchart — branch points

Start turn Build runtime contextprompt, tools, ledger, provider Provider block?text or tool_use Textappend stream Toolpolicy + result Persist / compact / idle loop until the final response
Steering: queued input is injected between stream and tool cycles, never mid-mutation.
Compaction: runs on the loop edge, after a safe checkpoint, when history approaches the model-aware budget.

Checkpoint Restore Flow — preview vs apply

/undo or CLIlatest or id mode?preview/apply/reset Previewstat + capped diff Worktree applypaths or whole tree Restore sidecarsuntracked copies ResetHeadgit reset --hard
Default is safe: a normal restore updates worktree paths only; moving HEAD requires the explicit reset-head mode.
Verified content: blobs are rehashed before and during restore, and the checkpoint ref is retained either way.

Key Enums and Structs

TypeDefined InPurpose
ThinkingEffortmain.rsReasoning depth: Off, Minimal, Low, Medium, High, XHigh, Max
ReasoningModemain.rsIndependent Standard or Pro execution mode; active only for official OpenAI GPT-5.6 Responses requests
OutputModemain.rsText, Json, StreamJson — output format for non-interactive mode
ApprovalProfilemain.rsAsk, AutoRead, AutoWrite, Never, Always — permission gating
SandboxProfilemain.rsReadOnly, WorkspaceWrite, DangerFullAccess
ContextModemain.rsStandard, Frugal — context window management
MutationPreviewModemain.rsOff, Simple, Git — diff preview before mutations
Usageusage.rsToken tracking: input, output, cache_create, cache_read
BudgetCapusage.rsSession budget limits in USD or tokens
WorkLedgermain.rsObjective, phase, decisions, pending/done/blocked items
SessionHeadermain.rsPersisted session metadata with full provenance
Message / Blockmain.rsConversation history: Text, Thinking, ToolUse, ToolResult
AgentEventevents.rsEvent stream: TurnStart, TextDelta, ToolCallResult, etc.

Main.rs — Agent Core .rs

main.rs owns the Agent facade, CLI and slash-command dispatch, prompt/context assembly, compaction, built-in tool adapters, and eval harness. Provider stream parsing lives in streaming.rs; tool planning and dispatch live in tool_round.rs; durable side-effect records live in tool_journal.rs.

The default policy is approval always with sandbox profile danger-full-access; a no-argument run is equivalent to passing both flags explicitly. Full access adds no Dext confinement and uses ambient authority inside the current host, container, VM, namespace, WSL environment, CI worker, remote shell, or service account without attempting escape. Current-run policy overrides saved provenance on resume. Approval and sandbox dimensions remain independent, and ask, auto-read, auto-write, never, read-only, and workspace-write remain available. The last CLI policy flag takes precedence over environment configuration. Invalid approval environment values warn and fall back to ask unless a valid higher-precedence choice or true DEXT_TRUST supplies a policy; an invalid DEXT_SANDBOX_PROFILE fails normal startup instead of silently selecting full access.

Agent Loop

The central Agent struct drives a turn-based loop:

  1. Prompt composition — Builds the system prompt from a compact invariant-driven built-in agent policy plus DEXT.md, project context, tool catalog, session header, work ledger, shelf abilities, and recall working memory. The standard built-in prompt is compact; tool-specific syntax stays in lean schemas instead of being duplicated in universal prose. Composition splits into a cached stable block and a volatile environment tail that rides after the cache breakpoint. Turn-stable sections — project guidance, recall, and the pack and shelf registry summaries — stay in the cached block; context-file cache identity includes size/time plus file identity/change metadata where the platform exposes it, and mutation-capable tool rounds and approved hooks invalidate pack discovery before the next request. The model-visible environment retains actionable cwd/OS/Git, provider/model/effort/context, approval, sandbox, session id, UTC date, and privacy state; variable string values are byte-bounded and unsafe whitespace/control content is JSON-quoted onto one line, while persisted ledger/provider-health strings are collapsed and bounded before rendering. It omits toolset/schema labels already evident from provider tool definitions and host-only compaction thresholds. Context strategy budgets are omitted before the first tool action, when every counter would only repeat zero state, then remain explicit after actions so reset, warning, pattern, and pivot signals are preserved. Each ancestor DEXT.md/recall.md input is limited to a regular non-symlink file of at most 1 MiB; recall content is privacy-redacted and receives one aggregate 4 KiB payload budget across ancestry before prompt injection. Provenance paths and raw-file hashes come from the same bounded reads actually considered for the prompt. Aggregate project-context and per-section caps include headings or truncation markers as applicable, and wholly omitted files are excluded from prompt provenance. Todo state is limited to 256 KiB across tool, prompt-summary, and TUI loading paths. Only per-round state (work ledger, context state, provider health, todos, shelf context) is re-sent at full input rate. Standard and frugal tail caps come from one table and differ only in cap values and hint wording.
  2. History management — Maintains a Vec<Message> with automatic compaction when context pressure exceeds configurable thresholds (default: 90% at end-turn, 80% after safe tool-result checkpoints).
  3. Provider dispatch — Sends streaming requests to the active provider. Applies connect, first-byte, and between-chunk idle deadlines, then parses bounded SSE frames into Block variants.
  4. Tool execution — Dispatches tool_use blocks to native Rust implementations. Supports parallel execution for read-only tools. The round waiter observes interrupts independently of task completion, aborts in-flight tasks, and prevents a call that wins its concurrency permit after the interrupt from starting; every abandoned tool_use id still returns a matching interrupted result. Native file/todo reads check cancellation between bounded chunks; explicit read_file limits stop after detecting additional data, and read_symbol source input is capped at 8 MiB.
  5. Permission gating — Routes tool calls through EventSink::request_permission() based on the active ApprovalProfile and tool risk classification.
  6. Steering — Active-turn user input is routed to a steering channel, injected at safe boundaries.

Stream Parsing

ProviderAPIRequest Builder
Anthropic / GLM / Kimi CodeMessages API (/v1/messages)Request struct with AnthropicThinking
Official OpenAI GPT-5.6 / explicit custom Responses profilesResponses API (/v1/responses)build_openai_responses_request(); official GPT-5.6 adds independent reasoning.effort and reasoning.mode
Other OpenAI / DeepSeek / LocalChat Completions (/v1/chat/completions)OaiRequest with provider-compatible reasoning_effort
ChatGPT / CodexCodex Responses API (/codex/responses)build_chatgpt_request(); no Platform-only mode field

Transport Deadlines and Body Bounds

The shared provider client uses a 15-second connect timeout. Cloud requests allow 180 seconds to first response headers and 90 seconds between stream chunks; local llama.cpp requests default to 600 seconds and 300 seconds respectively. DEXT_PROVIDER_CONNECT_TIMEOUT_SECS, DEXT_PROVIDER_FIRST_BYTE_TIMEOUT_SECS, and DEXT_PROVIDER_STREAM_IDLE_TIMEOUT_SECS accept positive overrides. Initial requests and compaction retries use the same first-byte policy. Non-stream compaction responses use the stream-idle deadline while reading and reject JSON bodies above 4 MiB; provider error diagnostics stop at 4,000 bytes, and an empty summary error does not echo the raw provider body or hidden reasoning. A ChatGPT Responses finalize error caused by malformed function arguments is never executed; if no visible text was streamed, Dext compacts once when a safe split exists and retries exactly once, then surfaces the protocol error if it persists.

src/sse.rs limits its in-progress input buffer to one configured event plus at most four delimiter bytes before rejection. One large network read may still contain many valid small events; the decoder drains complete frames incrementally rather than rejecting by chunk size.

Responses terminal events with status=incomplete are recoverable rather than fatal protocol errors. Dext executes only function calls that reached a streamed completion event and discards truncated calls, ignoring complete-looking terminal snapshots for those unfinished calls. Discarded live text/thinking preview state is reset; a content-filter terminal also discards function calls and opaque encrypted reasoning state. Interactive warnings identify the recovery. If no executable call remains, Dext issues a bounded continuation request with a concise runtime hint; provider-reported content filtering halts immediately instead of retrying. Recovery lowers reasoning effort for that request only (for example, xhigh to medium) without changing the user-selected effort or consuming the normal tool-iteration budget. After three unsuccessful continuation requests, Dext halts the turn with guidance while keeping the session usable. Responses-based compaction summaries likewise reject text from incomplete terminals, retry within the existing four-attempt summary stream budget, halt immediately on content filtering, and accumulate usage from every parsed attempt. Stream protocol errors raised inside a response.incomplete terminal event (missing or self-conflicting response objects) classify as transient backend truncation and retry within the bounded stream-attempt budget instead of failing the turn permanently.

Anthropic Messages streams that emit message_stop while text or thinking blocks remain open close those display blocks implicitly and emit their block-complete updates. Tool-use blocks are stricter: a call that never received content_block_stop is never executable, even when its buffered argument JSON looks complete. An explicitly stopped tool-use block cut off mid-JSON at stop_reason=max_tokens is also discarded, but only when the JSON parser reports an EOF-shaped incomplete value; malformed non-EOF JSON and complete non-object arguments remain precise finalize errors. Dext surfaces discarded calls as unfinished, preserves and executes any earlier explicitly completed calls in the same message, and when none remain issues the same bounded automatic continuation used for Responses incomplete recovery (up to three recovery requests, then a halt notice that keeps the session usable).

Official OpenAI GPT-5.6 uses stateless Responses requests with store:false. Tool-bearing requests explicitly include reasoning.encrypted_content; when the provider returns a valid opaque reasoning item, Dext retains it in session history and replays it only within the current user/tool turn. Older-turn reasoning and malformed or placeholder items are omitted. ChatGPT/Codex input continues to omit persisted reasoning items. OpenAI Responses tools use the flat function shape with strict:false; ChatGPT/Codex tools retain their existing flat strict:null shape.

Session Persistence

Sessions are stored as JSONL files under ~/.dext/projects/<project-key>/sessions/. The first line is a SessionHeader with full provenance. Plain unseated writes retain format v3 and Seat-only writes use v4 for backward compatibility; runtime-bearing writes use v5 so pre-runtime binaries reject rather than ignore executable-runtime state. Valid transitional v3 Seat headers remain loadable and validated, then upgrade on the next Seat-only save; v1–v2 cannot carry Seat metadata, and v1–v4 cannot carry nonempty runtime metadata. Header serialization and reads are bounded at 256 KiB. Invalid persisted accounting metadata is rejected instead of affecting resumed enforcement. Runtime restoration preserves current-run approval/sandbox policy and preflights project trust, exact canonical pack-directory/source identity, manifest/hash/state accounting, and current executable approval before applying any saved sandbox/model/session fields; failure does not partially mutate the live agent. Session review remains available through list, brief, analyze, grep, failure, verification, and decision views. Export writes an explicit HTML or JSONL copy. Prune removes stale locks and lock-only project trees while preserving session JSONL, Seats, and other project state.

Eval Harness

Outcome-oriented assertions on files and command output. The eval path validates objective completion semantically (not just command exit), runs model tools and verification commands under the resolved --sandbox/DEXT_SANDBOX_PROFILE policy, and records verification artifacts for later audit. Run via dext --eval [NAME] [--sandbox PROFILE].

Crash Recovery

A panic hook writes an owner-private JSON snapshot under ~/.dext/crashes/ with a generated crash id, hashed source-location metadata, terminal dimensions, process id, current generated session id, recent event breadcrumbs, and whether backtraces were enabled. Front-end sinks record each structural breadcrumb exactly once, including JSON and stream-JSON modes. Free-form panic payloads, raw source paths, environment text, and backtrace content are omitted.

Session.rs — Session & State .rs

Manages all persistent state for Dext sessions, logs, and project-scoped data.

State Directories

~/.dext/                         # DEXT_HOME (overridable)
├── providers.json               # Provider catalog
├── auth.json                    # Stored credentials (0600 on Unix)
├── .env                         # Optional user-owned Dext dotenv settings
├── settings.json                 # user compaction threshold settings
├── session-locks.operation.lock # owner-private cross-process session lock guard
├── crashes/                     # owner-private crash snapshots
├── shelves/                     # User-scoped shelves and packs
└── projects/
    └── <project-key>/
        ├── seats/
        │   └── <seat-id>/
        │       └── seat.json        # Bounded durable identity + latest session id
        └── sessions/
            ├── <name>.jsonl     # Named session export
            └── <session-id>/
                ├── _latest.jsonl
                ├── latest.log
                ├── session.lock.json
                ├── DEXT.todo.json
                ├── tool-journal.json
                ├── artifacts/
                ├── git-auth/
                └── sudo/

Project Key

Each project gets a stable key derived from its canonical path: <slug>-<FNV-1a-hash>. This ensures isolated state per project even with identical directory names.

Each project may contain bounded Seat records under seats/<seat-id>/seat.json. A Seat is a durable agent identity; a session is one transcript/crash-recovery incarnation. Portable lowercase ids are validated before path construction; trailing dots and Windows device names are rejected. Unix ancestors must be owner-safe, managed directories owner-private, and record files regular, private, single-link, and no-follow. Selecting --seat NAME does not create an empty record; the first successful durable save or explicit dext seat set persists it. Use dext seat set NAME --label TEXT, --summary-file PATH|-, --clear-label, or --clear-summary to maintain bounded context. --seat NAME --resume follows that Seat's latest-session pointer. Cross-Seat, same-name cross-project, malformed, oversized, and unprovenanced restoration fails before saved state mutation. --no-session --seat NAME is contextual only. Prompt-visible context is privacy-redacted JSON marked as user-authored data, not instructions, and the complete rendered Seat section is capped at 1,000 bytes in both context modes; persistent labels retain their independent 128-character validation cap. Project changes clear active identity. /reset serializes pointer update and transcript removal and attempts pointer rollback on deletion failure. Crew directly maps portable role names to crew.<agent> and assigns deterministic crew.agent-<hash> ids to other valid custom names; captured, detached, and pane workers pin one absolute Dext state root. Same-user replacement is outside Dext's isolation boundary; validation and atomic replacement remain correctness controls.

Atomic Writes

Sensitive state writes use a temp-file-and-replace helper. On Windows the replacement uses MoveFileExW; on Unix it uses fs::rename. Session logs normally use private append writes while they remain below the cap.

Session State Lock

Each active session directory has a session.lock.json record containing PID, token, project, sandbox, and session identity. Session open, stale reclamation, cleanup, and prune operations are serialized by the owner-private session-locks.operation.lock file under DEXT_HOME. Stale removal revalidates the token and PID while holding that lock, so a replaced live lock is preserved. Matching locks are released through a cleanup registry. dext session prune runs as a dry run unless --apply is supplied; it preserves every project directory containing session or other project state and removes only stale locks plus stale lock-only directory trees.

Log Rotation

Each session's latest.log is capped at 64,000 bytes. DEXT_LOG_ARCHIVES may retain up to 16 rotated archives; the default is zero.

Provider.rs — Provider Catalog .rs

Manages the catalog of model providers, authentication, request shaping, and model normalization.

ApiProvider Enum

Three wire-format families:

  • Anthropic — Anthropic Messages API (also used by GLM via api.z.ai/api/anthropic)
  • OpenAi — OpenAI-compatible APIs. Ordinary models use Chat Completions; the built-in official openai profile routes GPT-5.6 at api.openai.com through the Platform Responses API.
  • ChatGpt — ChatGPT Responses API (OAuth-backed)

Built-in Providers

IDDisplayAPIDefault ModelAuth
glmZAI GLMAnthropicglm-5.2[1m]API Key (ZAI_API_KEY); catalog also includes glm-5.3-flash and glm-5.3-flash[1m] with 1M context, 131,072-token output, multimodal capability metadata, and low/high/max effort
chatgptChatGPTChatGPT Responsesgpt-5.4OAuth (OpenAI auth)
openaiOpenAI APIOpenAIgpt-5API Key (OPENAI_API_KEY)
anthropicAnthropicAnthropicProvider catalog defaultClaude Pro/Max OAuth via /login; API key via ANTHROPIC_API_KEY
kimiKimi CodeAnthropick3API Key (KIMI_API_KEY)
deepseekDeepSeekOpenAIdeepseek-chatAPI Key (DEEPSEEK_API_KEY)
localLocal llama.cppOpenAIqwen3.6-35b-a3b-mtp-ud-q5_k_mNone; accepts the server model alias, probes live context, and defaults to frugal context

Provider Resolution Chain

  1. DEXT_PROVIDER env var
  2. DEXT_PROFILE env var
  3. DEXT_API_PROVIDER env var
  4. active_provider field in providers.json
  5. Default: glm

Model Resolution Chain

  1. DEXT_MODEL_{PROVIDER} (provider-specific)
  2. DEXT_MODEL (with DEXT_MODEL_FORCE override)
  3. Profile's default_model

ChatGPT Model Normalization

Compact aliases are canonicalized (for example gpt5codexgpt-5-codex and ChatGPT gpt-5.6gpt-5.6-sol). GLM models get an automatic glm- prefix when omitted, and model-name context hints such as [1m] are honored.

GPT-5.6 Responses Controls

The built-in OpenAI and ChatGPT catalogs include gpt-5.6-sol, gpt-5.6-terra, and gpt-5.6-luna; OpenAI also keeps the official unsuffixed gpt-5.6 Sol id, while ChatGPT normalizes it to gpt-5.6-sol. Compact aliases are gpt56/gpt56sol, gpt56terra, and gpt56luna. All variants declare 1,050,000-token context and 128,000-token output metadata.

For API-key OpenAI at the official endpoint, the four listed GPT-5.6 ids use /v1/responses; unknown gpt-5.6-* suffixes do not silently inherit that route. GPT-5.6 effort levels are none, minimal, low, medium, high, xhigh, and native max. Standard versus Pro is an independent model mode, defaults to Standard, and is carried inside the Responses reasoning object. Main turns and compaction summaries resolve effort through the selected model's advertised levels; unsupported selections clamp, and Off emits none only when advertised. Summaries preserve the selected Standard/Pro mode and receive Dext's reasoning-aware summary allowance whenever the summary model resolves a Responses reasoning effort, even when main-turn effort is Off. Tool-bearing stateless requests ask for opaque encrypted reasoning state so current-turn function-call continuation can replay it. DEXT_COMPACT_MODEL is normalized through the active provider; request contract, reasoning capability, effort levels, mode, and usage pricing come from the resolved summary model. This capability-scoped route does not change custom OpenAI-compatible endpoints or non-GPT-5.6 models.

ChatGPT OAuth retains its Codex Responses contract and GPT-5.6 levels from none through xhigh; Dext max maps to xhigh. It receives neither reasoning.mode nor max_output_tokens. Other built-in providers retain their prior model-specific request shaping.

Authentication

Credentials are stored in ~/.dext/auth.json. Two types are retained through runtime resolution: ApiKey (plain key, supports env var refs and !command secret references) and OAuth (access token + refresh token + expiry, used independently by ChatGPT and Anthropic subscription login). Provider/profile resolution merges built-ins with user catalog overrides while pruning retired bundled providers.

/login anthropic and dext auth login anthropic start an unofficial Claude Pro/Max OAuth flow; web forces replacement while retaining the existing credential until the new exchange succeeds. Only an OAuth credential on the built-in official Anthropic profile and endpoint activates the version-pinned Claude Code-compatible billing block, Agent SDK block, seeded checksum, beta/header, and UUID request/session shape. Anthropic API keys, GLM, Kimi, and custom profiles retain their ordinary request form; a stored Anthropic subscription credential combined with an overridden base URL or request contract fails closed rather than sending the OAuth token without its required wire shape. OAuth exchange and refresh use bounded, no-redirect requests and bounded JSON responses; long-running sessions re-check expiry before every user turn and persist refresh-token rotation without overwriting a newer credential. The loopback callback polls for new connections without blocking, switches each accepted connection to blocking I/O, and requires complete request headers within one two-second total deadline; it renders a lightweight Dext result page, but reports success only after exchange and secure credential storage complete; it includes no credential data and uses no-store/CSP response headers. The implementation is pinned to Claude Code 2.1.224 and must be revalidated when that private wire contract changes.

Tools.rs — Tool Catalog .rs

Defines all provider-visible tools and a shared ToolSpec registry for required fields, permission, side-effect/process, parallel-safety, and default/full-profile metadata. Static native tools and active pack-runtime tools first become provider-neutral {name, description, schema} descriptors, and the selected lean/full schema profile applies to both. Anthropic Messages, OpenAI Chat Completions, OpenAI Responses, and ChatGPT Responses adapters preserve those three fields while adding contract-required nesting, strictness, and cache controls. Empty tool collections are omitted from serialized requests, including summaries and models whose metadata disables tools. Lean schema stripping removes schema annotations without deleting arguments named description or literal data with that key. Policy code consumes registry metadata instead of repeating lists.

Tool Profiles

ProfileDescriptionSchemas
FullComplete tool descriptions and full JSON schemasFull
Lean (default)Compact action/safety/usage cues, schemas without field descriptionsStripped

Controlled via DEXT_TOOL_PROFILE. Lean mode saves significant prompt tokens per turn. A clean-repository fixture measures the real standard built-in prompt, a runtime tail with neutral provider/model placeholders, and deterministic normalized JSON for the default 13 lean {name, description, schema} descriptors; their total is capped below 6,000 bytes. This provider-neutral comparison payload is not an actual provider request, tokenizer count, billing count, formal canonical-JSON encoding, or universal maximum. The fixture separately reports actual tool-array bytes and a signed size delta from the normalized payload for Anthropic cache-on/cache-off, OpenAI Chat Completions, OpenAI Responses, and ChatGPT Responses. A separate toolset profile is selected by --toolset or DEXT_TOOLSET: default hides specialized tools and full exposes the complete catalog. Non-JSON startup emits [tools] toolset full whenever the full catalog is selected, including in frugal mode. Frugal mode applies smaller context/result/capture budgets without overriding explicit toolset or schema selections and uses the stricter pseudo-tool-protocol sanitizer described in the TUI section. The resolved live mode is passed through sequential and parallel tool rounds, so /context immediately controls subsequent native read captures and tool-result shaping. An explicit context choice remains pinned across provider switches and session restoration; without one, local providers select frugal and cloud providers select standard automatically. A valid CLI context mode takes precedence over a stale environment value, while an invalid DEXT_CONTEXT_MODE fails when no CLI override is supplied. The retired tiny mode and --tiny alias are rejected rather than silently mapped to frugal.

Tool Classification

CategoryTools
Read-only (parallel-safe)read_file, read_symbol, fd, rg, jq, fzf, git_diff, git_log, todo_read
Needs permissionbash, write_file, edit_file, multi_edit, http, awk, csvkit, git_commit, todo_write
External processfd, rg, jq, fzf, awk, csvkit, git_diff, git_log

Wire Format Adapters

  • provider_neutral_tools() — shared {name, description, schema} descriptors for static and active runtime tools
  • wire_tools() — Anthropic Messages format with an optional final cache breakpoint
  • wire_tools_oai() — OpenAI Chat Completions nested function format
  • wire_tools_chatgpt() — ChatGPT flattened Responses format with strict: null
  • wire_tools_openai_responses() — OpenAI Platform flattened Responses format with strict: false

Pack and shelf management commands are separate from provider-visible tools. Packs act as modular battery packs: Dext creates, discovers, inspects, maintains, and invokes shelf-contained workflows through the existing approval and sandbox surface. Dext ships no pack content. A reviewed executable pack runtime may append validated dynamic tools only while active; those tools pass through the same provider-neutral descriptor layer and retain normal risk, approval, checkpoint, and session controls.

Tool_policy.rs — Risk & Guards .rs

Validates tool inputs, classifies command risk, and enforces bash guardrails.

Command Risk Levels

RiskDescriptionExamples
ReadRead-only operationsls, cat, native git_diff, http GET
WriteModifies files or statewrite_file, edit_file, non-dangerous bash
DangerDestructive or sensitive operations; never auto-approved by auto-writesudo, rm -rf, shell Git outside the explicit --no-pager metadata allowlist, inline/stdin interpreters, git push, HTTP POST/DELETE

Bash Guardrails

  • Auto-injects set -o pipefail if missing. Because pipefail surfaces SIGPIPE from … | head-style truncation, exit 141 with captured stdout is classified as success (the raw exit line stays visible); exit 141 with empty stdout remains a failure.
  • Blocks --break-system-packages pip flag (overridable)
  • Detects sudo commands needing password and routes them to the local auth prompt
  • Classifies destructive Git worktree/ref/stash changes, per-command config overrides, and unknown aliases/subcommands as Danger. Because repository configuration can execute pagers, filters, fsmonitor, diff/textconv drivers, hooks, or aliases, shell Git is also Danger unless it uses explicit git --no-pager and matches a narrow helper-free metadata-inspection allowlist; commands such as grep, diff-tree, ls-files, check-ignore, and check-attr remain gated because they can invoke fsmonitor. Use Dext’s hardened native Git tools for review operations. Inline or stdin interpreter execution—including shell input redirections and heredocs—and recognized dynamic or wrapper command paths are Danger because payload effects cannot be inferred safely. Dynamic command words include variable/command, glob, brace, tilde, and attached-redirection expansion forms. Actual shell curl/wget/HTTPie/XH requests are gated because startup configuration and request bodies are not safely inferable; use the native http tool for read requests. Attached/clustered flags and common versioned Python/PyPy/Perl/Node/Ruby/PHP launchers are recognized. Python -v remains verbose and does not suppress stdin detection; Windows .exe/.com command and wrapper matching is case-insensitive.
  • Bash calls are atomic: Unix roots run in detached sessions/process groups; Windows roots start suspended, enter kill-on-close Job Objects, then resume. Dext cleans the full child tree after completion, timeout, interrupt, or cancellation/unwinding that drops an in-flight process-tree guard.
  • On Windows, shell-backed tools skip Windows/WSL app aliases and select a real bash.exe from PATH (for example Git for Windows); DEXT_BASH_PATH provides an explicit override.
  • Advisories: prefer rg over grep -r, prefer fd over find. Native-tool advisories only reference always-exposed tools (rg, fd); optional catalog tools such as jq are never advised as native replacements because lean sessions do not expose them.

Input Validation

Every tool call is validated for required fields before execution. Missing/empty required fields, type mismatches, numeric range violations, and mutually exclusive params are caught early with descriptive errors. Search extra_args reject positional and operand-changing forms, including attached ripgrep -ePATTERN/-fFILE, so model-supplied pattern/path fields remain the only search operands.

Auth Failure Detection

Tool output is scanned for auth failure markers (unauthorized, 401, 403, invalid api key) to trigger circuit breakers and prevent repeated credential burn on failing hosts.

Tui.rs — Terminal UI .rs

An inline TUI built on Ratatui + Crossterm that renders in the regular terminal buffer and uses native terminal scrollback during ordinary operation. On every effective transcript-pane width change, Dext immediately replaces stale-width scrollback with a complete replay of its logical transcript at the observed width. The backend output viewer is the only alternate-screen surface.

Layout

  • Transcript area — Scrollable message history with user, assistant, tool, thinking, and steering lines
  • Live status bar — Provider, model, thinking effort and selected /pro mode when applicable, approval profile, context pressure, usage stats
  • Input panel — Multi-line text input with history (200 entries), completion for canonical handled slash commands, and argument hints

Startup Welcome

The startup welcome remains inline transcript content, so it scrolls away naturally with the conversation. One transcript-owned blank separator row keeps it visually distinct from CLI approval and sandbox diagnostics, including after inline viewport placement or replay. Its brand row shows Dext and the version, plus the working directory and cached Git branch/state at 80 columns or wider; narrower terminals drop that right segment. Exactly two facts, Model and Approval, sit between horizontal rules, followed by one rotating tip selected from verified commands and key bindings. Terminal-cell width controls alignment and path truncation. Git status uses one git status --porcelain=v1 --branch probe off the render loop, with only an 8 ms startup wait before path-only fallback. The empty composer reads ❯ Type a request…   @ files · / commands.

Permission Prompts

Tool calls requiring permission render the pending prompt inside the inline viewport (with risk tier color coding: yellow for Read/Write, red for Danger), not into terminal scrollback. Choices: Once, Always (session), Deny. Only the compact decision line is appended to the transcript once resolved. Because inline scrollback is append-only, this keeps approvals from re-emitting (and therefore duplicating) the full history that older builds produced for the prompt spotlight and prompt-to-result swap.

Key Bindings

KeyAction
EnterSubmit input
Shift+Enter / Alt+EnterInsert newline
Ctrl+OToggle the latest expandable tool output
Ctrl+BOpen backend output viewer while bash runs
Ctrl+LOpen the read-only current todo list without entering the alternate screen
Ctrl+DQuit
Ctrl+CInterrupt current turn
Up/DownNavigate input history
ScrollNavigate transcript
?Show keymap help

Status and Active Time

The main status row presents the exact main branch label as Main, including Main (dirty) when the working tree is dirty, without renaming the branch or changing any other branch casing. It reserves its right edge for a live cumulative agent-active clock while Dext handles a turn. The clock advances during provider waits, tool calls, permission/auth waits, and in-turn compaction; while Dext is idle awaiting user input, it pauses and is hidden, then resumes on the next turn. It updates through the existing redraw cadence and uses compact forms such as 7s, 7m 05s, and 1h 07m without adding a timer thread.

Todo View

Ctrl+L opens a clean read-only modal during ordinary idle or busy work. Security-critical permission and local-auth prompts intentionally retain input and rendering priority and must be resolved or canceled first. The modal loads persisted session/project todos at startup, refreshes after todo_read/todo_write, scrolls with arrows, Page Up/Down, Home/End, or the mouse wheel, and closes with Ctrl+L, Esc, or q. Empty-state parsing matches Dext's generated sentinel lines exactly, so ordinary todo text cannot clear the modal accidentally. When todo progress is the live-status fallback above the composer, the battery follows the list length up to seven cells: Todos 3/4 ■■■□ maps one cell to each short-list item, while a longer list such as Todos 15/20 ■■■■■□□ stays capped and proportional. Partial progress retains at least one filled and one empty cell, and the active task remains visible when space permits. Editing remains on the existing todo_write path so validation, approval, checkpoint, and session-state behavior are not duplicated. The backend viewer remains the only alternate-screen surface.

Tool Result Rendering

Tool results are rendered as collapsible blocks with density grouping. Long outputs are compressed with head+tail preservation and JSON shape hints. Failed native write_file, edit_file, and multi_edit blocks explicitly state that no edits were applied, matching the atomic prepare-and-replace invariant. The alternate-screen backend viewer normalizes CRLF across arbitrary output chunks without introducing blank rows and visually matches the main TUI with a Dext header, agent-active clock, command summary, styled stdout/stderr lanes, output panel, command position, and compact controls. It continues to use the existing bounded ring buffer, selection, scrolling, event stream, and permission/auth priority.

Markdown Rendering

Assistant text blocks are rendered using tui-markdown with sanitization and terminal-safe formatting rules. Frugal mode applies the stricter pseudo-tool-protocol sanitizer across partial-stream recovery, transcript rendering, and the inspector: serialized or multiline tool-call-like assistant payloads are replaced with [tool call redacted; waiting for structured tool event] while surrounding prose remains visible. Standard mode retains the narrower legacy line detector. Rendering preserves readability under narrow widths and avoids alternate-screen dependence. Thinking and steering blocks use a light or dark contrast palette. DEXT_THEME=light|dark is the explicit override; otherwise Dext converts the terminal's COLORFGBG 16/256-color background index to luminance when present and falls back to dark.

Dependency and resize contract

The exact stack is ratatui 0.30.2, ratatui-core 0.1.2, tui-markdown 0.3.8, crossterm 0.29.0, and unicode-width 0.2.2. The lockfile pins Ratatui's transitive lru cache to patched 0.18.2. Dext selects exact versions and patches exact vendored ratatui-core source to avoid synchronous cursor-position queries during inline replay and extra whole-display clears during horizontal shrink, and to add Terminal::reset_inline_viewport. Every effective transcript-pane width change uses one synchronized terminal update: Dext clears the visible display and resets the inline viewport to the origin without a cursor query, purges stale-width scrollback, and immediately rebuilds the complete logical transcript at the observed width before appending pending output. Clearing before purging removes the still-visible old intro before logical history replays it once. Repeated frames at the same width do not rebuild. There is no quiet-settle debounce, visible-suffix overwrite, or short-history exception. This removes mixed-width history, duplicate copies, and width/height-shrink bookkeeping edge cases; the explicit tradeoffs are complete replay work during resize bursts and replacement of pre-Dext shell scrollback. A prepared insertion batch that fails remains separate from newly queued raw output, so retry does not regroup or rerank it.

The Unix real-PTY regression suite starts each Dext child in a fresh session with the slave PTY as its controlling terminal and applies resize geometry through that slave endpoint, matching real terminal resize delivery on macOS and Linux. Resize assertions wait for the replay marker with a bounded deadline rather than assuming a fixed scheduler delay on shared CI hosts. It verifies editable input during streaming, resize survival with populated history, bounded cursor queries, a visible-display clear before each paired scrollback purge, exactly one Dext intro in every replay segment, terminal-height-bounded complete replay, reconstruction through simultaneous width/height shrink, no repeated rebuild at a stable width, and completed output after resize. Its final stream marker has a bounded 10-second wait so slower macOS CI hosts do not create false negatives. A separate native Windows ConPTY test launches the real binary, submits /status, verifies the default approval/sandbox policy, exits through /quit, and checks clean process termination. The harness forces null std handles so ConPTY children bind pseudoconsole stdio even under redirected test capture, and companion self-check tests validate the pseudoconsole plumbing with cmd.exe and a non-interactive dext --version run. Submitted /quit and /exit mirror into the render loop's quit state, and the keyboard-reader join at shutdown is bounded so a blocked console read cannot stall process exit. Both CI and release workflows require the platform-appropriate harness. See docs/TUI.md and vendor/ratatui-core/DEXT_PATCH.md for the maintained contract and patch rationale.

Safety & Recovery

dext doctor reports ok, info, and warn findings for the effective approval/sandbox policy, provider/auth state, bounded latest session/journal state, and Git recovery metadata without making provider calls. Checkpoint findings name observed unsafe or recoverable modes, distinguish the 256 KiB doctor inspection ceiling from the 16 MiB runtime manifest limit, say when mutating tools are blocked, and point to dext checkpoint repair only for repairable manifest validation failures. Doctor remains inspection-only and exits 0 even when warnings are present.

Git_checkpoints.rs .rs

Git-native recovery checkpoints created before approved non-read-risk tool calls in Git workspaces.

Checkpoint Lifecycle

  1. Create — Immediately before each sequential tool dispatch, a checkpoint is created under refs/dext/checkpoints/<session>/<timestamp>-<ordinal>-<tool>-<random-nonce>; a later call in one round therefore captures state produced by earlier calls
  2. Dirty state capture — If the worktree is dirty, git stash create captures a snapshot without touching the working tree or reflog
  3. Untracked sidecars — Direct file tools preserve an untracked target. Write-risk bash/awk/csvkit checkpoints inventory up to 500 existing untracked paths, preserve regular files within 8 MiB/file and 32 MiB total bounds, and preserve bounded UTF-8 symlink targets without following them. Non-UTF-8 names, unsupported types, and path/type/size bounds are explicit partial-recovery gaps. Checkpoint storage containers must be real directories; on Unix they must be current-user-owned, .dext must not be group/world-writable, and managed checkpoint, sidecar, and blob directories are owner-private. Locked Unix mutations may repair modes on current-user-owned managed directories and read-only mode drift on owner-owned manifest/lock files; inspection does not repair them, restore rejects unsafe sidecar/blob containers, and pruning retains unsafe artifact directory trees with bounded warnings while unlinking only an orphan top-level sidecar symlink without following it. Regular-file content is stored in owner-private SHA-256-addressed blobs shared by retained checkpoints; unchanged source paths reuse the session cache only while source and blob metadata fingerprints remain stable, preview/restore rehash blobs before trusting them, and prune or failed checkpoint creation removes valid unreferenced/new blobs. Malformed or unsafe blob entries and sidecar directory trees remain untouched and produce bounded warnings without stopping other retention cleanup; an orphan top-level sidecar symlink with a valid checkpoint ID is unlinked without following it. Owner execute state is descriptor metadata. Manifest rows carry 11 or 12 tab-separated fields; the retired pre-JSON 8/9-field encodings are no longer parsed, so every path in a current manifest is validated by one repo-relative rule with no weaker alternative a row can select. Current 12-field manifests record exact direct-sidecar membership; 11-field manifests without that field fail conservatively before mutation when a missing artifact is ambiguous rather than deleting current path content. A recognized retired row must match the complete retired field grammar and is skipped with a warning so one stale row cannot disable /undo or block write-risk tools. Its recorded OID must match any live checkpoint ref; retention publishes the compacted manifest before deleting expired or integrity-matched retired refs and artifacts, so cleanup failure leaves orphan state instead of a manifest naming a deleted recovery point. Mismatches and other corrupt or tampered rows fail the manifest closed. A recovery gap triggers separate repository/session-scoped approval: denial blocks the command, while approval preserves tracked/staged state and the bounded subset. Other checkpoint failures remain fail-closed. Repositories without an initial commit still block writes that would overwrite existing state. A workspace with no .git marker is non-Git unless ambient GIT_DIR/GIT_WORK_TREE/GIT_COMMON_DIR routing exists; routed-without-marker and malformed-marker cases fail loudly because Dext-owned Git commands scrub routing variables.
  4. Manifest — Appended to owner-private .dext/checkpoints/manifest.txt; /.dext/ is added to the repository-local Git exclude. Rows are tab-separated with 11 or 12 fields; a 12-field row adds the exact direct-sidecar membership index. Untracked preview entries with unsafe host-native targets are omitted, and a malformed field fails its row closed rather than degrading it. Runtime manifest reads are capped at 16 MiB; doctor inspects at most 256 KiB and reports a larger runtime-bounded manifest as uninspected rather than corrupt. Symlinks, hard links, foreign ownership, and group/world-write access remain integrity-fatal with observed modes and actionable errors. Owner-owned manifest/lock files that are only group/world-readable are normalized to 0600 at startup and the pre-mutation boundary. Checkpoint-free dext checkpoint repair and /undo --repair quarantine invalid manifest bytes as manifest.txt.quarantine-<timestamp>-<nonce>, recreate an empty private manifest, and retain hidden refs until prune. Retention writes the compacted manifest first, then removes expired/retired refs and orphan artifacts while holding the operation lock; forensic quarantine files survive ordinary prune.

Restore Modes

ModeDescription
PreviewShow diff vs current worktree without modifying anything
WorktreeRestore affected paths from the checkpoint OID with literal Git pathspecs; revalidate and atomically replace captured sidecar files/symlinks on supported platforms
ResetHeadHard reset HEAD to checkpoint's original HEAD

Pruning and Repair

Default pruning keeps the 20 newest checkpoints and removes entries older than 168 hours (7 days). Pruning first publishes the reconciled local manifest, then removes orphaned refs/dext/checkpoints/*, legacy sidecar directories, and valid unreferenced content-addressed blobs for bounded disk growth. Malformed or unsafe blob entries and sidecar directory trees are retained for inspection and reported with bounded warnings without stopping other cleanup; an orphan top-level sidecar symlink with a valid checkpoint ID is unlinked without following it. Quarantined manifests are preserved as forensic evidence. Use dext checkpoint repair, dext undo --repair, or /undo --repair when doctor reports manifest corruption. Never mirror-push refs/dext/*.

Checkpoint Triggers

Always checkpointed: write_file, edit_file, multi_edit, todo_write, git_commit. Other tools are checkpointed when tool_policy::classify_command_risk() returns Write or Danger. Required checkpoint creation is fail-closed before execution; HTTP and other non-filesystem side effects are not recoverable merely because a local Git checkpoint exists.

Mutation_preview.rs .rs

Generates in-memory line-level mutation previews before applying direct file mutations. Shows the preview during permission prompts without touching disk.

Preview Flow

  1. Read the current file content (or empty for new files)
  2. Compute the proposed content (apply edit/multi-edit transforms in memory)
  3. Generate an alignment-aware Myers line diff with one context line around changed hunks; final-newline-only changes are explicit, and inputs above the line budget use a bounded conservative fallback
  4. Keep added/removed counts for the full proposed change even when rendered output is truncated
  5. Cap at 4,096 bytes with a truncation notice
  6. Show added/removed line counts and whether it's a new file

Sandbox Validation

All mutation paths are canonicalized and validated against the effective write scope. Inside a Git worktree that scope is the canonical repository top-level, even when Dext was launched from a subdirectory; command cwd and project context remain the selected sandbox directory. Canonicalization normalizes Windows verbatim-path forms before native Git pathspec, file mutation, checkpoint, and optional workspace-write scope comparisons. /sandbox prints both cwd and write scope and can widen/change them in-session. Paths outside that scope are rejected with the effective root and remedy. Cross-platform display and Git pathspec rendering strips Windows verbatim prefixes and normalizes separators to forward slashes; work-ledger filtering recognizes both native and foreign-platform absolute paths.

Extensibility

Orchestrator.rs — Runtime Intelligence .rs

Per-turn runtime state that guards against loops, manages external resource access, and tracks objectives.

Work Phases

PhaseDescription
ProbeInitial exploration — reading files, searching, understanding the codebase
ScaleExpanded collection — triggered when probe passes for external hosts
SynthesizeFinal delivery — writing files, committing, producing the answer

Guard Systems

  • Dedupe cache — Short-circuits identical external requests within a turn
  • Similarity guard — Blocks after 3+ similar unproductive bash/file operations
  • Tool retry budget — Circuit breaker after 3+ failures with same error signature
  • Empty tool call loop detection — Detects provider bug where tool arguments are dropped (threshold: 4 consecutive empty calls)
  • Auth circuit breaker — Blocks hosts after 2 auth failures
  • Feasibility guard — Requires single-item probes before bulk external collection

Objective Tracker

Parses user prompts to extract checkpoints (plan, analyze, implement, verify, document). Exact verification keywords avoid turning descriptive morphology such as verifiable into new work. Satisfaction is refreshed after every tool round and is derived from tool usage, bash commands, and explicit completion reports such as “checks pass,” so the next model request does not receive a stale [unresolved] checkpoint after custom checks pass.

Adaptive Caps

Tool UI content and result caps scale down dynamically based on context pressure: ≥90% → 25%, ≥75% → 50%, ≥60% → 75% of default. This preserves critical evidence near context limits while reducing prompt bloat.

Packs.rs — Pack System .rs

Packs are shelf-contained, source-first workflows with an optional bounded executable runtime. Dext supplies the create, discover, inspect, maintain, and run lifecycle; users own the content. PACK.md-only packs orchestrate the native tool surface. A reviewed pack may additionally declare runtime.json protocol v1 to expose dynamic tools only while active, without adding pack-specific code to the Dext binary.

Design Contract

  • PACK.md-only packs add no provider tools; optional reviewed runtimes may add a bounded active-pack tool set.
  • Source-first reviewability: every pack is plain files in a directory.
  • Deterministic discovery and precedence: first resolved name wins after ordered search roots.
  • Optional hooks via phooks.json and optional one-shot native runtime via runtime.json; both remain separate from project hooks.json.
Battery-pack model: Dext ships no packs. Every pack lives under <shelf>/packs/<name> in a user, project, or explicitly configured shelf root.

Pack Object Model (PackInfo)

packs.rs normalizes each discovered pack into PackInfo with:

  • name, description, declared credential_env names (from front matter or directory defaults)
  • path, pack_md_path, optional phooks_path, optional runtime_path
  • source label (project/env/user) and shelf ownership

Invocation wraps this as PackInvocation { pack, task } so the task is explicit and auditable.

Discovery and Creation

  1. Project shelves: .dext/shelves/*/packs
  2. Explicit shelf roots: DEXT_SHELVES_DIR
  3. User shelves: ~/.dext/shelves/*/packs

Create reusable packs with dext pack create <shelf>/<name>; add --project only for an explicitly project-local pack. The scaffold creates a minimal PACK.md, refuses overwrite, and is immediately available to normal inspection and explicit invocation. Direct packs/, .dext/packs, ~/.dext/packs, DEXT_PACKS_DIR, and per-pack override roots are not discovery inputs.

Project-local PACK.md content has a repository trust boundary. An explicit /pack or dext pack invocation confirms only that selected project workflow; it does not approve unrelated project shelf metadata. Conversational auto-invocation prompts once per active repository before loading project workflow text. Choosing Always stores a bounded owner-private, single-link project-scoped approval marker on Unix; unsafe or permissive markers are ignored, and /project-extensions reset refuses unsafe marker shapes while removing a safe marker or clearing a session denial. Denied project pack metadata stays out of the model prompt and cannot shadow a same-named user or run-shelf pack. Project-local credential declarations remain ignored.

The resolver deduplicates by canonicalized path and normalized pack name. Name collisions are resolved by precedence order, not random filesystem order.

Pack Layout and Front Matter

my-pack/
├── PACK.md          # required; markdown workflow with YAML front matter
├── runtime.json     # optional bounded executable protocol v1 manifest
├── phooks.json      # optional hook template for this pack
├── bin/             # optional ordinary or runtime helpers
└── README.md        # optional human docs
---
name: my-pack
description: One-line workflow summary
credential-env: [SERVICE_TOKEN]
---

# Workflow
1) Setup
2) Probe
3) Transform
4) Verify
5) Deliver

Only PACK.md is required. Front matter stays minimal: identity/description plus an optional inline credential-env list of exact credential-shaped names required by the pack's own helper. That field is honored only for user and DEXT_SHELVES_DIR packs; project-local declarations are ignored.

Prompt Injection Model

pack_prompt() builds a bounded invocation prompt (cap: PACK_PROMPT_CAP = 32_000 bytes) containing:

  • pack identity and source metadata
  • paths for workflow and optional hooks
  • pack-scoped environment names (DEXT_PACK_DIR, DEXT_PACK_<NAME>_DIR), which the runtime supplies to active-pack bash calls and hook processes
  • declared credential names only, never their values
  • capped PACK.md workflow body
  • explicit user task text

This gives the model procedural guidance without changing core runtime policy unless the selected pack declares an approved runtime. Workflow loading and optional runtime activation must succeed before pack hooks or environment are activated. Once active, phooks.json entries are added to the hook set, and pack-scoped environment variables are passed to subsequent bash tool commands and hook processes. Declared credential values are narrower: for user and DEXT_SHELVES_DIR packs, they reach only a simple direct invocation of the active pack's own ordinary native bin/ helper, never runtime helpers, hooks, arbitrary bash, pipelines/redirections, prompts, logs, or sessions; provider-auth names remain excluded. Project-local declarations are ignored. Changing the sandbox root clears active pack state.

Compact pack/shelf metadata is byte-bounded and normalized to one safe line before entering the stable prompt. Shelf Context bodies may retain ordinary newlines and tabs, but terminal controls and Unicode line separators are normalized before injection.

Pack Runtime Protocol v1

src/pack_runtime.rs loads runtime.json only as a regular non-symlink file capped at 256 KiB. The manifest declares version 1, a relative regular executable no larger than 256 MiB inside the canonical pack root, optional bounded arguments, a 1–604800-second timeout, a continuation budget, and at most 32 dynamic tools. Tool names must be provider-safe and collision-free across the full native catalog, active dynamic tools, and host approval pseudo-operations. Recursive schemas require an explicit supported type at every node and accept only type, properties, required, boolean additionalProperties, items, enum, and bounded description; schema annotations follow the session's lean/full tool profile on provider wires. Every tool declares read, write, or danger risk, defaulting to write.

Approval always automatically authorizes runtime activation after exact executable identity validation; approval never disables it, while prompt-level Always under prompting profiles is in-memory, nonserialized, and scoped to the exact canonical pack-directory/source identity, manifest digest, and executable digest. Dext displays the executable SHA-256 at approval and rehashes the regular no-follow file before every invocation; changed bytes require reactivation. Changing approval or sandbox policy revokes the active runtime, its dynamic grants/denials, and queued callbacks. Activation, idle events, and every declared tool use the selected sandbox profile consistently. Write/danger tools retain durable side-effect journaling and fail-closed Git checkpoint controls. Runtime helpers are one-shot process-group-contained calls, receive no inherited credentials, and exchange exactly one JSON request/response through stdin/stdout. A present malformed timeout override fails closed. The configured deadline bounds stdin delivery and root execution; output drain after process-tree cleanup has a separate one-second cap. The default is 120 seconds; manifest timeout_seconds configures it and DEXT_PACK_RUNTIME_TIMEOUT_SECS overrides it within the same 1–604800 bound.

Requests identify activate, tool, or idle, plus pack/session/project context, bounded state, turn/iteration/compaction context, and optional tool input. Responses may contain content, an error bit, replacement state, and at most 16 effects: steer, delayed continue (maximum 30 seconds), or a Markdown view. Request/response size is 256 KiB, state 64 KiB, content/effect text and views 128 KiB; the subprocess capture path preserves that full response ceiling. Runtime-exposed content, effect text, view titles/Markdown, and queued prompts reject unsafe terminal controls. State/effects/continuation accounting validates and commits atomically. State, used continuation count, and at most 32 queued prompts totaling 64 KiB persist inside the existing owner-private 256 KiB session-header ceiling; interrupted delayed prompts are canceled and refunded. Every runtime result uses a durable state/result checkpoint, including read-risk calls. Resume preserves current-run approval and sandbox policy and discards saved grants. Before changing the live agent, it preflights project-extension trust, exact saved source plus canonical pack-directory fingerprint, manifest/hash/state accounting, and approval against the current executable digest; changed, missing, shadowed, denied, or malformed runtimes cannot partially apply saved sandbox/model/session state. Content, steering, views, and surfaced activation/idle errors are privacy-redacted. Opaque structured state is bounded and owner-private but is not rewritten by privacy redaction, so helpers must not place secrets in state. Active dynamic tools participate in /allow, /revoke, and /allowed.

The user-owned autoresearch pack exercises this protocol with bounded experiment state, metrics, checks, continuation, and Markdown views. It requires a repository with an initial commit and a real non-symlink .auto directory; measurement, check, and hook programs must be executable regular non-symlink files in their expected locations. Its helper restores persisted segment/cap/continuation/stopped state plus measured-but-unlogged results; stopped or capped segments reject new runs until reinitialized, and reinitialization cannot discard an unlogged result. Each repository using the pack owns its local .auto experiment workspace; the Dext source repository ignores its root .auto/, while reusable pack code belongs in the user's shelf. Append-only .auto/log.jsonl evidence is a bounded regular no-follow, single-link file outside keep commits. Runtime-owned Git calls scrub ambient routing/credentials and suppress hooks, fsmonitor, signing, external diff, and configured filter subprocesses. Only one active autoresearch/Dext session is supported per Git working tree because protocol state is session-bound.

Execution Paths

  • CLI: dext pack create <shelf>/<name>, list, inspect, and run
  • Interactive: /pack create, list, inspect, and run
  • Conversational detection: invoke <name> / run <name> on ... patterns mapped by selector inference

Operational Guidance for Pack Authors

  • Write reproducible loops: inputs, stop conditions, verification gates.
  • Install reusable packs under user-global Dext scope by default; use project-local roots only for intentionally repo-scoped packs.
  • Keep shell helpers idempotent; prefer deterministic outputs for agent reuse.
  • Use phooks.json only when needed; packs must still function without hooks.
  • Treat pack workflows as code: version, test on disposable repos, and review diffs.

Outside the active sandbox, native mutation tools may write only below a concrete user pack directory (~/.dext/shelves/<shelf>/packs/<pack>/...) containing a regular PACK.md. Shelf manifests and loose files directly under packs/ remain outside that exception. Dext revalidates the destination and marker before atomic replacement. Same-user replacement is outside Dext's isolation boundary.

Shelves.rs — Shelf Registry .rs

Shelves are typed metadata registries over packs. They describe capabilities and signal/effect behavior without adding provider-visible tools.

Why Shelves Exist

  • Give the model structured capability hints (commands/hooks/context) with low token cost.
  • Separate extension metadata from prompt prose.
  • Enable scoped override rules across core/user/project/run environments.

Manifest and Type System

shelf.json is parsed into ShelfManifestPackManifestAbility enums:

TypePurpose
ShelfManifestIdentity, description, origin/scope, mode, and contained packs
PackManifestPack id/name/version/description with typed abilities
AbilityTool, Command, Hook, Context
GrantRead/Write/Network/Process/Secret/Browser permission intent metadata
ExposureHidden / OnDemand / Visible model-facing exposure level

Signal and Effect Types

The shelf module defines a typed signal/effect vocabulary for in-process shelf implementations. Filesystem shelf.json manifests must be regular non-symlink files no larger than 1 MiB and are declarative metadata: a matching load/prompt hook declaration can opt declared context into bounded prompt injection, but manifests do not register executable tools or slash commands and do not supply arbitrary effect handlers.

SignalsTypical Use
load, prompt, tool(before|after), turn(start|end), compact, shutdownTyped lifecycle vocabulary for in-process shelves
EffectsBehavior
noteDiagnostic annotation
contextInject prioritized context text
blockHard-stop signal flow with explicit reason
rewrite_toolTyped input-rewrite effect for an in-process shelf; not executed from a static manifest
stateTyped state effect for an in-process shelf; static manifests do not persist it

Block effects terminate in-process signal flow early. Static manifest shelves only produce context/note data for opted-in load or prompt signals. Under approval always, project shelf metadata proceeds automatically; prompting profiles request first-use confirmation per active repository, and choosing Always persists a bounded owner-private, single-link project-scoped decision marker on Unix. Unsafe or permissive markers are ignored, and /project-extensions reset refuses unsafe marker shapes while removing a safe marker or clearing a session denial. Before approval under a prompting profile, project metadata is omitted from the model prompt, cannot shadow same-key trusted user/run metadata, and cannot contribute behavioral tool effects, while approved project context is explicitly labeled repository-controlled.

Discovery and Scope Precedence

ScopeRankLocationIntent
Core0In-process registrationBase defaults; release binaries do not probe build-machine source paths
User1~/.dext/shelvesUser-level customization
Project2.dext/shelvesRepo-specific policy/context
Run3DEXT_SHELVES_DIRPer-invocation highest-priority overrides

resolve() keeps one winning ability per key using tuple ordering by scope rank and stable discovery order. This gives deterministic override semantics.

Prompt and CLI Surfaces

  • render_registry_listing() powers dext shelves / /shelves operator visibility.
  • registry_summary_for_prompt() injects compact typed ability summaries into model context.
  • Ability metadata is provider-neutral and does not modify the external tool schema.

Methodology for Building Shelf Ecosystems

  1. Start with pure packs; add shelf metadata only for reusable capability contracts.
  2. Define ability keys as stable API-like surfaces.
  3. Use project scope for repository policy, user scope for personal defaults, run scope for experiments.
  4. Treat shelf.json as versioned interface documentation and test precedence resolution.

Testing & Performance

Testing

Unit Tests (main_tests.rs)

The test suite covers provider catalogs and request shaping, auth and state formats, stream/tool-call assembly, tool policy, sandboxing, side-effect journals, recovery checkpoints, TUI rendering, and runtime orchestration. Unit tests are hermetic against user state: when a test does not set DEXT_HOME, the state directory resolves to one throwaway per-process home under the OS temp root, so live test agents cannot leak project/session residue into the real ~/.dext. Git fixture subprocesses explicitly clear ambient repository-routing variables such as GIT_DIR and GIT_WORK_TREE, so parallel environment-isolation tests cannot redirect unrelated temporary repositories. Pricing override coverage uses pure default/override composition rather than mutating process-global pricing variables, so parallel local-provider and cloud-pricing tests cannot observe transient test values.

  • Provider catalog loading, normalization, and model resolution
  • Auth store operations (save, load, normalize)
  • Session header parsing and backwards compatibility
  • Tool input validation and risk classification
  • Bash guardrails and sudo detection
  • Checkpoint creation and restore logic
  • Objective tracking and satisfaction assessment
  • URL parsing/error redaction, proxy isolation, shared bounded DNS reuse/lookups, destination policy, dangerous/duplicate header rejection, exact decoded-body caps, bodyless response semantics, safe cross-origin GET/HEAD redirects, sensitive redirect replay blocking, and automatic-Referer suppression; redirect fixtures use bounded blocking reads on accepted sockets for consistent Windows behavior

Run with: cargo test --release --locked

Real-PTY TUI Regression Suite (tests/tui_smoke.rs)

Spawns the real Dext binary in a Unix PTY at narrow and wide terminal sizes and verifies:

  • Application launch, banner/composer rendering, help, multiline input, and clean Ctrl+D exit
  • Editable input while output streams and process survival during populated-history resize bursts
  • One visible-display clear before one scrollback purge per effective populated-transcript width change, exactly one Dext intro per replay segment, resize-bounded cursor queries, and terminal-height-bounded complete replay chunks
  • Completed stream output and accepted input after resize, with no crash markers

Run with: cargo test --release --locked --test tui_smoke -- --nocapture

The complete verification gate also runs formatting, Clippy with warnings denied across the Linux, macOS, and Windows CI matrix, vulnerability auditing, dependency-license policy checks, the vendored ratatui-core unit tests, a locked release build, Unix PTY smoke tests, and the native Windows ConPTY smoke test. Windows CI and release builders parse and execute the full installer harness under both inbox Windows PowerShell 5.1 and PowerShell 7; each engine evaluates the installer from in-memory text through Invoke-Expression, matching the public irm | iex path. Platform-specific fixtures follow host filesystem semantics: the invalid-UTF-8 checkpoint filename case runs only on Unix filesystems that permit creating it, because macOS APFS rejects that filename before Dext can inspect it. Release publication accepts only an annotated tag whose commit is contained in origin/main and whose exact vX.Y.Z value matches Cargo.toml. It generates dext.cdx.json, includes it in SHA256SUMS, and attests and verifies it alongside the four platform archives. The first successful tag run and its end-to-end evidence are recorded in docs/RELEASING.md. On Windows, the scheduler-sensitive fast_bash_command_returns_without_100ms_poll_tail regression runs alone after the remaining release tests. A dedicated child helper flushes a wall-clock timestamp immediately before exiting; the parent measures from that marker until Dext returns and accepts the fastest of three post-exit samples under the original <90 ms bound. This excludes shell startup from the measurement while still detecting a structural 100 ms polling tail, and isolation prevents unrelated suite load from obscuring the process-wait regression. The stdin-backpressure regression still requires bounded completion under the shared deadline, but accepts either the stdin-write or root-process timeout phase on Windows because pipe buffering can complete the write at the deadline boundary; Unix continues to require the stdin-write phase. The tool-call mock provider consumes its bounded Content-Length request body before responding so Windows does not reset the connection with unread request data. See docs/TUI.md for renderer-specific live-terminal checks.

GitHub Pages

The public site at siliconstate.github.io/Dext is deployed from the static docs/ tree by .github/workflows/pages.yml. The workflow validates local links and anchors, uploads an immutable Pages artifact, and gives the pinned deployment action a bounded 15-minute status-poll deadline within a 20-minute job ceiling. Only the deployment job receives pages: write and id-token: write. Third-party actions are pinned to full commit hashes and checkout credentials are not persisted.

Repository release controls: verified on 2026-08-07, main strictly requires Ubuntu, macOS, and Windows CI; active v* rules prevent tag updates and deletion; releases are immutable; and private vulnerability reporting, vulnerability alerts, and Dependabot security updates are enabled. Initial release-tag creation remains a trusted maintainer action guarded by annotated-tag, origin/main-ancestry, and package-version workflow validation; this residual boundary remains tracked in R-008.
Optional confined-profile limitation: if a developer explicitly selects workspace-write or read-only, shared-temp, PTY, and Cargo-home writes may be denied. The default danger-full-access profile adds no such Dext restrictions, while any parent container, VM, namespace, seccomp, Seatbelt, service-account, or host policy remains authoritative.

Benchmarks (benches/dext_bench.rs)

Criterion benchmarks for performance-critical paths:

BenchmarkWhat it measures
production_sse_decode/coalesced_provider_readProduction SSE framing for 512 events delivered in one provider read
production_sse_decode/fragmented_97_byte_readsThe same production decoder under fragmented network reads

Run with cargo bench; CI and the release quality job compile the benchmark with cargo bench --no-run --locked.

Build Profile

Release builds use: lto = false, codegen-units = 16, strip = "symbols" — optimized for build speed while keeping binary size small.

Reference

CLI Reference

CommandDescription
dextStart interactive session
dext "prompt"One-shot task
dext -pRead prompt from stdin
dext --resumeResume latest session
dext --seat <name>Start a new session with a durable project identity
dext --seat <name> --resumeResume that Seat's latest durable session
dext seat list|show <name>Inspect project Seat records
dext seat set <name> ...Set or clear a bounded Seat label/summary
dext --forkFork latest session into new session
dext session ...Brief/export/analyze/grep/failures/verification/decision/prune operations
dext doctor [...]Inspect effective safety policy and bounded local state without repair or provider calls
dext pack list|inspect|run ...Discover or invoke source-first packs
dext shelvesList typed shelf manifests and ability metadata
dext --cd <dir>Change command cwd/project context; in Git repositories, mutation/Git/checkpoint write scope is the containing top-level
dext --no-sessionDon't save session
dext --pack <name> "task"Invoke a pack in one-shot mode
dext --frugalSelect the reduced-context mode
dext --context-mode standard|frugalChoose context/cap mode
dext --toolset default|fullChoose provider-visible tool count profile
dext --tool-profile lean|fullChoose provider tool schema verbosity
dext --preview off|simple|gitChoose mutation preview mode
dext --sandbox read-only|workspace-write|danger-full-accessSet sandbox profile
dext --eval [NAME] [--sandbox PROFILE]Run eval harness under the resolved sandbox profile
dext --output json|stream-jsonMachine-readable output
dext --effort off|minimal|low|medium|high|xhigh|maxModel reasoning effort; supported levels are clamped per model
dext --reasoning-mode standard|proSelect GPT-5.6 execution mode; sent only by official OpenAI Responses requests
dext --approval ask|auto-read|auto-write|never|alwaysApproval profile
dext --trustExplicitly select approval always
dext --no-trustExplicitly select the ask approval profile
dext --budget <amount>Session budget cap: dollars, tokens, or one of each (e.g., $0.50, 100ktok, $0.50 + 100kt); duplicate dimensions are rejected

Auth Subcommands

CommandDescription
dext auth providersList available providers
dext auth login <provider> [credential|web|import]Open a provider login flow; omit command-line credentials for interactive use
dext auth logout <provider>Remove stored credentials
dext auth statusShow auth status for all providers

Other Subcommands

CommandDescription
dext undo --listList recovery checkpoints
dext undo --preview <id>Preview checkpoint restore without modifying files
dext undo --apply <id>Apply a checkpoint restore
dext checkpoint repairQuarantine an invalid checkpoint manifest byte-for-byte and recreate an empty private manifest
dext undo --repairAlias for checkpoint manifest repair
dext undo --pruneRemove expired and excess checkpoints plus orphan refs/artifacts
dext pack listList available packs
dext pack inspect <name>Show pack details
dext pack run <name> "task"Run a pack
dext shelvesList shelf manifests and abilities

Slash Commands

Slash completion lists each /login provider exactly once by its provider id; numbered selector aliases remain accepted but are not shown as duplicate login choices. Planning is conversational rather than a separate mode: ask Dext to inspect and propose a plan without editing, revise it in the same thread, then tell it to proceed. The retired /plan command is no longer intercepted, so equivalent text is handled as an ordinary prompt. When a prompt reads as planning/analysis-only (for example “plan …”, “review …”, “don’t change anything”), Dext injects an advisory-only turn policy into the volatile runtime status directing read-only tools and a structured Goal/Findings/Steps/Risks answer; a bare approval such as “go” or “proceed with the plan” injects an implementation policy directing the agreed plan into todos before editing. Explicit mutation requests always win over scoping clauses, question-phrased prompts (ending in ?) are never treated as approvals, and mid-turn queued user updates re-evaluate the policy so a steering approval or hold-off takes effect immediately. The policy note steers rather than blocks — approval prompts and /sandbox-profile read-only remain the deterministic enforcement layers for untrusted or weaker models.

Structured listings from /help, /tools, /system, packs, shelves, and sessions retain the established /sessions hierarchy: compact count headers, bold section labels, two-space names, four-space details, and detached Use: footers. Dense name/description catalogs such as /help use aligned rows at 64 columns and wider, with hanging description wraps, and fall back to stacked rows on narrow panes. The TUI passes its actual transcript-pane width into slash rendering; output keeps a two-cell gutter and a 120-column readability cap. /system preserves prompt paragraphs, blank lines, and leading indentation while wrapping prose rather than hard-splitting every display-width chunk. Dynamic fields have terminal escapes, controls, line separators, and bidi formatting controls removed before layout. Every physical row is bounded by Unicode display cells; only a grapheme that cannot fit in an otherwise impossible one-cell measure is replaced with ?. An explicit structured-slash event keeps listings free of dim bullet prefixes when ANSI color is disabled; generic confirmations such as /model and thinking-effort status retain the faded info treatment.

CommandDescription
/helpShow available commands as a grouped listing in the established /sessions presentation
/quit, /exitExit Dext
/resetClear conversation history
/tools [default|full]List (profile, exposed, permission-gated, explicit session grants, hidden sections) or switch provider-visible tools
/historyShow turn count and last 5 messages
/system [text]Show the composed system prompt with its source files, or replace the base prompt
/allow <tool>Auto-approve a native or active runtime tool for this session
/revoke <tool>Remove auto-approval for a tool
/allowedList native and active-runtime grants
/trust [on|off|status]Auto-approve all gated tools
/approval [profile]Set approval profile
/preview [off|simple|git]Set mutation preview mode
/sandbox [path]Show cwd plus effective write scope, or change the sandbox root in-session
/sandbox-profile [profile]Set sandbox: read-only, workspace-write, danger-full-access
/privacy [on|strict|off|status]Control local redaction and optional strict native-read path/search-scope blocking
/effort [level]Set thinking effort: off, minimal, low, medium, high, xhigh, max
/reasoning-mode [standard|pro|next|prev|status]Select or inspect GPT-5.6 execution mode; reports inactive outside official OpenAI Responses
/model [name]Show or change model
/provider [id]Show or change provider
/modelsList available models
/providersList providers and auth status
/login <provider> [credential|web|import]Login to a provider; pending API keys/tokens and manual OAuth callbacks can be pasted directly in Dext
/logout <provider>Logout from a provider
/compact [N%]Run compaction or set threshold. After compaction the generated summary is rendered once into TUI/CLI history for every provider; the summary model’s own stream stays muted so responses-contract providers no longer leak it as live assistant output while others showed nothing
/context [mode]Set context mode: standard, frugal
/tool-profile [lean|full]Set provider tool schema verbosity
/usageShow cumulative token usage
/statusShow runtime diagnostics
/tokensApproximate tokens per message and top context hogs
/diagnosticsRun rust-analyzer diagnostics, falling back to cargo check
/save <name>Save history/config to named JSONL session
/export [html|jsonl] [path]Export the current session
/resume [name]Resume latest or named session
/sessions ...List session history in the established session presentation, or analyze/brief/grep/failures/verification/decisions
/budget [cap]Set/show a dollar, token, or combined cap; off clears it
/pack create|list|inspect|runCreate or use shelf-contained packs
/shelvesShow shelf registry
/project-extensions [status|reset]Inspect or reset repository-scoped project extension approval
/undo [--list|--apply|--repair|<id>]Recovery checkpoint preview/apply/repair management

Tool Reference

Dext implements 18 built-in provider-visible tools. The default profile contains 13 core tool names. Specialized built-ins (jq, fzf, awk, git_log, csvkit) require dext --toolset full, DEXT_TOOLSET=full, or /tools full. Ordinary workflow packs orchestrate this surface; a separately approved runtime.json pack may add bounded dynamic tools for the active session.

File Operations

ToolPermissionDescription
read_fileReadRead file with line numbers. Positive offset/limit windows get a larger cap, stop once additional data is detected, and can be cancelled between bounded chunks.
read_symbolReadLocate a source symbol or block around a line number. Selector ranges are validated; loading is cancellable and capped at 8 MiB.
write_fileWriteWrite content to file, creating parent dirs. Overwrites existing.
edit_fileWriteReplace exactly one occurrence of old_string with new_string.
multi_editWriteBatch of exact-string edits applied atomically to one file.

Search & Discovery

ToolPermissionDescription
fdReadFind files by regex pattern. Subprocess-execution flags are rejected.
rgReadSearch file contents. Preprocessor and subprocess-capable flags are rejected.
fzfRead, full toolsetNon-interactive fuzzy filter.

Shell & Process

ToolPermissionDescription
bashVariesExecute shell command with pipefail. Risk classified.
awkVaries, full toolsetAWK text processor. Optional stdin.

Data Processing

ToolPermissionDescription
jqRead, full toolsetQuery/transform JSON.
csvkitVaries, full toolsetCSV processing suite: csvcut, csvstat, csvgrep, csvjson, etc.

HTTP

ToolPermissionDescription
httpVariesHTTPie-style client with HTTP/2 and gzip/Brotli decoding; provider, OAuth, and local-context clients retain HTTP/1/no-auto-decompression behavior. The entire DNS answer is validated before one shared 256-entry cache retains at most 32 addresses per host; connect, lookup, idle-read, 10-minute total-request, redirect, and exact decoded-response bounds apply. --extract-text reads at most 128 KB of source. Duplicate/framing/method-override headers and URL credentials are rejected; errors omit URLs; automatic Referer and HTTPS downgrades are disabled. Headerless/bodyless GET and HEAD requests may follow validated cross-origin redirects, while sensitive requests remain same-origin. GET, HEAD, and OPTIONS requests with raw bodies are Danger-class. IPv4 current-network/broadcast, IP multicast, and IPv6 unspecified destinations are always blocked. Loopback, private/CGNAT, unique-local, link-local, and metadata destinations are blocked unless narrowly opted in.

Git Operations

ToolPermissionDescription
git_diffReadShow git diff. Prefer stat=true first.
git_logRead, full toolsetShow recent git log entries.
git_commitWriteStage files and create commit.

Task Management

ToolPermissionDescription
todo_readReadRead project todo list.
todo_writeWriteReplace project todo list.

Environment Variables

VariableDefaultDescription
DEXT_HOME~/.dextDext state directory
DEXT_SESSIONS_DIRProject-scopedOverride sessions directory
DEXT_LOGS_DIRProject-scopedOverride logs directory
DEXT_LOG_ARCHIVES0Number of log archives to keep (max 16)
DEXT_PROVIDERCatalog activeActive provider ID
DEXT_PROFILECatalog activeAlias for DEXT_PROVIDER
DEXT_API_PROVIDERCatalog activeProvider selection by API family
DEXT_API_KEYOverride API key for any provider
DEXT_MODELProfile defaultOverride model for any provider
DEXT_MODEL_FORCEfalseForce DEXT_MODEL even if incompatible
DEXT_PROVIDER_CONNECT_TIMEOUT_SECS15Provider TCP/TLS connection deadline
DEXT_PROVIDER_FIRST_BYTE_TIMEOUT_SECS180 cloud · 600 localDeadline through response headers; one positive override applies to all providers
DEXT_PROVIDER_STREAM_IDLE_TIMEOUT_SECS90 cloud · 300 localMaximum idle interval between provider body/stream chunks
DEXT_MODEL_{PROVIDER}Provider-specific model override
DEXT_BASE_URLProfile defaultOverride base URL for any provider
ANTHROPIC_BASE_URLProfile defaultOverride Anthropic-family base URL
CLAUDE_CONFIG_DIRUser homeOptional Claude state directory used only to read validated installation/account identity from .claude.json in memory for official Anthropic subscription requests
OPENAI_BASE_URLProfile defaultOverride OpenAI-family base URL
DEXT_CONTEXT_MODEstandardContext mode: standard, frugal
DEXT_THINKING_EFFORTmediumModel reasoning effort: off, minimal, low, medium, high, xhigh, max
DEXT_REASONING_MODEstandardSelected GPT-5.6 execution mode: standard or pro; sent only by the capability-scoped official OpenAI Responses route
DEXT_COMPACT_MODELActive modelOptional same-provider summary model; normalized through the provider and used for summary request contract, reasoning capability, mode, and usage pricing
DEXT_MAX_OUTPUT_TOKENSModel/default capPositive streaming output-token override; ChatGPT/Codex deliberately omits the unsupported field
DEXT_TOOLSETdefaultProvider-visible tool count profile: default, full
DEXT_TOOL_PROFILEleanTool schema profile: full, lean
DEXT_MUTATION_PREVIEWsimpleMutation preview mode: off, simple, git
DEXT_APPROVALalwaysApproval profile: ask, auto-read, auto-write, never, always. Invalid values warn and use ask unless a valid CLI choice or true DEXT_TRUST takes precedence.
DEXT_SANDBOX_PROFILEdanger-full-accessSandbox profile: read-only, workspace-write, danger-full-access. Invalid values fail normal startup unless a valid CLI sandbox override takes precedence. Full access adds no Dext confinement and preserves ambient temp/cache configuration inside the current environment. Optional confined profiles use native Linux/macOS controls when available and otherwise warn and continue with path guards.
DEXT_PRIVACYtrueBefore model context and session logging, replace private-key blocks, real secret assignments, and explicitly labeled SSNs, payment-card numbers, and account identifiers while keeping user-readable files readable. Set to strict to also block sensitive-looking native paths plus hidden, ignored, symlink-following, and sensitive-glob search scopes, including compact/combined ripgrep forms such as -g.env and -ig .env plus wildcard-prefixed sensitive globs such as *.env; disable only for intentionally raw output.
DEXT_INHERIT_TOOL_CREDENTIALSfalseHigh-trust opt-in to pass credential-shaped parent environment variables to model-invoked bash/external tools. Hooks and Dext-owned subprocesses remain scrubbed.
DEXT_BUDGET_CAPSession cap in dollars, tokens (t/tok/token(s)), or one of each separated by +/,; duplicate, empty, or otherwise invalid components fail startup instead of disabling the guard
DEXT_CONTEXT_WINDOW_TOKENSProfile defaultOverride context window size
DEXT_CONTEXT_WINDOWProfile defaultAlias override for context window size
DEXT_BASH_TIMEOUT_SECS60Default bash command timeout
DEXT_BASH_PATHbashExplicit Bash executable. On Windows, the default resolver skips Windows/WSL app aliases and selects a real bash.exe from PATH.
DEXT_ALLOW_BREAK_SYSTEM_PACKAGESfalseAllow the otherwise-blocked pip --break-system-packages flag
DEXT_EXTERNAL_TIMEOUT_SECS60Default timeout for external tools
DEXT_HOOK_TIMEOUT_SECS60Default timeout for hook commands
DEXT_TRUSTfalseExplicit alias for approval always when true; false values leave the default always unchanged.
DEXT_HTTP_ALLOW_LOOPBACKfalseBuilt-in HTTP tool only: allow loopback destinations. Unspecified/current-network, multicast, and broadcast destinations remain blocked.
DEXT_HTTP_ALLOW_PRIVATEfalseBuilt-in HTTP tool only: allow private, CGNAT, and IPv6 unique-local destinations.
DEXT_HTTP_ALLOW_LINK_LOCALfalseBuilt-in HTTP tool only: allow link-local and cloud-metadata destinations.
DEXT_NO_TUIfalseDisable inline TUI even when a terminal is available
DEXT_THEMEautoTUI contrast palette: light or dark; auto converts a COLORFGBG 16/256-color background index to luminance and otherwise uses dark.
DEXT_SYSTEMBuilt-in promptOverride the base system prompt
DEXT_SANDBOX.Override sandbox root path
DEXT_HOOKS_FILEhooks.jsonOverride project hook configuration file
DEXT_SUDO_ASKPASSCustom askpass helper for sudo
DEXT_SHELVES_DIRAdditional shelf directories
DEXT_SESSION_TAGPIDSession tag for checkpoint ref names
ANTHROPIC_API_KEYAnthropic API key
OPENAI_API_KEYOpenAI API key
KIMI_API_KEYKimi Code plan API key; distinct from MOONSHOT_API_KEY
ZAI_API_KEYZAI GLM API key
CHATGPT_ACCESS_TOKENChatGPT/Codex access token override
DEEPSEEK_API_KEYDeepSeek API key

Provider Details

GLM (ZAI)

  • Default built-in provider. Uses the Anthropic-compatible API at api.z.ai/api/anthropic
  • Catalog models: glm-5.3-flash, glm-5.3-flash[1m], glm-5.2[1m] (default), glm-5.2, glm-5.1, glm-5.0, and glm-4.6. Both Flash spellings declare a 1M-token context, 131,072-token output, model-specific image-input capability, stable list pricing ($0.15 input, $0.03 cached input, $0.50 output per MTok; cache storage is currently free), and native low/high/max effort. Flash always sends enabled thinking: Dext Off/Minimal/Low map to low, Medium/High to high, and XHigh/Max to max; compaction summaries use enabled low-effort thinking because Flash rejects thinking-disabled requests. The 5.2 variants also declare 1M context; the provider fallback is 200K. Image-input metadata describes the provider model capability; Dext's current persisted conversation blocks remain text/tool based.
  • Auth: API key via ZAI_API_KEY or dext auth login glm, then paste at Dext's prompt

ChatGPT / Codex

  • Uses the OpenAI OAuth flow with a local callback server on port 1455
  • Catalog models include GPT-5.6 Sol/Terra/Luna, gpt-5.4 and 5.4-mini, gpt-5.5, Codex variants, gpt-5/mini, gpt-4.1, gpt-4o/mini, o3/o3-mini, and o4-mini.
  • GPT-5.6 variants declare 1.05M context and 128K output metadata. This OAuth backend accepts reasoning effort through xhigh; Dext max maps to xhigh, and selected Standard/Pro mode is not sent. Auth: dext auth login chatgpt or CHATGPT_ACCESS_TOKEN.

OpenAI API

  • Standard OpenAI Platform API using API-key auth. The built-in official endpoint routes GPT-5.6 through /v1/responses; other models retain Chat Completions.
  • Catalog models include the official gpt-5.6 Sol id plus Sol/Terra/Luna variants, gpt-5/mini, gpt-4.1/mini, gpt-4o/mini, o3/o3-mini, and o4-mini. GPT-5.6 exposes native minimal through max effort plus independent Standard/Pro mode; model-specific metadata overrides the 400K provider fallback.
  • Auth: API key via OPENAI_API_KEY or dext auth login openai, then paste at Dext's prompt

Anthropic

  • The catalog includes current Sonnet, Opus, Fable, and Haiku model variants. Adaptive-capable public models (Sonnet 4.6, Sonnet 5, Opus 4.6/4.7/4.8, Opus 5, and Fable 5) send adaptive thinking plus the selected output effort without a thinking.display member, allowing Anthropic to return thinking deltas instead of requesting an omitted display. xhigh effort maps natively on Sonnet 5, Opus 4.7/4.8, Opus 5, and Fable 5; max additionally covers Sonnet 4.6 and Opus 4.6; unsupported extended levels downgrade to high. Built-in pricing follows the published per-model rates (Sonnet 5 $2/$10, Opus 4.5–4.8 and Opus 5 $5/$25, Fable 5 $10/$50, Opus 4.1-and-earlier $15/$75 per MTok input/output), which Anthropic applies at standard per-token rates across the full context window. The TUI presents thinking only while verbose display is enabled; stream-json emits thinking events, while console text and final JSON omit thinking content.
  • Context fallback: 200K tokens; Sonnet 5, Opus 5, and Fable 5 declare 1M-token windows. dext auth login anthropic or /login anthropic starts the unofficial Claude Pro/Max subscription OAuth flow; re-run with web to replace an existing OAuth credential. ANTHROPIC_API_KEY remains the standard Anthropic Console API-key path.
  • Subscription OAuth uses Dext-native request construction with a Claude Code 2.1.224-compatible billing/version fingerprint, seeded XXH64 body checksum, Agent SDK system block, OAuth beta/header set, and per-session/per-request UUIDs. Optional identity metadata is read in memory from ${CLAUDE_CONFIG_DIR:-$HOME}/.claude.json only after bounded regular non-symlink validation; identifiers are not copied, printed, or persisted by Dext.
  • This compatibility path is unofficial, may be restricted by provider terms, and is version-specific. It fails closed during request construction and is isolated from API keys, non-official endpoints, GLM, Kimi, and custom Anthropic-compatible profiles.

Kimi Code

  • Uses Anthropic Messages semantics at api.kimi.com/coding; default model K3 declares 1,048,576-token context, 131,072-token output metadata, and adaptive max thinking.
  • Auth: Kimi Code plan API key via KIMI_API_KEY or dext auth login kimi, then paste at Dext's prompt. This key is distinct from MOONSHOT_API_KEY.

DeepSeek

  • OpenAI-compatible API
  • Models: deepseek-chat, deepseek-reasoner — Context: 128K tokens — Auth: DEEPSEEK_API_KEY

Local (llama.cpp)

  • OpenAI-compatible local server at 127.0.0.1:8080 — No API key required — accepts the configured server model alias; the built-in Qwen3.8 shorthand selects qwen3.8-27b-ud-q5_k_xl while requests retain that exact server id. Local providers default to frugal context and probe llama.cpp for the live runtime context window without model-specific built-in context values. If the local server is offline, discovery falls back cleanly and the later provider request reports the ordinary connection error.
  • For reasoning-capable local profiles, Dext consumes streamed choices[].delta.reasoning_content as raw local thinking up to a 4 MiB aggregate stream bound, persists it as thinking blocks, replays only current-turn reasoning across llama.cpp tool-result rounds, and includes that active replay in context-pressure compaction while excluding prior-turn thinking. Local behavior requires the built-in local identity or a parsed URL host of localhost or a loopback IP; path/query substrings do not classify a cloud endpoint as local. Verbose TUI and stream-json expose it; console text and final JSON omit it. Local requests explicitly set chat_template_kwargs.enable_thinking: Off sends false, every other effort sends true, and declared reasoning_effort levels remain in use. These fields and parsing are local-only; cloud Chat Completions behavior is unchanged. Current llama.cpp servers should use --reasoning-preserve when their template supports reasoning replay.

Configuration Files

Provider catalogs and auth stores are read through bounded non-symlink regular-file checks. On Unix, foreign ownership is rejected; group/world-writable provider catalogs are rejected; owner-owned auth files with loose mode are repaired to 0600 before parsing. dext doctor uses bounded, no-follow, inode-stable inspection and reports the same ownership/mode policy without repairing files.

providers.json (~/.dext/providers.json)

Provider catalog with merged built-in + custom profiles. Editable via /provider and /model commands. Auto-normalized on load. The built-in local profile accepts Qwen3.8 as a case-insensitive alias for qwen3.8-27b-ud-q5_k_xl; both /model Qwen3.8 and /model local/Qwen3.8 resolve from a clean catalog while requests retain the exact server id.

Catalog v2 profiles may set request_contract to anthropic-messages, openai-chat-completions, openai-responses, or chatgpt-responses. Optional model_aliases, model_defaults, and model_specs provide canonical model ids, limits, effort levels, reasoning modes, capabilities, and pricing. Explicit per-model metadata wins; a selected effort uses an exact advertised level when available and otherwise clamps to the nearest supported level. Responses main turns and summaries both use this resolved effort metadata; Off sends none only when advertised, otherwise the reasoning object is omitted. Chat Completions main turns resolve reasoning_effort the same way: declared per-model effort levels win before the legacy model-name clamp, so servers that reject unadvertised levels (for example llama.cpp templates accepting only xhigh/medium/low) receive an advertised value. Context hints in model names such as -128k and [1m] win over provider-wide context defaults. Built-in metadata only fills omissions, and v1 profile fields remain accepted. The built-in official OpenAI profile selects openai-responses automatically only for GPT-5.6 at api.openai.com; custom profiles may select it explicitly without inheriting GPT-5.6-only Standard/Pro behavior. DEXT_PROMPT_CACHE=on|off overrides catalog prompt-cache capabilities for Anthropic-style requests; auto mode uses catalog metadata.

auth.json (~/.dext/auth.json)

API-key entries may reference a parent environment variable by name or intentionally run a !command through bash -lc. Treat write access to this store as code-execution authority.

Stored credentials per provider. Set to mode 0600 on Unix. Supports API-key and OAuth credential types, and retains the type through request construction so Anthropic subscription tokens cannot be confused with Console API keys. Credential-shaped parent environment variables are removed from agent-run subprocesses by default. DEXT_INHERIT_TOOL_CREDENTIALS=1 opts in only trusted model-invoked bash/external tools; hooks and Dext-owned subprocesses remain scrubbed.

.env (~/.dext/.env or $DEXT_HOME/.env)

Optional user-owned Dext settings. Project .env files and parent directories are never auto-loaded, so repository content cannot silently alter runtime policy.

DEXT.md (sandbox ancestry)

Tracked machine-facing project guidance auto-injected into the system prompt. Dext scans the sandbox root and ancestors, labels it as project-controlled guidance, and does not modify the file automatically.

recall.md (optional sandbox ancestry)

Git-ignored agent working memory, auto-injected into the system prompt when present. Whether the agent maintains it is project policy carried by DEXT.md, not an unconditional built-in action. Native write_file, edit_file, and multi_edit mutations whose target filename is recall.md (ASCII case-insensitive) are rejected when the resulting file would exceed 4 KiB; injected recall payloads also share one aggregate 4 KiB budget across sandbox ancestry, so external or shell-written files cannot bypass the context bound. Under the active privacy mode (enabled by default), those native mutation payloads are privacy-redacted before preview, approval, journaling, assistant-history persistence, and disk write, so reviewed, durable, and later model-visible forms agree and secret-shaped content the model saw cannot persist through a native recall mutation. Recall content is redacted again before prompt injection to cover external writers; /privacy off disables these scrubs along with all other redaction. Raw source-file hashes remain provenance evidence. Writes retain normal approval, sandbox, mutation-preview, and checkpoint controls.

hooks.json (project root or DEXT_HOOKS_FILE)

Project-root hooks configuration loaded from hooks.json by default, or from DEXT_HOOKS_FILE when set. This is not the same as pack phooks.json. Hook execution still requires its own approval. Approved pre_tool and post_tool hooks receive privacy-redacted DEXT_TOOL_INPUT; approved post_tool hooks also receive privacy-redacted DEXT_TOOL_RESULT, not raw input or output.