EmbeddedSnapshot exposes the build-time snapshot.json catalog as a
read-only singleton, and Fetch hits the live models.dev API directly,
surfacing a network failure instead of silently degrading to a cache
or the embedded snapshot.
Together with the existing NewDatabaseStore these let callers build a
Store that is either provably hermetic or provably live, instead of
Store's usual best-effort network-then-cache-then-snapshot fallback.
Groundwork for #4134.
models.dev removed deepseek-chat and deepseek-reasoner from the deepseek
provider. Update all references to use deepseek-v4-pro (the current
flagship model) and refresh the embedded snapshot to match the live
catalog.
Fixes: TestParseExamples/../../examples/deepseek.yaml
gemini-3-pro-preview was removed from the live models.dev catalog,
causing TestParseExamples to fail on both Linux and Windows CI runners
with: 'model "gemini-3-pro-preview" not found in provider "google"'.
Switch the reviewer agent in examples/gopher.{yaml,hcl} to
gemini-3.1-pro-preview, its direct successor in the catalog.
Also regenerate pkg/modelsdev/snapshot.json so the baked-in fallback
snapshot stays in sync with the current live catalog.
Runs go generate ./pkg/modelsdev/... to pull the latest catalog from
models.dev. The only change to the anthropic provider is the addition of
claude-opus-5 (family claude-opus, 1M context, 128k output, $5/$25
pricing, text+image+pdf input). Five new niche providers are also added
as part of the wholesale refresh.
Adds TestEmbeddedSnapshotIncludesClaudeOpus5 to guard against a future
stale snapshot silently dropping the new model, mirroring the existing
GPT-5.6 regression test.
pkg/modelsdev, pkg/gateway and pkg/httpclient only needed
remote.NewTransport, but importing pkg/remote drags the whole OCI
registry stack (go-containerregistry, docker/cli, credential helpers)
into every consumer of pkg/model/provider, pkg/session and pkg/agent.
Move the transport (and its proxy fallback logic) to
pkg/desktop/transport, which only depends on pkg/desktop and
pkg/memoize. remote.NewTransport is kept as a deprecated forwarder so
existing integrations keep compiling and behaving identically.
pkg/model/provider now pulls 24 external modules instead of 31.
Prevent picker stacking on ctrl+r, preserve the search query across
refreshes, and avoid holding the store mutex during network I/O.
Coalesce refreshes with a dedicated refreshMu, improve error handling
for in-memory and not-modified edge cases, and add covering tests.
Assisted-By: Claude (Anthropic)
Pressing Ctrl+R in the model picker bypasses the models.dev cache TTL,
re-fetches the catalog, and reopens the picker with updated choices.
Assisted-By: docker/docker-agent
Restore the context.Context parameter on app.New (the rebase auto-merge
silently dropped it while keeping the ctx-using body) and thread the
caller's context through all call sites, including tests.
Annotate the build-time root context in the new
pkg/modelsdev/internal/gen snapshot generator (landed from main) for the
ContextConnectivity cop: it is a standalone main with no parent context.
Adds a build-time snapshot of the models.dev catalog baked into the
binary via go:embed, used as last-resort fallback when the cache and
live fetch are both unavailable. A generator (pkg/modelsdev/internal/gen)
refreshes the snapshot; task update-models and a weekly GitHub Actions
workflow keep it current.
The fallback snapshot is not memoized on transient fetch failure, so the
Store retries the fetch once the network recovers. Context windows for
Fable and Opus 4.6-4.8 now come from the embedded snapshot (always
available, even offline); the Anthropic client falls back to the flat
200k floor only when no catalogue entry exists. A warning is logged when
the embedded snapshot fails to parse so a bad build artifact is
discoverable.
The snapshot freshness check is opt-in: TestSnapshotDateParses always
runs (time-independent) while TestSnapshotDateIsFresh is skipped unless
CHECK_MODELS_SNAPSHOT_FRESHNESS is set. A 'task check-models-fresh'
target provides the CI backstop.
Assisted-By: Claude
Resolving a model for a user-defined custom provider (a name that is not a
built-in or alias, e.g. mistral_gateway) loaded the full models.dev catalog,
which performs a blocking 30s GET to https://models.dev/api.json. In
internet-restricted environments this hung the request loop (loop.go) and
session compaction on every turn, making a self-contained custom-provider
config (base_url + token_key) unusable.
modelsdev.Store gains WithKnownProvider: a provider the predicate rejects is
resolved from the cache only and never triggers an outbound fetch (a missing
cache yields an empty catalog, so the lookup returns a clean "provider not
found"). The cache-only snapshot is memoized separately from the authoritative
catalog, keeping the per-turn hot path cheap (no repeated catalog re-parse)
without ever satisfying a fetch-eligible lookup for a known provider.
The runtime's default store (lazyModelStore) and RuntimeConfig.ModelsDevStore()
are wired with provider.IsKnownProvider, so only catalog providers (built-ins
and aliases) can reach the network; custom providers resolve locally and the
agent stays usable air-gapped.
Rebase PR #2746 on top of origin/main, resolving conflicts with PR #2738.
Key changes:
- Add RuntimeConfig.ModelsDevStore() lazy accessor with sync.Once caching
- Add options.WithModelsDevStore to thread the store through provider options
- Merge modelcaps.Load and LoadFromStore into a single Load(store, modelID)
- Remove per-client modelsStore fields from all provider clients (anthropic,
openai, gemini, bedrock, dmr) — they now get the store via ModelOptions
- Convert modelsdev.NewStore from sync.OnceValues singleton to a plain
function since RuntimeConfig handles the caching
- Wire RuntimeConfig.ModelsDevStore through teamloader, cmd/root/models,
and rag/strategy/embedding
- Update detectCachingSupport (bedrock) to accept store parameter
- Rename oaistream ConvertMessagesFromStore/ConvertMultiContentFromStore
to ConvertMessages/ConvertMultiContent
- Keep c.ID() (qualified provider/model) from PR #2738 at all call sites
Scope gosec to G301 (dir perms), G302 (chmod perms), and G306 (WriteFile
perms) so future regressions of the project's 0o700/0o600 convention
for on-disk private state are caught in CI.
Real fix uncovered by the new check:
- pkg/modelsdev/store.go: tighten cache directory to 0o700 and cache
file to 0o600 to match every sibling cache (pkg/cache, pkg/content,
pkg/history, pkg/userconfig, etc.). This was an outlier.
Legitimate exceptions silenced inline with a one-line reason:
- pkg/snapshot/snapshot.go: 0o755/0o644 mirror what 'git init' itself
writes for .git and .git/info/exclude.
- pkg/toolinstall/{installer,archive}.go: installed/extracted binaries
need +x; their parent directories need traversal/exec.
- pkg/app/export/html.go: exported HTML is meant to be world-readable.
- pkg/gateway/catalog.go: MCP catalog is shared with other docker MCP
gateway processes.
- pkg/server/listen.go: unix-socket access is gated by the socket file's
own permissions, not by the parent directory.
- pkg/tools/builtin/lsp/lsp.go: file pre-exists; os.WriteFile only
applies the mode argument on creation.
- cmd/root/pull.go, cmd/root/root.go: pulled agent yaml and the empty
first-run marker file are intended to be readable.
Test files are excluded from gosec (test fixtures legitimately use
0o755 and chmod for permission-failure scenarios).
Pre-existing //nolint:gosec directives targeting G203/G204 are removed
in this commit because they are flagged as 'unused' under the narrow
scope; they will be re-added when gosec is broadened in a follow-up.
Move all the scattered name-pattern checks (Responses API selection,
o-series defaults, adaptive thinking for Opus 4.6+, Gemini 3 thinking
levels, Bedrock-Claude detection) into a single capability-named
package, and wire it through the existing provider clients.
Replace all slog.Debug/Info/Warn/Error calls with their *Context variants (slog.DebugContext, slog.InfoContext, etc.) throughout the codebase. This enables log correlation and tracing by threading context through all logging statements.
Key changes:
- Update all logging calls in runtime, agent, provider, and tool packages
- Pass context.TODO() in cases where no request context exists (e.g., config validation, debug commands)
- Update agent.Model() signature to accept context parameter for log correlation
- Maintain existing log levels and messages; only add context parameter
This change improves observability by allowing logs to be correlated with traces and requests, making debugging distributed operations easier.
Updates fetch, openapi, api tools, gateway catalog, skills cache, and models.dev store to use remote.NewTransport for Desktop proxy support when downloading external content.
Assisted-By: docker-agent
Signed-off-by: Guillaume Tardif <guillaume.tardif@gmail.com>
Make modelsdev.Store a process-wide singleton via sync.OnceValues so the
models.dev database is loaded at most once and shared across all callers
(teamloader, runtime, bedrock, embedding, config tests).
Key changes:
- NewStore() returns the same *Store instance for the entire process
- Add ETag support: store the ETag from models.dev responses and send
If-None-Match on refresh; a 304 Not Modified skips the download
- Switch from json.Decoder to ReadFile/ReadAll + json.Unmarshal to
avoid the decoder's intermediate buffering overhead
- Hoist store creation out of per-model loops in teamloader to make
the singleton intent explicit and handle errors gracefully
- Persist ETag in the cache file for reuse across process restarts
This eliminates ~50-60MB of redundant heap allocations per run (the 3MB
JSON was being parsed into ~66MB of Go objects up to 6 times).
Assisted-By: docker-agent
Replace the lazy-loaded closure-based Store with a simple struct
that holds a cache file path and loads the database on first
GetDatabase call. Pass caller-provided context to the HTTP request
in fetchFromAPI instead of using context.Background().
NewStore() remains context-free (no I/O). All Store methods
(GetDatabase, GetProvider, GetModel, ResolveModelAlias) and
ModelSupportsReasoning accept context for the network call.
Updated all callsites, using context.Background() only in TUI
callbacks where no caller context is available.
Assisted-By: cagent
This is more reliable and requires less maintenance compared to checking for a valid provider prefix intstead
Signed-off-by: Christopher Petito <chrisjpetito@gmail.com>