Skip to content

feat(code): warn before expensive cold-cache turns - #5439

Merged
Mason Daugherty (mdrxy) merged 26 commits into
mainfrom
mdrxy/code/cold-cache-warning
Aug 18, 2026
Merged

feat(code): warn before expensive cold-cache turns#5439
Mason Daugherty (mdrxy) merged 26 commits into
mainfrom
mdrxy/code/cold-cache-warning

Conversation

@mdrxy

@mdrxy Mason Daugherty (mdrxy) commented Aug 12, 2026

Copy link
Copy Markdown
Member

Docs

When an interactive chat message reaches the front of the queue, dcode now warns before sending it to the model if its prompt cache may need re-warming, showing the estimated cold-input cost and warm-cache delta.

flowchart TD
    A["Queued message reaches front of queue"] --> B{"Qualifying message?"}
    B -- "No (e.g. using a slash command, ACP mode)" --> S["Send the message"]
    B -- "Yes - sent inside the interactive TUI and not a slash command" --> C["Load checkpointed last request time + model identity"]
    C -- "Missing or malformed:\nlog a warning and treat as cold (see Fig. 1 for specifics)" --> D
    C -- "Loaded" --> D{"Known cache policy for current model + endpoint?"}
    D -- "No (e.g. custom endpoint or other provider)" --> S
    D -- Yes --> E{"Idle past window, age unknown, or cache-affecting params changed?"}
    E -- "No: within window" --> S
    E -- Yes --> E2{"Context ≥ minimum cacheable tokens?"}
    E2 -- "No: tiny context" --> S
    E2 -- Yes --> F{"Re-warm price delta ≥ threshold?"}
    F -- "No: below threshold or unpriceable" --> S
    F -- Yes --> M["Show blocking warning modal"]
    M -- "Keep draft (Esc)" --> R["Restore draft to chat input"]
    M -- "Cannot be shown (render failure, or another prompt owns the modal slot)" --> R
    M -- "Send anyway" --> S
    M -- "Send + don't warn again this session" --> SS["Mute until restart (in memory)"] --> S
    M -- "Send + don't warn again ever" --> SP["Persist to warnings.suppress in config.toml"] --> S
Loading
Fig. 1 — the "treat as cold" fallback

The main flow compares the checkpointed identity (what last warmed the cache) against the current model (what's about to run). Policy and cost always come from the current model, which is known from live config — so a missing checkpointed identity doesn't need its own policy; it just forces the comparison to "changed":

flowchart TD
    subgraph P["Checkpointed identity (written by middleware after each completed model call)"]
        P1 -- "Request time bad, absent, or in the future" --> P2["Log, discard time:\nthe age is unknown, not zero"]
        P1 -- "Model spec bad" --> P3["Log warning, discard spec"]
    end
    subgraph Q["Current identity (from live config)"]
        Q1["Effective model spec\n(always resolvable)"] --> Q2["Resolve policy + price against this model"]
    end
    P2 -- "No age to compare against a window" --> OUT2["Continue down the cold-cache path:\nreason = age_unknown"]
    P3 -- "Discarded spec reads as the model having changed in last vs. current" --> OUT["Continue down the cold-cache path:\nidentity_changed = true"]
    Q2 --> OUT
    Q2 --> OUT2
Loading

The three causes are kept distinct because each gets its own modal copy, and reporting one as another states something untrue about the user's configuration. In particular an unknown age is never reported as a model change, which would claim a change that never happened.

The retention windows come from documented provider policies:

Provider Condition Retention window Warning wording
Anthropic any 5m "expired"
OpenAI prompt_cache_retention: in_memory 1h "expired"
OpenAI prompt_cache_retention: 24h 24h "expired"
OpenAI GPT-5.6 or newer, no explicit retention 30m minimum "may be cold" (provider may retain longer)

The window is either a documented maximum or a documented minimum, and the wording follows from which. Anthropic's TTL and OpenAI's prompt_cache_retention ceilings are maximums, so once the window passes the entry is gone ("expired"). GPT-5.6+'s 30 minutes is a guaranteed minimum that the provider may exceed, so past it the cache can only be called "may be cold".

The two OpenAI knobs are independent, which is why the table is ordered as it is: prompt_cache_retention states a maximum lifetime while the GPT-5.6+ guarantee states a minimum one, so an explicitly configured retention is the later and firmer bound and takes precedence on those models too. Warning a user who asked for 24h at the 30-minute mark would contradict their own configuration.

Anthropic is always 5m regardless of a configured cache_control.ttl. AnthropicPromptCachingMiddleware runs inside ConfigurableModelMiddleware and rewrites model_settings["cache_control"] with its own TTL (5m, which this stack never overrides), so a user-supplied 1h never reaches the API. Treating it as an hour would suppress the warning for 55 minutes of a cache that died at five.

The warning combines three sources of information:

  1. Provider retention policies (from the table above) — how long each provider keeps cache entries
  2. Checkpointed timing — when you last sent a request to this model
  3. Pricing overrides — your configured cost per token

If the estimated re-warm cost reaches your threshold, a modal appears before sending with four keyboard-driven choices. Cost is estimated by pricing two synthetic usage payloads — one billed as a full cache read, one as a cold request — through the ordinary cost-tracking path, so the pricing catalog stays the single source of truth. Both figures are upper bounds and are rounded upward so the displayed estimate never reads lower than the modelled spend.

How a miss is priced follows the model: Anthropic carries a write premium over plain input, GPT-5.6+ bills a miss as a cache write, and OpenAI before 5.6 charges no write surcharge at all, so those misses price at the plain input rate. Tagging a pre-5.6 miss as a write would apply a premium the provider never charges and overstate both the displayed cost and the threshold comparison.

Screenshot

The model identity check looks at the model name plus the invocation parameters that actually select or invalidate a cache entry — prompt_cache_retention, prompt_cache_key, prompt_cache_options, and cache_control. A change to any of them invalidates the prefix, so it warns regardless of how recent the last turn was. Unrelated knobs are deliberately excluded: /effort rewrites reasoning_effort wholesale, and temperature or max_tokens are just as inert for caching, so comparing whole parameter maps would open the modal asserting that the cached prefix "cannot be reused" when nothing about the prefix moved.

Anything that prevents the confirmation from being shown keeps the draft rather than sending: if the modal fails to render, or another prompt already owns the modal slot, the message is not dispatched and the text is restored to the chat input. A warning that cannot be presented must not become an implicit authorization to spend. Where a draft genuinely cannot be put back — the input already holds different text, or the active thread changed while the modal was open — the notification says the message was not sent and points at history recall rather than implying it was merely deferred.

Only interactive chat submissions are checked — prompts sent programmatically (e.g. via ACP from an editor client), slash commands, requests to custom endpoints, and requests without pricing information all skip this warning entirely. The default threshold is $0.50; adjust it with warnings.cold_cache_min_delta_usd.

The request time is kept fresh from two directions. A completed turn reads it back from the checkpoint; a turn that reached the model but ended without a readable checkpoint — an interrupt, say — stamps it locally instead. Without that second path the field would never advance past its initial value, and every send after the first would open the modal reporting no record of a turn that plainly happened.

If the checkpointed request time or model identity cannot be loaded (for example, a checkpoint written by a newer version, or a malformed value), dcode logs a warning naming the affected channel and proceeds down the cold-cache path rather than silently assuming the cache is warm: an unusable request time makes the age unknown, and a missing model identity is treated as a model change. A request time in the future — a corrected clock, or a thread synced from a skewed machine — is treated as unknown age too, since clamping it to zero would place it inside every retention window and suppress the warning outright.

Skipping the warning means sending at full cold-cache price, so the paths that skip it on failure are made visible rather than left to the log alone: if the evaluation itself fails, the session says once that cost checks are unavailable; an unparseable configured base_url warns where it is parsed, since it silently disables warnings for that provider; and a pricing catalog that produces a non-finite figure is reported as a data defect rather than reading as "no price published". A failed persistent suppression names its cause — a malformed [warnings] table needs one line of TOML, not the chmod that a generic "check file permissions" would send the user to.

Made by Open SWE

References

@github-actions github-actions Bot added dcode Related to `deepagents-code` feature New feature/enhancement or request for one internal User is a member of the `langchain-ai` GitHub organization size: XL 1000+ LOC labels Aug 12, 2026
@mdrxy
Mason Daugherty (mdrxy) marked this pull request as ready for review August 12, 2026 01:27

@open-swe open-swe Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open SWE Review found 1 potential issue.

Open in WebView Open SWE trace

Comment thread libs/code/deepagents_code/configurable_model.py Outdated

@open-swe open-swe Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open SWE Review found 1 potential issue.

Open in WebView Open SWE trace

Comment thread libs/code/deepagents_code/tui/modals/cold_cache.py

@open-swe open-swe Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open SWE Review found 1 potential issue.

Open in WebView Open SWE trace

Comment thread libs/code/deepagents_code/cold_cache.py Outdated
Comment thread libs/code/deepagents_code/tui/widgets/startup_tip.py Outdated

@open-swe open-swe Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open SWE Review found 1 potential issue.

Open in WebView Open SWE trace

Comment thread libs/code/deepagents_code/cold_cache.py Outdated

@open-swe open-swe Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open SWE Review found 2 potential issues.

Open in WebView Open SWE trace

Comment thread libs/code/deepagents_code/app.py
Comment thread libs/code/deepagents_code/cold_cache.py
@mdrxy
Mason Daugherty (mdrxy) force-pushed the mdrxy/code/cold-cache-warning branch from 3b6db23 to 6a95f6b Compare August 17, 2026 21:42
Mason Daugherty (mdrxy) and others added 12 commits August 17, 2026 21:33
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
When a runtime model override fails with ModelConfigError, the middleware
falls back to the original model but the checkpoint update associated the
call with the rejected override spec, letting the TUI resolve cache policy
and pricing for the wrong model on the next turn. Persist the resolved
spec — the model that actually served the request — instead.
Say the cached prefix "has likely expired" since providers may retain
the cache past the documented TTL, drop the output-cost disclaimer and
the /clear hint, and rename the Cancel button to "Cancel & keep draft"
to signal the message text is restored to the prompt input.
Warn when a resumed thread's checkpointed `_last_model_request_at` fails
to parse or no usable `_last_cache_model_spec`/`_model_spec` identity is
found, since both conditions silently changed cold-cache warning behavior
(warnings disabled until the next successful request, or every send
treated as a model change). The in-session re-check in
`_cold_cache_warning_for` logs at debug to avoid repeating the resume-time
warning.
The warning already keys off the provider-documented retention window;
adding an undocumented 5s buffer let turns slip through just past the
window without a warning. Compare against window_seconds directly.
At $0.10 the modal fired for a Sonnet conversation of only ~29k context
tokens after each 5-minute cache expiry — an ordinary session — so users
saw it after every short break and learned to dismiss it reflexively.
$0.50 moves the trip point to ~145k tokens on Sonnet, closer to a
material surprise charge, while still catching Opus-class re-warms well
below that.
- Apply Anthropic's per-model cache minimums (512/2048/4096 tokens)
  instead of a flat 1024, which suppressed warnings on Opus 5-class
  models and fired false warnings on Haiku 4.5 / Opus 4.5+.
- Drop "3600s" from the Anthropic ttl check; the SDK types ttl as
  Literal["5m", "1h"], so the alias could never match a real value.
- Flip OpenAI confidence labels: the GPT-5.6+ 30-minute window is the
  documented guaranteed minimum ("expired" past it), while in_memory
  and 24h retentions are documented maximums ("may_be_cold").
…ache modal

The cold-cache warning modal now offers four choices instead of a bare
send/keep-draft prompt: send once, send and mute the warning for the
rest of the session (in-memory flag, bypassed by the debug env var so
the modal stays reachable while developing), send and persist the
suppression to [warnings].suppress in config.toml (re-enableable from
/notifications), or keep the draft. Rows are keyboard-only to avoid an
accidental click authorizing spend, matching the update-available
modal.

Also harden suppress_warning/unsuppress_warning against a hand-edited
non-table [warnings] section (e.g. warnings = []): they now log and
return False instead of raising AttributeError, which in the modal's
detached continuation would have silently dropped the submitted
message. The persistence call is additionally wrapped in a defensive
try/except so a failure surfaces a toast and the send still proceeds.
Mason Daugherty (mdrxy) and others added 13 commits August 17, 2026 21:33
OpenAI documents the 30-minute GPT-5.6+ lifetime as a guaranteed minimum
that may be retained longer, so past the window the prefix is only
possibly cold; "expired" would have shown "has likely expired" copy.
The app's priority shift+tab -> toggle_auto_approve binding consumed the
key before ColdCacheWarningScreen's own shift+tab binding could fire,
silently swallowing reverse navigation. Replace the hardcoded screen
tuple in action_toggle_auto_approve with a runtime-checkable
_SupportsReverseNav protocol so any modal implementing action_move_up is
routed automatically, and add an app-level keypress regression test —
bare-App modal tests cannot see the interception. Also clarify the
cancel copy ("Don't send (keep draft)" / "Esc cancel") and document
the trap in libs/code/AGENTS.md.
Price an OpenAI cache miss at the plain input rate. The `generic` write
bucket forwarded a `cache_write` detail key, which the catalog bills at a
25% write premium OpenAI does not charge, overstating both the displayed
cost and the threshold comparison (gpt-5.6-terra at 100k tokens showed
$0.25 against a true $0.20).

Stop honoring a user-supplied `cache_control.ttl`.
`AnthropicPromptCachingMiddleware` runs inside `ConfigurableModelMiddleware`
and overwrites `model_settings["cache_control"]` with its own 5m TTL, so a
configured `1h` never reached the wire while the policy granted an hour of
retention -- suppressing the warning for 55 minutes of a dead cache.

Label OpenAI's `in_memory` and `24h` windows `expired` rather than
`may_be_cold`. Both are documented maximums, so once the window passes the
entry is gone; only the GPT-5.6+ guaranteed minimum can outlive its window.
This also stops the modal from describing a maximum as a "minimum
cache-retention window".

Fail closed where the confirmation could not be made:

- A modal that fails to render now cancels and restores the draft instead
  of treating the failure as authorization to spend.
- Losing the modal slot keeps the draft, so the "answer the other prompt
  first" toast is no longer contradicted by a silent send.
- `_checkpoint_command` writes the request time only alongside a known
  model spec, making the "timestamp without identity" state -- which read
  back as a permanent model change -- unreachable at its source.
- The resume path passes raw checkpoint values to
  `_sync_cache_state_from_state`, so malformed values log the same discard
  warning on every entry point instead of being silently pre-filtered.
- `DEBUG_COLD_CACHE` overrides persisted suppression, so it is a true
  force rather than a no-op for anyone who chose "don't warn again ever".

`PromptCachePolicy` is keyword-only: `window_seconds` and `minimum_tokens`
are adjacent bare ints, and transposing them positionally built a
plausible-looking policy that silently mispriced and mis-gated.

Add coverage for the gates that had none: identity change by spec and by
params inside the window, below-threshold rejection, `threshold == 0`,
slash-command skip, the no-clobber guard on partial state reads, blank
checkpointed specs, request-start-not-completion timestamping, and the
click-swallow invariant the modal documented but never verified.
Signed-off-by: Mason Daugherty <github@mdrxy.com>
`test_cleanup_refreshes_git_branch` drove four `git` subprocesses through
`asyncio.to_thread` to stand up a repo on a `feature` branch. Once the rest
of `test_app.py` had run ahead of it, `subprocess.run` intermittently sat in
`communicate()` waiting on the captured pipes until pytest-timeout killed
the test at 30s -- passing in isolation and failing in full-file runs, with
the order dependence making it look like a product bug.

The test is about `_cleanup_shell_task` re-resolving the branch, and
`_refresh_git_branch` reads the common layout straight from `.git/HEAD`, so
the subprocesses were scaffolding rather than coverage. Write `.git/HEAD`
directly instead, matching `test_refresh_git_branch_reads_gitdir_pointer`
in the same class.

Verified with three randomized full-file runs (1499 passed each).
GPT-5.6 and later bill cache writes at 1.25x the uncached input rate,
and genai-prices models it. Sending the cold usage payload with no
cache-write detail priced a GPT-5.6 miss at plain input, so a turn
could skip the cold-cache warning despite exceeding the threshold.
Add a generic_write bucket that forwards the write detail for GPT-5.6+
policies; pre-5.6 OpenAI models keep the plain generic bucket, which
carries no write detail and prices a miss at the input rate.
- Remove the bold styling from the SEND row so the selection highlight is
  the only visual emphasis; the bold read as a confusing default marker.
- Reword "Send and don't warn again ever" to "Send and never warn again"
  for clearer contrast with the session-scoped option above it.
The modal's cost figures are worst-case estimates priced from synthetic
usage payloads -- the cache may be partially warm and the actual spend
lower -- so point values like "$0.62" overstate what is known.

- Add `format_cost_estimate` next to `format_cost` (which stays exact
  for recorded session spend): two significant figures with a `~`
  prefix, halves rounded away from zero via `Decimal` so the bound
  never shrinks on a tie, same $0.00 / <$0.01 edge conventions.
- Update the modal body to "may cost up to {cold}" with both figures
  rounded; under the `may_be_cold` policy the cost sentence gains an
  explicit "If the cache has expired" conditional since the provider
  may have retained the cache.
- The threshold comparison keeps using the unrounded estimate; the
  rounding is display-only.
Bound the confirmation modal with the standard watchdog. This continuation
holds the single `_schedule_off_message_pump` slot, so an await that never
resolved left `_modal_command_running()` true forever and stopped the pending
queue from draining for the rest of the session, with nothing logged.

Guard the post-decision dispatch. The continuation runs outside the caller's
`try/except`, so a send failure after the user authorized the spend reached
only `_log_task_exception` -- the message vanished with no transcript error.

Treat an unusable request time as unknown age rather than a warm cache. A
thread checkpointed before cold-cache tracking carries no
`_last_model_request_at`, which silently skipped the warning on the first send
after resuming a long-idle thread -- the scenario the feature exists for.
`identity_changed: bool` becomes `reason: ColdCacheReason` (`idle` /
`identity_changed` / `age_unknown`) with its own modal copy, so unknown age is
never reported as a model change that did not happen. `age_seconds` is now
optional, making "no age" unrepresentable rather than implied.

Report an unpriceable warm/cold delta as missing catalog data instead of
letting it read as "below threshold", and log every remaining skip: each one is
a decision to spend without asking.

Normalize the provider before `get_base_url`, which does exact lowercase-key
lookups -- `Anthropic:claude-opus-5` reported no custom endpoint and priced a
gateway user at official-API rates.

Fix the Anthropic minimums table: Haiku 3.5 ships as `claude-3-5-haiku-*`, so
the `claude-haiku-3-5` prefix never matched and fell through to the 1,024
default instead of 2,048.

Keep shift+tab reverse-nav in the model selector -- `_SupportsReverseNav`
matches on method presence, so enrollment is opt-out -- and correct the comment
that still named it as a screen reaching the no-op branch.

Enforce `RewarmEstimate`'s documented invariants at construction, make it
keyword-only, move the debug stand-in policy beside the constants it derives
from, fold `_ThreadHistoryPayload`'s presence flag into a single nullable
`cache_state`, and add `assert_never` exhaustiveness to both `Literal`
branches.

Correct docstrings this feature invalidated: `cache_write` is load-bearing for
GPT-5.6+ pricing rather than a defensive spelling, `generic_write` is selected
by model-name version rather than a catalog check, the debug flag fires on
every send rather than the next one, and `format_cost_estimate` keeps
cent-level precision below a dime. Align the hook's cost formatting with the
modal's so one estimate does not render two ways.
The warning could open and then assert something untrue about the user's
configuration, which trains people into the permanent suppression:

- An interrupted turn never refreshed the in-memory request time, because
  only a completed turn reads the checkpoint back. The next send then
  reported "no record of when this thread last reached the model" seconds
  after a turn that plainly reached it. A turn that made at least one model
  request now stamps the identity locally.
- `/effort` (and `temperature`, `max_tokens`) compared as a cache change,
  because the identity check compared whole `model_params` maps. Only
  `CACHE_IDENTITY_PARAM_KEYS` participate now. A failed model override also
  wrote `_model_params: None` while the app kept its override, pinning the
  comparison to a permanent mismatch; that path leaves the checkpointed
  value alone instead.
- GPT-5.6+ ignored an explicit `prompt_cache_retention`, so a user
  configured for `24h` was warned at 31 minutes and told they had exceeded
  a 30-minute window they had widened. Retention is a documented maximum
  and now outranks the 30-minute guaranteed minimum.
- `age_unknown` copy stated a retention ceiling for a provider whose window
  is a floor, inverting the distinction `CacheConfidence` exists to keep.
- A request time in the future clamped to zero, reading as maximally warm
  and suppressing the warning outright; it is treated as unknown age.

`ColdCacheWarning` moves to `cold_cache.py` and enforces its documented
`age_seconds`/`reason` pairing, and the modal takes the object whole rather
than five loose parameters with a defaulted `reason` -- that split let an
`age_unknown` warning render "idle for 0m".

Failures that send at full price are no longer log-only: evaluation failure
notifies once per session, an unparseable `base_url` and a non-finite price
warn where they happen, and a failed persistent suppression names its cause
instead of blaming file permissions for a malformed `[warnings]` table.

Also enforces `RewarmEstimate`'s documented finiteness (`NaN` satisfied
neither existing guard), names the `cold-cache` suppression key once,
collapses the duplicated threshold loading, and corrects the discard
warning that claimed warnings would stay off when they now fire.
`AnthropicPromptCachingMiddleware` overwrites `model_settings["cache_control"]`
with its own TTL on every Anthropic request, so a user-supplied value never
reaches the wire. Keeping the key in `CACHE_IDENTITY_PARAM_KEYS` made changing
an ignored setting read as `identity_changed`, popping the "prefix cannot be
reused" modal on a false premise.
@mdrxy
Mason Daugherty (mdrxy) force-pushed the mdrxy/code/cold-cache-warning branch from 6a95f6b to a37a4c5 Compare August 18, 2026 01:34
…-warning

# Conflicts:
#	libs/code/tests/unit_tests/test_app.py
@mdrxy
Mason Daugherty (mdrxy) merged commit 8d2298f into main Aug 18, 2026
54 of 55 checks passed
@mdrxy
Mason Daugherty (mdrxy) deleted the mdrxy/code/cold-cache-warning branch August 18, 2026 03:41
Mason Daugherty (mdrxy) added a commit that referenced this pull request Aug 18, 2026
)

Depends on #5439

[Docs](langchain-ai/docs#5524)

Some users don't talk to model providers directly — they go through a
gateway or corporate proxy (for example, LangSmith's gateway, or a
company-internal relay). The cold-cache warning from #5439 stays silent
for all of them, because it only trusts the provider's official API
address. This PR adds a setting to say "my endpoint plays by the
provider's rules," turning the warnings back on:

```toml
[warnings]
trusted_cache_endpoints = ["smith.langchain.com"]
```

One entry covers every provider routed through that endpoint — no need
to repeat it per provider.

---

**Why trust is per-endpoint, not global.** The warning's dollar
estimates are only accurate if the thing sitting between you and the
provider passes your cache settings through untouched and keeps cached
data for as long as the provider documents. A proxy that quietly drops
those settings would make the warning's numbers fiction. So instead of
one blanket "trust everything" switch, you name the specific endpoints
you've verified, and everything else stays silent.

**One case stays silent even on a trusted endpoint.** LangSmith's
gateway can translate between API formats — for example, you can send an
OpenAI-shaped request and have it answered by an Anthropic model. During
that translation, your cache settings get rewritten to a generic
5-minute cache (or dropped entirely), so the provider's documented cache
behavior no longer applies and any estimate would be a guess. When dcode
can see a request will take one of these translated routes (the model
name carries a cross-provider prefix like
`openai:anthropic/claude-...`), it skips the warning rather than show
numbers built on wrong assumptions. Requests that stay in their own
format — the normal case — are forwarded by the gateway byte-for-byte
(verified against the gateway source), so their warnings remain exactly
as accurate as going direct.

<details><summary>Test plan</summary>

- Unit tests cover trusted-endpoint policy resolution, gateway
same-format vs cross-format routes, lookalike-host rejection
(`notsmith.langchain.com`, `smith.langchain.com.evil.example`), and
malformed config tolerance.
- App-level tests confirm a trusted gateway endpoint allows the warning
through, and a cross-format gateway route suppresses it even when
trusted.
- Lint, type check, and the cold-cache/manifest suites pass.

</details>
Mason Daugherty (mdrxy) added a commit to langchain-ai/docs that referenced this pull request Aug 18, 2026
…#5524)

Draft documentation for two `deepagents-code` features:

-
[langchain-ai/deepagents#5439](langchain-ai/deepagents#5439)
— warn before expensive cold-cache turns
-
[langchain-ai/deepagents#5462](langchain-ai/deepagents#5462)
— trust user-declared endpoints for cold-cache policies

Generated with AI assistance (Deep Agents Code).
Mason Daugherty (mdrxy) pushed a commit that referenced this pull request Aug 18, 2026
> [!CAUTION]
> Merging this PR will automatically publish to **PyPI** and create a
**GitHub release**.

For the full release process, see
[`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md).

---

_Release notes preview: keep this section in sync with the package
`CHANGELOG.md`. Publish reads the merged CHANGELOG via `release.yml`,
not this PR description — keep them aligned anyway so the PR stays an
accurate historical record for reviewers and anyone returning later._

---


##
[0.1.57](deepagents-code==0.1.56...deepagents-code==0.1.57)
(2026-08-18)

### Features

- Added warnings before expensive cold-cache turns and trust
user-declared endpoints for cold-cache policies
([#5439](#5439),
[#5462](#5462)).
- Made the chat input resizable by dragging its top border
([#5524](#5524)).
- Added a `multi_select` question type to `ask_user`
([#5097](#5097)).
- Added support for ACP approval modes
([#5394](#5394)).
- Added `DeepSeek-V4-Pro-0813` to the model picker
([#5512](#5512)).
- Show conversation turns alongside message counts
([#5571](#5571)).
- Include `TERM_PROGRAM` in the resume hint
([#5548](#5548)).

### Bug Fixes

- Report total context after `/offload`
([#5488](#5488)).
- Fixed transcript and thread restoration issues, including hydration
lag, scrolling resumed threads to the bottom, and hiding empty
previous-thread hints
([#5479](#5479),
[#5543](#5543),
[#5552](#5552)).
- Fixed Auto-mode approval handling by binding “yes” to the paired
`ask_user` question and avoiding duplicate Auto denial notices
([#5038](#5038),
[#5501](#5501)).
- Improved reload behavior by keeping the chat input responsive during
`/reload`, reporting MCP server changes, and avoiding plugin reload
prompt flashes or startup hints
([#5529](#5529),
[#5504](#5504),
[#5500](#5500),
[#5502](#5502)).
- Improved dependency update UI by preserving editable fields and hiding
dependency details after updates
([#5521](#5521),
[#5519](#5519)).
- Fixed chat UI polish issues, including detached spacer mount anchors,
the unfocused input cursor, and relative timestamp toggle display
([#5516](#5516),
[#5258](#5258),
[#5503](#5503)).
- Refresh the splash version after updates
([#5520](#5520)).

_End release notes preview._

---

> [!NOTE]
> A **community contributors** list and a **Special thanks** section
(crediting the users who filed the issues this release's PRs closed) are
appended to the GitHub release notes automatically at publish time (see
[Release
Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline),
step 3).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: langchain-oss-automated-triage[bot] <248757908+langchain-oss-automated-triage[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dcode Related to `deepagents-code` feature New feature/enhancement or request for one internal User is a member of the `langchain-ai` GitHub organization size: XL 1000+ LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant