Help us shape the future of Go! Take the 2026 Go Developer Survey

settings

package
v0.1.1417 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 24, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Overview

Package settings keeps the few preferences the desktop app has: which palette to paint with, which language to speak, how the agents are arranged. Everything else magpie knows is derived from the agents' own files.

The file is ~/.config/magpie/settings.json; a missing file means "follow the system" for both.

Index

Constants

View Source
const (
	ImageTimeoutDefault = 5
	ImageTimeoutMost    = 60
)

ImageTimeoutDefault is the minutes an image is given to be drawn when ImageTimeout is 0, and ImageTimeoutMost the most it can be set to.

View Source
const (
	ClassifyTimeoutDefault = 8
	ClassifyTimeoutMost    = 60
)

ClassifyTimeoutDefault is the seconds a classifier is given when ClassifyTimeout is 0, and ClassifyTimeoutMost the most it can be set to: the turn waits on it before it goes anywhere, with nothing sent to its agent meanwhile.

View Source
const (
	SearchFirstModel = "model"
	SearchFirstAPI   = "api"
)

SearchFirst's values: a provider's search first, saved as "", or the search APIs first.

View Source
const AnyProvider = "*/"

AnyProvider begins a ModelPrices key that prices a model whichever provider it came through, its id lower-cased after it.

View Source
const DefaultPort = 3425

DefaultPort is the gateway's port unless Settings or MAGPIE_ADDR say another.

View Source
const WorkingWindow = 272000

WorkingWindow is the context window Codex and Claude Code are told for a model with a larger one, unless FullContext: Codex's own for OpenAI's models that can take more.

Variables

View Source
var (
	Themes     = []string{"system", "light", "dark"}
	Langs      = []string{"system", "en", "zh", "zh-TW", "ja", "de"}
	Trays      = []string{"panel", "window"}
	Currencies = []string{"usd", "cny"}
	// Warmups are CodexWarmup's and ClaudeWarmup's values, off as "".
	Warmups = []string{"", "week", "all"}
	// TrayEvery are TrayUsageEvery's values, in minutes.
	TrayEvery = []int{1, 3, 5, 10, 30}
	// UpdateEveries are UpdateEvery's values, in minutes.
	UpdateEveries = []int{30, 60, 360, 1440}
	// TextSizes are TextSize's values, in percent. None is under 100: the
	// webviews' zoom on Windows and Linux (Wails' SetZoom) goes no lower.
	TextSizes = []int{100, 110, 125, 150}
)

Themes and Langs are the accepted values, in the order the UI offers them.

View Source
var DefaultTextSize = func() int {
	if runtime.GOOS == "windows" || runtime.GOOS == "linux" {
		return 110
	}
	return 100
}

DefaultTextSize is the text size of settings that have never had one saved. Windows' and Linux's body text is 14px against magpie's 13px at 100%, a size smaller than what is around it there, so they start at 110% (#914); the Mac's is 13px, as magpie's. A size once saved, 100% too, is kept as it is. A var so tests can be any system.

Functions

func AccountDiscoveryOff added in v0.1.1176

func AccountDiscoveryOff() bool

AccountDiscoveryOff is whether magpie leaves agents' sign-ins unread (#1535): NoAccountDiscovery saved, or MAGPIE_NO_ACCOUNTS set to anything but "", "0" or "false" for this process alone. The variable only ever turns discovery off; it never turns on what the user saved off.

func AccountDiscoveryOffIn added in v0.1.1225

func AccountDiscoveryOffIn(s Settings) bool

AccountDiscoveryOffIn is AccountDiscoveryOff with the settings already read, for a caller asking it many times in one look.

func Arrange added in v0.1.60

func Arrange[T any](s Settings, items []T, id func(T) string) (shown, hidden []T)

Arrange puts items in the order the user gave the agents, those named first and the rest after in the order they came, and splits off the hidden ones, which keep that order too. id names an item's agent.

func CarryPerModel added in v0.1.496

func CarryPerModel(in, cur *Settings)

CarryPerModel puts cur's per-model maps into in's, whole as they are: the ones a page that sends only its own choices would otherwise save as nothing, and so lose. Every per-model map is carried, whichever page wrote it, so a map added to the settings later needs nothing said of it here.

func CheckModelKey added in v0.1.496

func CheckModelKey(what, key string) error

CheckModelKey is whether a key of one of the per-model maps names a model the way all of them do: "<provider id>/<model id>" — or "<provider id>/*" for all of that provider's models. A model id may have slashes of its own (vendor/model), so only the first one ends the provider's id. what names the setting being checked, for the error.

It is the one rule a model key is held to, and it lives here, beside the maps it keys. Every change that takes such a key from the user checks it here rather than spelling the shape out again, so what is written into a map and what is refused come out of the same rule — one rule, and not one each branch can drift from on its own.

func CheckModelPrice added in v0.1.504

func CheckModelPrice(key string, m ModelPrice) error

CheckModelPrice is whether a price the user gives is one magpie will bill a call at: the key names a model the way the per-model maps key one, and every part of the price is a number a vendor could charge, naming the first that is not. It is where a price is checked rather than in Save: one a hand edit left broken in the file is skipped where it is read, and must not stop an unrelated setting from being saved. That covers a part that is missing, which is what a file can be edited into: JSON has no NaN or Infinity, and Load discards the whole file rather than half of it, so neither reaches here. Both are still refused, from a price typed in or passed in.

func CheckPort added in v0.1.950

func CheckPort(p int) error

CheckPort says what is wrong with a port for the gateway: one from 1024 to 65535 (below that a system wants an administrator to listen on it), or 0 for the default.

func CheckProxy added in v0.1.418

func CheckProxy(p string) error

CheckProxy says whether p is a proxy setting magpie takes: "" (follow), "direct", or an http://, https:// or socks5:// address (host:port meaning http). The global Proxy and a provider's own are both checked with it.

func CleanAllowIPs added in v0.1.1214

func CleanAllowIPs(in []string) ([]string, error)

CleanAllowIPs checks each entry of an allow list and writes it in one form (an address alone, a range by its first address), without repeats.

func CleanOrigins added in v0.1.1095

func CleanOrigins(in []string) ([]string, error)

CleanOrigins is the web origins a user typed for CORSOrigins, as a browser's Origin header spells them: scheme://host[:port], lower-cased, with no path and the scheme's default port left out. http and https are origins, and so is a browser extension's page by its id (#1051, SkyAerope: an extension without host permissions is held to CORS like a page), as extensionIDs spells them; "*" is refused, since it would let every web page or extension the user has spend their subscriptions.

func ClientAgentsNow added in v0.1.1227

func ClientAgentsNow() map[string]string

ClientAgentsNow is Load().ClientAgents, parsed again only once the file changed: the usage pages ask for it on every read of the ledger, which the gateway does too, and the whole settings file is more to parse.

func Clock added in v0.1.289

func Clock(at string) (hour, min int, ok bool)

Clock reads a time of day, "06:00" (seconds, as a time field may send them, are dropped), as its hour and minute.

func Dir

func Dir() string

Dir is the folder every magpie file lives in: the data folder beside a portable magpie (appdir.Portable), else ~/.config/magpie.

func EditCheckinOff added in v0.1.1306

func EditCheckinOff(edit func([]string) []string) error

EditCheckinOff changes CheckinOff alone, as edit returns it from what the file holds now, so a save elsewhere from an older snapshot isn't undone. A settings file that can't be read is left as it is, with the error.

func EditUsageAliases added in v0.1.1358

func EditUsageAliases(edit func(map[string]string) (map[string]string, error)) error

EditUsageAliases changes UsageAliases alone, as edit returns them from what the file holds now, so a save elsewhere from an older snapshot isn't undone. A settings file that can't be read is left as it is, with the error: what it holds is unknown, not "no aliases". edit is given a copy, and an error from it saves nothing.

func GatewayAddr added in v0.1.950

func GatewayAddr() string

GatewayAddr is where the gateway listens: MAGPIE_ADDR when it is set, else the port Settings has, on loopback (Magic_zero on Discord: 3425 was taken, and a port of one's own is easier to tell apart).

func Migrate

func Migrate()

Migrate carries the files of an install that predates the name over: ~/.config/dial and ~/.cache/dial become ~/.config/magpie and ~/.cache/magpie. It copies rather than moves, so a dial that is still running keeps working, and it only fills folders that do not exist yet. The old folders can be deleted once nothing uses them. A portable magpie has only its own data folder and copies nothing in.

func ParseAllowIP added in v0.1.1214

func ParseAllowIP(raw string) (netip.Prefix, error)

ParseAllowIP reads one entry of the gateway's allow list: an IP address (IPv4 or IPv6, for itself alone) or a range in CIDR form. An IPv4 address written as IPv6 (::ffff:1.2.3.4) is the IPv4 one.

func Parses added in v0.1.1225

func Parses() int64

Parses is how many times settings.json has been parsed in this process, for a test that a look holding the settings doesn't read them again for each thing it looks at.

func Path

func Path() string

Path is the settings file.

func PerModelKeys added in v0.1.496

func PerModelKeys(s *Settings, fn func(name string, m reflect.Value))

PerModelKeys calls fn with the name of every per-model map of s and the map itself, the settings' own values and not copies, so fn may change them (see ModelNames). A field that is not per-model is not passed; the name comes with the map so a caller that reports on the maps it was given can tell them apart.

func Portable added in v0.1.630

func Portable() string

Portable is the data folder of a portable magpie, or "" when installed.

func Save

func Save(s Settings) error

Save validates and writes the settings, preserving current recording consent.

func SavedAddr added in v0.1.950

func SavedAddr() string

SavedAddr is GatewayAddr with MAGPIE_ADDR left aside: the address of the magpie whose settings these are, which one the variable moves (magpie-dev, a sandbox) runs beside.

func SetGatewayConversations added in v0.1.1146

func SetGatewayConversations(on bool) error

SetGatewayConversations changes local recording consent without overwriting other settings from an earlier snapshot.

func SetRedactModel added in v0.1.1295

func SetRedactModel(on bool) error

SetRedactModel turns the local masking model on or off, on its own, as Settings → Privacy does once the model is downloaded.

func SplitAllowIPs added in v0.1.1214

func SplitAllowIPs(v string) []string

SplitAllowIPs splits MAGPIE_ALLOW_IPS ("1.2.3.4,10.0.0.0/8 2001:db8::/32") into its entries, at commas and spaces.

Types

type ModelPrice added in v0.1.504

type ModelPrice struct {
	Input      *float64 `json:"input,omitempty"`
	Output     *float64 `json:"output,omitempty"`
	CacheRead  *float64 `json:"cache_read,omitempty"`
	CacheWrite *float64 `json:"cache_write,omitempty"`
	// CacheWrite1h is a 1-hour cache write's price, absent where none is
	// given: such a write is then counted at 2× input for a Claude model,
	// else at the 5-minute price (catalog.OneHourFor).
	CacheWrite1h *float64 `json:"cache_write_1h,omitempty"`
	// Tiers are the prices of a request whose input is over a size, each
	// whole: "magpie model price … --tier 272k …" (PAMI on Discord).
	Tiers []catalog.Tier `json:"tiers,omitempty"`
}

ModelPrice is the price of one model as the user states it. Each part is a pointer so that a file leaving one out is told so rather than billing that part of the call at zero: a model is priced whole or not at all.

func StatedPrice added in v0.1.1032

func StatedPrice(p catalog.Price) ModelPrice

StatedPrice is a price as ModelPrices keeps it: every part given, the 1-hour cache write only when it is.

func (ModelPrice) Price added in v0.1.504

func (m ModelPrice) Price() (catalog.Price, string)

Price is the price the user stated, when every part of it is given and is a number a vendor could charge. The second return names the first part that is not, empty when the price is usable.

type OTel added in v0.1.602

type OTel struct {
	Enabled  bool              `json:"enabled,omitempty"`
	Endpoint string            `json:"endpoint,omitempty"`
	Headers  map[string]string `json:"headers,omitempty"`
	Metrics  bool              `json:"metrics,omitempty"`
	// Sessions follows newly recorded local Codex and Pi interactions.
	Sessions bool `json:"sessions,omitempty"`
	// Bodies adds each call's request and reply, as the gateway captured
	// them (secrets masked, cut at 256 KiB), to its span as Langfuse's
	// observation input and output (#538); off, only metadata leaves
	Bodies bool `json:"bodies,omitempty"`
	// BodiesWhole keeps those bodies entire, not cut at 256 KiB: the reply
	// is written to a temporary file as it streams and read back for the
	// export, so a long prompt or reply reaches the trace whole. Only
	// meaningful with Bodies on
	BodiesWhole bool `json:"bodiesWhole,omitempty"`
}

OTel is the optional OTLP/HTTP metadata export. Endpoint is the base URL, including /api/public/otel for Langfuse; signals append /v1/traces or metrics.

func OTelExport added in v0.1.602

func OTelExport() (OTel, error)

OTelExport applies explicit MAGPIE_OTEL_* overrides without changing the saved preferences. Merely setting an endpoint never enables export.

func (OTel) Check added in v0.1.602

func (o OTel) Check() error

type Settings

type Settings struct {
	Theme string `json:"theme,omitempty"` // system | light | dark
	// Colorblind switches chart series (--c1…--c7) to an Okabe–Ito palette
	// and gives each quota curve window, and its legend key, a dash of its
	// own (#2073).
	Colorblind bool   `json:"colorblind,omitempty"`
	Lang       string `json:"lang,omitempty"` // system | en | zh
	Tray       string `json:"tray,omitempty"` // what clicking the tray icon opens: panel | window
	// SessionTerminal is the Mac app that opens a resumed session, by bundle
	// id. "" and "system" follow the .command file association. This
	// computer's own (KeepOwn): another Mac may not have that app.
	SessionTerminal string `json:"sessionTerminal,omitempty"`
	// Currency is what a cost — the Usage page's, the tray panel's, the
	// TUI's and the CLI's — is shown converted to: usd (its native
	// currency, list prices being in dollars) or cny, at a live exchange
	// rate (see internal/fx). A vendor's own balance, already in its own
	// currency (a Chinese relay's ¥), is never touched by this.
	Currency string `json:"currency,omitempty"`
	// ChineseUnits shortens a large count in 万 and 亿 when magpie speaks
	// Chinese (8000 万 rather than 80M); otherwise it is in K, M and B, as
	// prices per million tokens and context windows (200K, 1M) are, in
	// Chinese too (#740). It means nothing in English. It replaces
	// westernUnits, which asked for K/M/B: what that chose is now the
	// default, so an old file's is left unread.
	ChineseUnits bool `json:"chineseUnits,omitempty"`
	// Dock keeps magpie in the Mac's Dock as well as the menu bar, for a
	// menu bar too full to show its icon.
	Dock bool `json:"dock,omitempty"`
	// DockWindow shows it in the Dock only while its window is open, so
	// Cmd-Tab reaches the window without an icon kept there the rest of
	// the time. Dock wins over it.
	DockWindow bool `json:"dockWindow,omitempty"`
	// Proxy for magpie's own requests to vendors: "" follows the
	// environment and then the system, "direct" uses none, anything else
	// is the proxy (http://, https:// or socks5://; host:port means http).
	Proxy string `json:"proxy,omitempty"`
	// ProxyCustom is the last proxy of the user's own Proxy held, kept
	// while Proxy is "" or "direct" so that choosing Custom again in
	// Settings brings back the address and login typed (Zane on Discord).
	// Save sets it from Proxy, and it is stored as Proxy is. This
	// computer's own (KeepOwn), as Proxy is.
	ProxyCustom string `json:"proxyCustom,omitempty"`
	OTel        OTel   `json:"otel,omitempty"`
	// GitHubToken is a GitHub token the library's requests to GitHub's
	// API carry, raising its rate limit from 60 requests an hour to 5,000.
	// It is a secret: the Settings page is told only a masked one, and a
	// backup or sync without keys leaves it out, as it does LANKey.
	GitHubToken string `json:"githubToken,omitempty"`
	// Redact keeps secrets in what agents send (API keys, private keys,
	// tokens, passwords) from the vendors behind magpie: they go as
	// placeholders, and come back as they were. RedactPersonal does the same
	// for emails, phone numbers and ID and bank card numbers, and
	// RedactWords for the user's own words. RedactRules are the user's own
	// rules for secrets magpie's don't know (a gateway's oc_sk_… key), a
	// prefix or a pattern each, masked with the secrets while Redact is on.
	// RedactKinds turns the other kinds of personal data on or off one by
	// one, by redact.Categories' ids (ssn, ip, home…), under
	// RedactPersonal: only the user's choices, a kind not in it is as its
	// category's On says. RedactModel has a local model (internal/pii) find
	// what the rules can't as well, a name or an address in free text,
	// under Redact and RedactPersonal; it is this computer's own, as the
	// model is downloaded on it.
	Redact         bool            `json:"redact,omitempty"`
	RedactModel    bool            `json:"redactModel,omitempty"`
	RedactPersonal bool            `json:"redactPersonal,omitempty"`
	RedactKinds    map[string]bool `json:"redactKinds,omitempty"`
	RedactWords    []string        `json:"redactWords,omitempty"`
	// RedactWordsAnyCase matches RedactWords in any case (ZoeCats on
	// Discord); off, the default, a word is matched as written.
	RedactWordsAnyCase bool          `json:"redactWordsAnyCase,omitempty"`
	RedactRules        []redact.Rule `json:"redactRules,omitempty"`
	// RedactActions is what is done with a value of a kind (redact.Hit's
	// Kind: API_KEY, EMAIL, a rule's own…) other than masking it, where
	// the user chose: redact.Warn sends it as written and records it,
	// redact.Block holds the request back. RedactAllow are values sent
	// as written whatever finds them, each whole, in any case (#1977).
	RedactActions map[string]string `json:"redactActions,omitempty"`
	RedactAllow   []string          `json:"redactAllow,omitempty"`
	// LAN shares the gateway on the local network; remote callers must use
	// named caller keys. LANKey is retained for older Magpie versions.
	// LAN is this computer's own (KeepOwn; Lullaby on Discord: sharing
	// turned on at home came on at work too). LANKey and LANKeyID name the
	// default key, which rides the synced gateway-key store, so they follow
	// the folder: turned on on another computer, sharing there takes up the
	// same key.
	LAN    bool   `json:"lan,omitempty"`
	LANKey string `json:"lanKey,omitempty"`
	// CORSOrigins are the web pages (scheme://host[:port]) and browser
	// extensions (chrome-extension://<id>, moz-extension://<uuid>,
	// safari-web-extension://<uuid>) whose scripts may call the gateway
	// from a browser (#1051): a preflight from one is
	// answered, and its calls carry the CORS headers that let it read the
	// reply, each with an enabled gateway key. None by default: a page
	// gets no CORS headers, as before.
	CORSOrigins []string `json:"corsOrigins,omitempty"`
	// AllowIPs are the addresses (IPs and CIDR ranges) other computers may
	// reach the gateway from at all (lc on Discord); any other is refused
	// 403 even with a gateway key. None: every address, as before. This
	// computer is always let in. MAGPIE_ALLOW_IPS, where set, is used in
	// its place (gateway.AllowIPs). This computer's own (KeepOwn), as LAN
	// is: the addresses are of the network this computer is on.
	AllowIPs []string `json:"allowIPs,omitempty"`
	// FetchPrivate are the private hosts and ranges (a name, name:port, an
	// IP or a CIDR range) magpie may download a URL from that it didn't
	// choose itself: an image or video a request names or a vendor answers
	// with, or a page web_fetch reads. None by default: such a URL is
	// downloaded from public addresses only, whoever sent the request and
	// from wherever (gateway.publicClient). In settings.json only; it names
	// this computer's own network, so it isn't synced (KeepOwn).
	FetchPrivate []string `json:"fetchPrivate,omitempty"`
	// Port is the gateway's port on this computer, 0 for DefaultPort.
	// MAGPIE_ADDR, where it is set, comes first (GatewayAddr).
	Port int `json:"port,omitempty"`
	// LANKeyID remembers the default named key created when sharing is enabled.
	LANKeyID string `json:"lanKeyId,omitempty"`
	// RequestArchive keeps each call the gateway serves — its headers and
	// bodies both ways, secrets taken out — in the S3 bucket sync keeps
	// its backup in (gateway/archive.go), for looking into a request later.
	RequestArchive bool `json:"requestArchive,omitempty"`
	// RequestArchiveMaxMB is how much of each body the archive keeps, in
	// MiB: 0 for 32, at most 1024 (#447)
	RequestArchiveMaxMB int `json:"requestArchiveMaxMB,omitempty"`
	// RequestArchiveLocal keeps the request archive on this computer's disk,
	// in magpie's folder (gateway/archive_local.go), while sync has no S3
	// bucket for it (CyberCaius on Discord). Its own switch, separate from
	// RequestArchive, which sync carries to every computer: this computer's
	// own (KeepOwn), so a request body is written to a disk only where the
	// user turned it on.
	RequestArchiveLocal bool `json:"requestArchiveLocal,omitempty"`
	// RequestArchiveLocalMB is how large the archive on this computer grows
	// before its oldest calls are removed, in MiB: 0 for 1024. In
	// settings.json only; this computer's own (KeepOwn).
	RequestArchiveLocalMB int `json:"requestArchiveLocalMB,omitempty"`
	// GatewayConversations is local, opt-in recording of session traffic.
	// Only SetGatewayConversations changes it; other saves keep local consent.
	GatewayConversations bool `json:"gatewayConversations,omitempty"`
	// CodexWarmup starts a ChatGPT account's next window as soon as the
	// last one resets, with one tiny request, so it counts from then (a
	// Codex window starts at its first use): "" off, "week" the weekly
	// window, "all" the 5-hour one too.
	CodexWarmup string `json:"codexWarmup,omitempty"`
	// ClaudeWarmup is CodexWarmup for the Claude accounts, the request
	// sent through Claude Code.
	ClaudeWarmup string `json:"claudeWarmup,omitempty"`
	// CodexWarmAt starts each ChatGPT account's 5-hour window at a time of
	// day of the user's choosing, local "15:04", with the same tiny
	// request: an account whose 5-hour window isn't running then gets one,
	// so the windows line up with the day (06:00 gives three by 21:00, where
	// the first use at 9 gives two by the end of it); "" off. It works
	// with CodexWarmup or without it. ClaudeWarmAt is the Claude accounts'.
	// Each is CodexWarmAts' (ClaudeWarmAts') first, all a magpie before
	// them read: a file with it and no list has that one time, and it is
	// kept written for an older magpie to go on starting the day's first.
	CodexWarmAt  string `json:"codexWarmAt,omitempty"`
	ClaudeWarmAt string `json:"claudeWarmAt,omitempty"`
	// CodexWarmAts are the times of day CodexWarmAt's start is made at,
	// each once a day, earliest first: 09:00, 15:05 and 19:10 start a
	// window at each, where it isn't running (#1260); none when empty.
	// ClaudeWarmAts is the Claude accounts'.
	CodexWarmAts  []string `json:"codexWarmAts,omitempty"`
	ClaudeWarmAts []string `json:"claudeWarmAts,omitempty"`
	// CodexWarmAtOf is a ChatGPT account's own time of day for that, by
	// its name in lower case, "off" for none: two accounts started hours
	// apart take over from one another, where at one time they run out
	// together (#957). An account not in it has CodexWarmAt.
	CodexWarmAtOf map[string]string `json:"codexWarmAtOf,omitempty"`
	// CodexAutoReset are the ChatGPT accounts (lower-case) that spend one
	// of their rate-limit resets by themselves once their weekly window is
	// used up and no other account can take the request: at most one a
	// week each (see provider.AutoUseCodexReset); and one about to run out
	// unused half an hour before it does, or at once when the account is
	// held up past then (provider.SpendExpiringCodexResets).
	CodexAutoReset []string `json:"codexAutoReset,omitempty"`
	// CodexAutoResetOnly narrows an account in CodexAutoReset to one of
	// those two times (#2078, alanwalk: Codex's own resets come often, so
	// a week used up is worth waiting out, while a card about to run out
	// isn't): "week" for its week used up only, "expiring" for the one
	// about to run out only. An account not in it uses one at both, as
	// every account did before the two were split.
	CodexAutoResetOnly map[string]string `json:"codexAutoResetOnly,omitempty"`
	// CodexNoCredits are the ChatGPT accounts (lower-case) that don't
	// spend their credits: once a usage window is used up, routing holds
	// the account as used up and goes on to the user's other accounts,
	// groups and fallbacks, where by default the vendor answers on its
	// credits (see provider.CodexCredits).
	CodexNoCredits []string `json:"codexNoCredits,omitempty"`
	// WorkBuddyCheckin presses WorkBuddy's daily check-in (签到) for each
	// signed-in WorkBuddy (China) account once a Beijing day, claiming the
	// credits it gives while its event runs.
	WorkBuddyCheckin bool `json:"workbuddyCheckin,omitempty"`
	// TraeCheckin presses Trae CN's daily check-in (每日签到) for each
	// signed-in Trae CN account (its plugin's) once a Beijing day, claiming
	// the credits it gives.
	TraeCheckin bool `json:"traeCheckin,omitempty"`
	// MiniMaxCheckin presses MiniMax Code's daily check-in (签到) for each
	// signed-in MiniMax Code (China) account (its plugin's) once a Beijing
	// day, claiming the credits it gives (#811).
	MiniMaxCheckin bool `json:"minimaxCheckin,omitempty"`
	// QoderCheckin claims Qoder's daily credits for each signed-in Qoder
	// and Qoder CN account (its plugin's) once a Beijing day (ARNO on
	// Discord).
	QoderCheckin bool `json:"qoderCheckin,omitempty"`
	// PluginCheckins turns a plugin's own daily check-in (auth.checkin)
	// on or off, by the plugin's provider id; one not set follows its
	// vendor's switch above, which the plugin took over, else is off.
	PluginCheckins map[string]bool `json:"pluginCheckins,omitempty"`
	// PluginOptions are the user's values of the switches plugins declare
	// for their providers (auth.options, #2038): provider id → the
	// option's key → on. A key not here takes the plugin's default.
	PluginOptions map[string]map[string]bool `json:"pluginOptions,omitempty"`
	// CheckinOff are the accounts left out of their vendor's daily
	// check-in while it is on (#1902), each by its entry in the vendor's
	// check-in file: "<site or provider>|<account id>" ("workbuddy|<uid>",
	// "trae-cn|<uid>", a plugin's "<provider>|<sign-in key>", e.g.
	// "autoclaw|autoclaw#x1y2z3"; before plugins#254 a plugin's was
	// "<provider>|<display name>", which provider.adoptLegacyPluginCheckins
	// carries over).
	CheckinOff []string `json:"checkinOff,omitempty"`
	// MemberModel has a reply's model name the routing group's member
	// that answered, as magpie's provider/model id (workbuddy/glm-5.3-flash),
	// rather than the vendor's own name for it, for agents that count
	// usage by the reply's model (#822). Claude Code, Claude Desktop and
	// Codex always get the vendor's name: they read it themselves.
	MemberModel bool `json:"memberModel,omitempty"`
	// NoLoopGuard lets a streamed reply run on when its reasoning or text
	// is stuck in a loop of the same few lines (#1359). Off, as by
	// default, the gateway ends such a reply with an error.
	NoLoopGuard bool `json:"noLoopGuard,omitempty"`
	// ClaudeLeanPrompt starts a Claude subscription's Claude Code run with
	// the caller's own instructions as its system prompt
	// (--system-prompt-file), in place of Claude Code's (about 1.1K tokens
	// a request on Sonnet 5.5), and not in its first message. Still the
	// real Claude Code, its billing line and identity line kept; off, as by
	// default, the run is the shape every Claude Code run has.
	ClaudeLeanPrompt bool `json:"claudeLeanPrompt,omitempty"`
	// ClaudeRunsMost is how many Claude Code processes a Claude
	// subscription may have running at once: conversations' runs, those
	// waiting for a next turn or on tool results, and one-off tests and
	// allowance reads alike. 0, as by default, has magpie size it from
	// this machine's memory on Linux (its cgroup's limit, else MemTotal),
	// and leaves it unbounded elsewhere: each Claude Code holds about
	// 100 MiB of its own, and a 512 MiB service was OOM-killed (#2956).
	// It is this machine's, not taken from another's settings.
	ClaudeRunsMost int `json:"claudeRunsMost,omitempty"`
	// RefusalRetries is how many more times a request the vendor's safety
	// filter refused, with nothing said, is asked of another account or
	// routing group member (#248), unset for every one left. 0 hands the
	// refusal straight back: to an agent on Anthropic's Messages API as
	// the vendor's own reply, stop_reason "refusal", which Claude Code
	// answers by switching to its fallback model itself (DD on Discord).
	RefusalRetries *int `json:"refusalRetries,omitempty"`
	// NoAccountDiscovery leaves every agent's sign-in unread (#1535,
	// MaxHaiCom): no credential file, Keychain item or agent CLI is asked
	// who is signed in, and no subscription is offered; API-key providers
	// work as before (see AccountDiscoveryOff, provider/discovery.go). Off,
	// as by default, subscriptions are found as ever. This computer's own
	// (KeepOwn), set only by its own switch or `magpie accounts discovery`.
	NoAccountDiscovery bool `json:"noAccountDiscovery,omitempty"`
	// NoStats stops the one event a day that counts magpie's users (see
	// internal/stats).
	NoStats bool `json:"noStats,omitempty"`
	// NoUsageStats keeps which agents, providers and models magpie is
	// used with out of that event, which then counts the user only.
	NoUsageStats bool `json:"noUsageStats,omitempty"`
	// NoUpdatePill keeps the header's Update pill away when a newer magpie
	// is out; UpdateSkip is the one version it was hidden for, and a newer
	// one brings it back. Either way magpie still downloads the version and
	// puts it in as it quits, and Settings' version row still offers it.
	NoUpdatePill bool   `json:"noUpdatePill,omitempty"`
	UpdateSkip   string `json:"updateSkip,omitempty"`
	// NoAutoUpdate stops magpie asking for a newer version by itself, and
	// so downloading one (#472): only Settings' Check, or magpie update,
	// asks then. UpdateEvery is how often it asks otherwise, in minutes:
	// one of UpdateEveries, 0 for every six hours.
	NoAutoUpdate bool `json:"noAutoUpdate,omitempty"`
	UpdateEvery  int  `json:"updateEvery,omitempty"`
	// NoAutoDownload has a check, by the clock or Settings' Check, only
	// say a newer version is out (with its notes and size) rather than
	// download it (#2296): a metered network or a proxy billed by traffic
	// downloads it only on a click (Download update, the header's Update,
	// magpie update). Off, as by default, a newer version is downloaded
	// as soon as it is found.
	NoAutoDownload bool `json:"noAutoDownload,omitempty"`
	// AutoRestart has the app restart into an update it downloaded by
	// itself, once the gateway it serves has nothing in flight, rather than
	// waiting for a click or for quitting (#2096). Off by default: a
	// restart ends the window and whatever an agent sends next waits for
	// the gateway to be back. Nothing happens with NoAutoUpdate on.
	AutoRestart bool `json:"autoRestart,omitempty"`
	// UpdateMirror is a GitHub download mirror an update's file is fetched
	// through, its prefix put before the release's github.com URL
	// (https://mirror.example/https://github.com/…): "" for none, which is
	// the default — magpie names no mirror of its own. The file is still
	// checked against the SHA-256 the update feed at usemagpie.ai gives,
	// never one from the mirror. magpie update mirror sets it.
	UpdateMirror string `json:"updateMirror,omitempty"`
	// Vision is the model that describes an image to a model that can't see
	// it: a model's id (provider/model, group/<id>), "off" to turn such an
	// image away, or empty for one magpie picks (see gateway.seer).
	Vision string `json:"vision,omitempty"`
	// VisionEffort is how hard the Image recognition model thinks as it
	// reads an image (MOMO on Discord): a level of catalog.EffortRank,
	// fitted to the model's own levels when it is asked, or empty for
	// magpie's choice, the least the model has (gateway.visionEffort).
	VisionEffort string `json:"visionEffort,omitempty"`
	// ImageGen is the model magpie's generate_image tool draws with (the
	// gateway's /v1/images/generations when a request names no model): a
	// model's id, "off", or empty for one magpie picks (gateway.drawer).
	// "off" turns the video a request that names no model gets off too
	// (gateway.videomaker).
	ImageGen string `json:"imageGen,omitempty"`
	// ImageTimeout is how long, in minutes, a vendor is given to draw an
	// image and send it (neighbads on X: a slow image endpoint needs more
	// than 5): from 1 to ImageTimeoutMost, 0 for ImageTimeoutDefault.
	// ImageWait reads it.
	ImageTimeout int `json:"imageTimeout,omitempty"`
	// ClassifyTimeout is how long, in seconds, a routing group's classifier
	// is given to say which intent a turn is (#2934: a Kiro subscription's
	// haiku needed more than 8): from 1 to ClassifyTimeoutMost, 0 for
	// ClassifyTimeoutDefault. ClassifyWait reads it.
	ClassifyTimeout int `json:"classifyTimeout,omitempty"`
	// Searcher is the provider that searches the web for a model that
	// can't: "<provider>" with the small model magpie picks of it,
	// "<provider>/<model>", or empty for the one magpie picks
	// (gateway.searcher). "off" allows only configured search APIs.
	// One that is gone, off or can't search gives way to magpie's pick.
	Searcher string `json:"searcher,omitempty"`
	// SearcherEffort is how hard the searcher's model thinks as it searches
	// (MOMO on Discord): a level of catalog.EffortRank, fitted to the
	// model's own levels when it is asked, or empty to send none, the
	// model's own default (gateway.searchEffort).
	SearcherEffort string `json:"searcherEffort,omitempty"`
	// SearchFirst is what a model that can't search the web is searched
	// for with first, when both a provider and a search API can (#928):
	// SearchFirstAPI for the search APIs, the provider's search after them
	// when they fail; empty for the provider first, as before.
	SearchFirst string `json:"searchFirst,omitempty"`
	// TrayUsages are the subscriptions and plans whose windows are shown
	// beside the tray icon, in the order shown, each by its provider and
	// account ("claude|a@b.c"); none when empty.
	TrayUsages []string `json:"trayUsages,omitempty"`
	// TrayUsage is TrayUsages' first, all a magpie before them read: a file
	// with it and no TrayUsages shows that one, and it is kept written for
	// an older magpie to go on showing it.
	TrayUsage string `json:"trayUsage,omitempty"`
	// TrayUsageEvery is how often, in minutes, that text is brought up to
	// date; 0 is every 3 (one of TrayEvery).
	TrayUsageEvery int `json:"trayUsageEvery,omitempty"`
	// TrayNoLogos draws the Mac menu bar's cards without their logos: each
	// is its windows stacked alone, a thin line between one card and the
	// next.
	TrayNoLogos bool `json:"trayNoLogos,omitempty"`
	// TrayNoBird leaves magpie's bird out of the Mac menu bar while the
	// cards are there, the cards alone (KevinXC on Discord); a click on one
	// still opens the panel, and with no cards the bird is shown.
	TrayNoBird bool `json:"trayNoBird,omitempty"`
	// TrayPace puts each window's Pace number beside its figure in the
	// menu bar ("42% +12%"), as the Usage page's cards have it (fflake33
	// on Discord); off by default, as it widens every card.
	TrayPace bool `json:"trayPace,omitempty"`
	// TrayNoCost leaves money out of magpie's own totals in the menu bar:
	// the today and last-7-days cards, their tooltips and the tooltip with
	// no cards say the tokens alone (BussanQ on Discord); off by default,
	// the cost shown.
	TrayNoCost bool `json:"trayNoCost,omitempty"`
	// Lightweight lets the webview of a window closed — the tray panel or
	// the main window — go once it has stayed closed a while, and makes it
	// again when it is opened (#580): less memory, a moment's wait. This
	// computer's own (KeepOwn). Nil is off: a window made again on each
	// opening was too slow to have by default (the owner, 10-21: "太卡");
	// one who turned it on keeps it on (LightweightOn).
	Lightweight *bool `json:"lightweight,omitempty"`
	// KeepAwake keeps this computer from going to sleep by itself while
	// agents work through the gateway and for a while after (xiao_wang24004
	// on X; internal/awake). This computer's own (KeepOwn).
	KeepAwake bool `json:"keepAwake,omitempty"`
	// NoWSLAgents stops magpie looking in WSL on its own, on Windows
	// (#1264): no distro is listed or probed for agents, their sessions or
	// Claude Code, and the agents found there before aren't shown.
	NoWSLAgents bool `json:"noWSLAgents,omitempty"`
	// KeepAwakeDisplay keeps the display on too while KeepAwake holds the
	// computer awake (#975, Hu9956: an agent recording the screen to check
	// its work found it locked). This computer's own (KeepOwn).
	KeepAwakeDisplay bool `json:"keepAwakeDisplay,omitempty"`
	// GatewayMode is whether `magpie web` shows only what a gateway serving
	// other machines needs (Player on Discord): "on", "off", or "" to
	// decide by itself — on for `magpie web --gateway`, or with no agents
	// on this machine. This computer's own (KeepOwn).
	GatewayMode string `json:"gatewayMode,omitempty"`
	// QuotaLeft shows a subscription's windows by how much of each is left,
	// not used: the Usage page, the tray panel and the menu bar alike.
	QuotaLeft bool `json:"quotaLeft,omitempty"`
	// QuotaReads is when magpie reads subscriptions' allowances and keys'
	// balances from their vendors (#1518, RooobinYe): "" whenever it needs
	// them (the Usage page and tray, routing, alerts, the switch to an
	// account with room), "asked" only when the user asks — the Usage page
	// opened or refreshed, a card's refresh, `magpie quota`/`accounts`.
	// Otherwise the readings kept from the last time stand, marked as of
	// then; an account the vendor turned away for its quota is still read.
	// This computer's own (KeepOwn): magpie at login comes up before the
	// proxy app, and its reads went out direct.
	QuotaReads string `json:"quotaReads,omitempty"`
	// UsageAlert is how much of a subscription's or plan's window, in
	// percent, is used when magpie says so with a notification (#368):
	// once for each time the window runs, for every window routing counts
	// (not one set aside, as on-demand spending is); 0 is off.
	UsageAlert int `json:"usageAlert,omitempty"`
	// BalanceAlert is the balance a key or account has fallen to when
	// magpie says so, in that balance's own currency or credits, once until
	// it is topped up past it again; 0 is off.
	BalanceAlert float64 `json:"balanceAlert,omitempty"`
	// ResetReminder is how many hours before a renewal magpie says what
	// would be lost by it (#720): a weekly or monthly window with much of
	// it left before it renews, a reset credit unspent before it runs out;
	// once each. 0 is off.
	ResetReminder int `json:"resetReminder,omitempty"`
	// TurnDoneAlert is how many seconds an agent's turn runs, from the
	// user's prompt to its last reply, before magpie says it is done with a
	// notification (itieniu on X): a long task set going while the user
	// works elsewhere. Only turns the app's own gateway serves; 0 is off.
	TurnDoneAlert int `json:"turnDoneAlert,omitempty"`
	// PlainNames has the model lists magpie gives agents name each model
	// by its name alone, without its provider's or "routing group" after it
	// (#335) — but for two in one list that would read the same, which keep
	// it (see provider.Labels).
	PlainNames bool `json:"plainNames,omitempty"`
	// PlainOwnNames, with PlainNames off, has those lists name a model the
	// user gave a name of their own by that name alone, just as they wrote
	// it (#92: "Opus 5.5", not "Opus 5.5 · Claude Code"), the vendor's names
	// keeping their provider's after them (see provider.Labels).
	PlainOwnNames bool `json:"plainOwnNames,omitempty"`
	// BareModelIDs has the gateway's /v1/models list each model by its id
	// without the provider as well ("gpt-5.6-sol" beside
	// "codex/gpt-5.6-sol"), for clients moved from another proxy that ask
	// for bare ids (love1sbug on X; provider.BareEntries). Off by default.
	BareModelIDs bool `json:"bareModelIDs,omitempty"`
	// CodexAgentsV1 has the OpenAI models magpie hands Codex — the ChatGPT
	// account's own, their codex/ ids and the groups one is in — say
	// multi_agent_version "v1" (#141): their subagents are then handed
	// their tasks as text, which a magpie-served subagent can read, where
	// V2's are sealed by OpenAI's server, and it is V1 that a ChatGPT
	// lead's subagent on a magpie-served model needs (a V2 task to a
	// non-ChatGPT model fails). Codex's own features.multi_agent_v2 would
	// still win: where it is on in the config magpie routes, magpie says so
	// (internal/agent/codex.go, and the Settings row) and leaves it to the
	// user to turn off. A thread keeps the version it started with.
	CodexAgentsV1 bool `json:"codexAgentsV1,omitempty"`
	// CodexTitles is where the requests Codex makes for a thread's title
	// (thread_title, thread_title_reconsideration: a hidden turn of their
	// own, on Codex's own model through its ChatGPT sign-in) go (#705): ""
	// as Codex sends them, "off" answered by magpie with no title and sent
	// nowhere, or a model's id (provider/model, group/<id>) that writes it.
	CodexTitles string `json:"codexTitles,omitempty"`
	// CodexDescriptions is where the request Codex makes for a thread's
	// description (thread_description: its own hidden turn, on Codex's own
	// model through its ChatGPT sign-in) goes: "" as Codex sends it, or a
	// model's id (provider/model, group/<id>) that writes it. It is not
	// CodexTitles: a description asks for {"description"} alone.
	CodexDescriptions string `json:"codexDescriptions,omitempty"`
	// CodexPredictions is where the request Codex's app makes to predict
	// the user's next message for its composer (composer_predictions: a
	// hidden turn on a fork of the whole conversation, on the thread's own
	// model through its ChatGPT sign-in) goes (#1970, pluo): "" as Codex
	// sends it, "off" answered by magpie with no prediction and sent
	// nowhere, or a model's id (provider/model, group/<id>) that writes it.
	CodexPredictions string `json:"codexPredictions,omitempty"`
	// CodexAutoReview is the model Codex's auto-review (approvals_reviewer
	// = "auto_review": the guardian that decides an approval in the user's
	// place) runs on (#938): a model's id (provider/model, group/<id>) put
	// in every entry of the list magpie hands Codex as
	// auto_review_model_override, which Codex takes before its own
	// codex-auto-review or the conversation's model at low effort. ""
	// leaves the list as it was.
	CodexAutoReview string `json:"codexAutoReview,omitempty"`
	// CodexSubagentModel is the model the gateway puts every subagent
	// Codex spawns on (a request x-openai-subagent names collab_spawn),
	// whatever model its lead asked for in spawn_agent: a model a ChatGPT
	// account in magpie serves (provider/model), since a subagent's task
	// is sealed for one (willz on Discord). "" leaves each on the model its
	// lead asked for.
	CodexSubagentModel string `json:"codexSubagentModel,omitempty"`
	// DesktopSubagentModel is the model the gateway puts Claude Desktop's
	// subagent tool turns on. "" leaves them on the model their lead asks
	// for; tool-less helpers and the lead's own turns are never moved.
	DesktopSubagentModel string `json:"desktopSubagentModel,omitempty"`
	// DesktopSubagentEffort fixes Desktop's subagent tool turns at this
	// reasoning level. Empty keeps the effort each request asks for.
	DesktopSubagentEffort string `json:"desktopSubagentEffort,omitempty"`
	// FullContext has Codex and Claude Code told a model's whole context
	// window. Off, a window above WorkingWindow is told as WorkingWindow,
	// so they compact a long conversation there instead of sending ever
	// more of it on every turn (X: Chen, a 1M DeepSeek model through
	// Codex and Claude Code took 70–90s to its first token at 550K):
	// what OpenAI does with its own models, 272K though they can take
	// more, and Anthropic with its, 200K unless a [1m] one is picked.
	FullContext bool `json:"fullContext,omitempty"`
	// DesktopLongest has Claude Desktop list a model of 1M tokens or more
	// once, as its 1M entry (its id with Claude Code's "[1m]"), instead of
	// twice (#1272): Desktop adds a "1M context window" entry beside any
	// gateway model whose max_input_tokens is 1M or more and whose id has no
	// "[1m]", and adds none to one whose id has it. Off, as before, it shows
	// both, the plain one compacting at Claude Code's 200K.
	DesktopLongest bool `json:"desktopLongest,omitempty"`
	// GuideHidden: the user put away the Agents page's Get started card
	// (wangye1644294 on X) before finishing it. Kept here, not in the
	// page's storage, so the panel, the window and a reload agree.
	GuideHidden bool `json:"guideHidden,omitempty"`
	// CompactAt is the window told for a longer one when FullContext is
	// off, in tokens: 0 is WorkingWindow (#876: 272K was the only one).
	// A provider's or a model's own (ModelCompacts) comes before it.
	CompactAt int `json:"compactAt,omitempty"`
	// CodexAsyncCompact has Codex (0.163 on) compact a long conversation
	// in the background, while its turn goes on, once it is past
	// CodexAsyncCompactPercent of the model's window (#2490, lantern2077
	// on Discord): [features.async_compaction] enabled in the config Codex
	// is routed through, and capabilities.remote_compaction = "v2" on
	// magpie's provider table, without which Codex never starts one on a
	// provider other than OpenAI's. A value the user set there is theirs
	// and stays (internal/agent/codex_async.go).
	CodexAsyncCompact bool `json:"codexAsyncCompact,omitempty"`
	// CodexAsyncCompactPercent is that share, 1–100; 0 is Codex's own, 65.
	CodexAsyncCompactPercent int `json:"codexAsyncCompactPercent,omitempty"`
	// ChinaMirror is the Plugins page's 「国内镜像」 switch: the plugin list,
	// npm (the plugins' packages and what npm says of them) and Bun's
	// downloads are asked of mirrors in China first, and of their official
	// addresses after (see source.China).
	ChinaMirror bool `json:"chinaMirror,omitempty"`
	// TextSize is how large the window's and the tray panel's pages are
	// drawn, in percent (one of TextSizes): the webviews' own zoom, as a
	// browser's, so the text and everything around it grow together.
	TextSize int `json:"textSize,omitempty"`
	// UIFont and CodeFont are this computer's installed faces. Nil keeps
	// the platform's stack (or Omarchy's). Sync never replaces them.
	UIFont   *fonts.Face `json:"uiFont,omitempty"`
	CodeFont *fonts.Face `json:"codeFont,omitempty"`
	// How the agents are listed, by agent id. AgentOrder comes first, as
	// ordered; an agent it doesn't name (one installed since) follows in
	// magpie's own order. A hidden agent is folded away at the bottom of the
	// list; a shown one stays in view even while nothing is set on it, which
	// otherwise folds it away too. The agents' own files never hear of it.
	AgentOrder   []string `json:"agentOrder,omitempty"`
	AgentsHidden []string `json:"agentsHidden,omitempty"`
	AgentsShown  []string `json:"agentsShown,omitempty"`
	// UsageOrder is how the Usage page's cards are listed, by provider id,
	// as they were dragged there; one it doesn't name follows in magpie's
	// own order. Only the page's: the order providers are tried in is the
	// Providers page's.
	UsageOrder []string `json:"usageOrder,omitempty"`
	// PanelUsageHidden are the subscriptions, by provider id, that the tray
	// panel's Allowances tab leaves out (H20 on Discord). It is what the
	// panel shows, nothing more: routing, caps, the Usage page and the menu
	// bar's cells still have them. The panel's order is UsageOrder.
	PanelUsageHidden []string `json:"panelUsageHidden,omitempty"`
	// UsageAgentsHidden are the clients, by the id the ledger records them
	// under ("curl", "Go-http-client", an agent's id), that the Usage page's
	// Agents list leaves out (ikunbots on X). It is that list's only: the
	// ledger keeps their calls, and the totals and the chart count them.
	UsageAgentsHidden []string `json:"usageAgentsHidden,omitempty"`
	// ClientAgents counts a client magpie doesn't know as one of the agents
	// it does, on the Usage and Sessions pages (#1958, NorethSea): a bridge
	// that starts Codex's app-server under a name of its own
	// ("hermes-codex-thread-bridge") is counted as Codex. Keyed by the
	// client's name as the ledger records it, lower-cased, to the agent's
	// id. Only what the pages report: the ledger keeps the client's own
	// name, and routing, keys, caps and budgets go on by it. A pair taken
	// out counts the client on its own again, the history with it.
	ClientAgents map[string]string `json:"clientAgents,omitempty"`
	// Visible narrows the models an agent is shown, by agent id: the
	// families (the tag a provider or group is given), provider ids and
	// group ids its lists hold. An agent it doesn't name is shown them all.
	Visible map[string][]string `json:"visible,omitempty"`
	// HiddenModels are the catalog entries (their ids, "<provider>/<model>"
	// or a group's) taken out of an agent's lists one by one, by agent id,
	// after Visible: a model not named here, a new one among them, is shown.
	HiddenModels map[string][]string `json:"hiddenModels,omitempty"`
	// DisabledModels are models turned off for every request through magpie
	// (#1867), each an exact id or a glob ("*fable*"), matched ignoring case
	// against "<provider>/<model>" and the bare model id: they are left out
	// of every list, and the gateway refuses them on every route.
	DisabledModels []string `json:"disabledModels,omitempty"`
	// PickedModels, for an agent it names, are the only catalog entries its
	// lists show, after Visible (#1337): the user switched the agent to
	// "only models I pick", so a model that comes later, of a new provider
	// or an old one, is not shown until it is ticked. HiddenModels is not
	// read for such an agent. An agent named with no entries is shown none.
	PickedModels map[string][]string `json:"pickedModels,omitempty"`
	// OwnModelsHidden are the agents, by id, whose pickers leave out the
	// models the agent reaches on its own, not through magpie (DeepSeek
	// Harness's own DeepSeek models, #1642): only magpie's are offered, and
	// one the agent is set to stays.
	OwnModelsHidden []string `json:"ownModelsHidden,omitempty"`
	// AgentEfforts are the reasoning efforts the gateway asks for on an
	// agent's requests, by agent id, for an agent whose own config can't
	// carry one (Cursor Private Inference, #1003): one of the levels in
	// provider.MemberEfforts.
	AgentEfforts map[string]string `json:"agentEfforts,omitempty"`
	// OrderedModels is the order an agent's lists put its models in, by
	// agent id and then entry id, as the user dragged them on the Agents
	// page (Codex's, #855): the ones named first, any other after them.
	OrderedModels map[string][]string `json:"orderedModels,omitempty"`
	// FastPicks are the catalog entries ("<provider>/<model>") an agent's
	// picks of are sent in their vendor's fast mode, by agent id (#954):
	// switched by the model in the agent's picker, as a routing group's
	// member is (Group.Fast), without making a group of it.
	FastPicks map[string][]string `json:"fastPicks,omitempty"`
	// UltraPicks are the fast picks an agent's requests for are sent as
	// Ultrafast instead (#1954), by agent id: each is in FastPicks too. An
	// account that doesn't offer Ultrafast on the model is sent priority.
	UltraPicks map[string][]string `json:"ultraPicks,omitempty"`

	// The three maps below, and every one added beside them, are the
	// per-model ones: a field named Model* whose type is a map[string]X,
	// keyed "<provider id>/<model id>" (or "<provider id>/*", for all of
	// that provider's models — CheckModelKey). That is the whole of the
	// convention, and it is what RenamePerModel and the GUI's saving of
	// the settings go by, each of them walking the fields by it rather
	// than by a list kept up to date by hand: a map added here later is
	// moved when a provider is renamed and kept when another page saves
	// the settings, with no line written for it in either place. A field
	// whose keys are not a model's is not one of these, and is not named
	// Model* — Visible, which is by agent id, among them.
	// ModelNames are the names the user gave models, by "<provider
	// id>/<model id>": agents, the gateway's model list and magpie itself
	// show them for the vendor's (see provider.SetModelName).
	ModelNames map[string]string `json:"modelNames,omitempty"`
	// ModelEfforts are the reasoning levels the user keeps of a model's,
	// by "<provider id>/<model id>": the lists magpie hands out offer only
	// those, while the vendor is still asked at any level it has; for a
	// model with no levels known they are the levels given it, and the
	// gateway fits a request to them (see provider.SetModelEfforts,
	// magpie model spec).
	ModelEfforts map[string][]string `json:"modelEfforts,omitempty"`
	// ModelReasonless are the models the user said take no reasoning ask,
	// by "<provider id>/<model id>": magpie offers them no levels and
	// sends them no reasoning field, on any API, though their list says
	// they think (see provider.SetModelReasonless; yuexian7 on Discord: a
	// model models.dev lists with reasoning answered reasoning_effort
	// with 422).
	ModelReasonless map[string]bool `json:"modelReasonless,omitempty"`
	// ModelImages is whether the user said a model takes images, by
	// "<provider id>/<model id>". Absent leaves it to the vendor's list.
	ModelImages map[string]bool `json:"modelImages,omitempty"`
	// ModelPrices is what a model costs the user, in USD per million
	// tokens, by "<provider id>/<model id>", and "*" for every model of
	// that provider: a provider models.dev does not list, or one that
	// resells at a multiplier, is otherwise priced at whatever its maker's
	// list price is. "*/<model id>" (AnyProvider) is that model from any
	// provider not priced above, and a session's model named with no
	// provider at all: one whose provider is gone, or one models.dev no
	// longer lists.
	ModelPrices map[string]ModelPrice `json:"modelPrices,omitempty"`
	// ModelOutputs is the most a reply of a model may hold, by "<provider
	// id>/<model id>", and "*" for every model of that provider. Absent
	// leaves it to the vendor's own list and to models.dev, as a context
	// the user has not set is. A model's own value wins over its
	// provider's, and the agents' own files are told of either
	// (see provider.SetModelOutput).
	ModelOutputs map[string]int `json:"modelOutputs,omitempty"`
	// ModelCompacts is where Codex and Claude Code compact a conversation
	// on a model, by "<provider id>/<model id>", and "*" for every model
	// of that provider (#876): the window they are told when the model's
	// own is longer, over CompactAt and FullContext. One at or above the
	// model's window is its whole window.
	ModelCompacts map[string]int `json:"modelCompacts,omitempty"`
	// ModelWires is the name to send a vendor for a model magpie knows by
	// another, by "<provider id>/<model id>", and "*" for every model of that
	// provider. A "*" in the name is the model itself, so one name covers a
	// relay that namespaces its models — vendor-c/* asks for model-3 as
	// vendor-c/model-3 — while a name with no "*" in it sends every model of
	// that key under that one name. Everything else — the catalog agents see,
	// the routing groups, the usage records and what a call is priced at —
	// keeps the name magpie knows the model by.
	ModelWires map[string]string `json:"modelWires,omitempty"`
	// ModelAPIs is the one API a model is asked on at its provider, by
	// "<provider id>/<model id>": chat, responses or anthropic, for a relay
	// whose one key serves some models on one and others on another
	// (01huadalang on Discord). Absent leaves it to the vendor's list and
	// to each URL the provider has (see provider.SetModelAPI).
	ModelAPIs map[string]string `json:"modelAPIs,omitempty"`
	// ModelSameAs is the model another vendor sells under another name
	// that a model is, by "<provider id>/<model id>": the routing groups
	// magpie finds (provider's autoGroups) merge it with that one rather
	// than by its own id, for an id no rule of magpie's matches up
	// (kyzhouxu, #583), and a call to it that no list prices is costed at
	// that model's maker's price (provider.SamePriceIn, #2344). Absent
	// leaves it to its id.
	ModelSameAs map[string]string `json:"modelSameAs,omitempty"`
	// UsageAliases are the model ids the user counts as one in Usage and
	// Sessions, from an id to the name it is counted under
	// ("deepseek-v4.1-flash": "deepseek-flash", SlouchyQuill507 on
	// Discord): what spent the same model under two names is one row there.
	// Only how usage is grouped and shown changes: no request, model list
	// or price reads it (provider.SetUsageAlias). Keyed by a bare model id,
	// not "<provider>/<model>", so it is not named Model* and is not one
	// of the per-model maps a provider's rename moves. Absent is none.
	UsageAliases map[string]string `json:"usageAliases,omitempty"`
	// ModelKinds is what the user said a model makes, by "<provider
	// id>/<model id>": "image" or "video", for a provider set up with a
	// base URL whose own model list doesn't say so (shen-joe, #1399).
	// Absent leaves it to the vendor's list and the model's name (see
	// provider.SetModelKind).
	ModelKinds map[string]string `json:"modelKinds,omitempty"`
	// The main window's size as it last settled, width and height, so it
	// opens at it again after a restart: the size it is restored to, kept as
	// it was while it is maximised, and a side fitted to a smaller screen
	// keeps the larger one (gui's settle).
	Window []int `json:"window,omitempty"`
	// WindowMaximised: the main window was maximised (zoomed, on the Mac)
	// when it last settled, so it opens maximised again; Window is still the
	// size it is restored to.
	WindowMaximised bool `json:"windowMaximised,omitempty"`
}

Settings is what the user chose. "" and "system" both mean "follow the OS".

func Load

func Load() Settings

Load reads the settings; anything missing or unreadable is the default.

func LoadChecked added in v0.1.1214

func LoadChecked() (Settings, error)

LoadChecked is Load, and an error when what the file holds is unknown: it is there but can't be read, or it doesn't parse and has no last good generation to read instead. The Settings returned then are Load's (the defaults), which a caller guarding something with a setting (the gateway's allow list) must not take for the user's own: a failed read is unknown, never empty. No file, or an empty one, is no settings and not an error — the cases Save writes over, where it refuses the others.

func Shared added in v0.1.1306

func Shared() Settings

Shared is Load without a copy of its own: the settings every caller of Shared is given for the same settings.json, for a look made on each gateway request (redaction, wires, efforts, images). Only for reading: a caller never writes into its maps or slices (append reallocates, as each slice is held at its length), and one that changes anything takes Load's copy instead. Unreadable, it is Load's defaults as they are.

func (Settings) ClassifyWait added in v0.1.1332

func (s Settings) ClassifyWait() time.Duration

ClassifyWait is how long a classifier is given to answer: ClassifyTimeout's seconds, ClassifyTimeoutDefault's when it is 0 (or below), and no more than ClassifyTimeoutMost's when settings.json was written by hand past it.

func (Settings) Compact added in v0.1.979

func (s Settings) Compact() int

Compact is where a conversation on a longer window is compacted when neither its model nor its provider says (ModelCompacts): CompactAt, else WorkingWindow, and 0 under FullContext, the model's whole window.

func (Settings) ImageWait added in v0.1.1294

func (s Settings) ImageWait() time.Duration

ImageWait is how long a vendor is given to draw an image and send it: ImageTimeout's minutes, ImageTimeoutDefault's when it is 0 (or below), and no more than ImageTimeoutMost's when settings.json was written by hand past it.

func (*Settings) KeepOwn added in v0.1.601

func (s *Settings) KeepOwn(cur Settings)

KeepOwn puts back cur's settings that are this computer's own, which a sync or a restored backup never brings from another: the window's size and whether it was maximised, the proxy, the gateway's port, whether it is shared on the local network and the addresses let in to it, the Dock, gateway mode, whether WSL is looked in, whether agents' sign-ins are read, when allowances are read, the terminal app a session resumes in, whether the local masking model is on (it is downloaded on this computer alone), how many Claude Code processes may run (its memory), and what the menu bar or tray shows beside magpie's icon (yoooo on Discord: usage turned off on a Mac came back from a Windows box that shows it; Lullaby: sharing turned on at home came on at work).

func (Settings) LightweightOn added in v0.1.1298

func (s Settings) LightweightOn() bool

LightweightOn says whether lightweight mode is on: only when the user turned it on. It was on for a user who never chose, since a hidden webview kept for good holds what its page drew while hidden (~20 MB a minute on Windows under steady requests, #2515), but each opening then waited for the page to load again.

func (*Settings) RenamePerModel added in v0.1.496

func (s *Settings) RenamePerModel(from, to string) bool

RenamePerModel moves what the user said of a provider's models to the id it has now: in every per-model map (see ModelNames) each key beginning with from+"/" is rewritten to to+"/", and it says whether any key moved at all. A provider that changes its id keeps the names, levels, image answers and everything else given to its models, each map moved whether or not the ones before it moved anything.

func (Settings) Working added in v0.1.836

func (s Settings) Working(n int) int

Working is the context window an agent is told for a model with one of n tokens (see FullContext).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL