agent

package
v0.1.1228 Latest Latest
Warning

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

Go to latest
Published: Oct 13, 2026 License: MIT Imports: 62 Imported by: 0

Documentation

Overview

Package agent describes every coding agent magpie can drive: where its config lives, which fields matter (model, effort, …) and which values to offer.

Index

Constants

View Source
const (
	TuneCompact  = "compact"            // when the agent compacts its conversation, in tokens
	TuneCache    = "cache_ttl"          // Claude Code: the main conversation's cache, 5m or 1h
	TuneSubCache = "subagent_cache_ttl" // Claude Code: its subagents', workflows' and helpers'
)

The settings tune knows.

View Source
const CindyScheme = "cindy://provider/import?"

CindyScheme begins every Cindy import link.

View Source
const CursorLocalID = "cursor-local"

CursorLocalID is the agent's id, which its key names (TokenFor).

View Source
const DryRunArg = "agent-disconnect-dry-run"

DryRunArg is the command line's first word for the process that disconnects an agent on a copy of its files (main.go).

View Source
const RoutingGroups = "Routing groups"

RoutingGroups is the picker's group of routing groups.

Variables

View Source
var Running = func(patterns ...string) bool {
	if runtime.GOOS == "windows" {
		return len(patterns) > 0
	}
	for _, pat := range patterns {
		if err := proc.Command("pgrep", "-f", pat).Run(); err == nil {
			return true
		}
	}
	return false
}

Running reports whether a process whose command line matches any pattern (an extended regexp, as for pgrep -f) is alive. Windows can't be asked what runs, so anything may be: every caller is the advice an agent's own lists need after magpie changed what it reads at start ("restart Codex", "open a new dsh session"), and a Windows that answered no here dropped that advice silently — a model picked in magpie looked like it had done nothing at all. claudeRunning and Pencil's own check already say they can't be told, and say yes for the same reason. A var so tests can say what runs, as claudeRunning is: magpie's own CLI tests printed "restart the Codex app" on a Mac with Codex open, and on every Windows.

Functions

func AgyLaunch added in v0.1.404

func AgyLaunch(model string) string

AgyLaunch is the command that starts agy on magpie's gateway and one of its custom models, in the shell of this system. Its key names agy: its requests go out as Google's Gemini SDK's (User-Agent google-genai-sdk/…), with nothing of agy's own, so the gateway knows them by the key (x-goog-api-key) alone. A command copied before, with the plain token, still reaches magpie.

func CLIs added in v0.1.397

func CLIs(wait time.Duration) (out map[string]CLI, pending bool)

CLIs is every detected agent's CLI, as far as it is known within wait; pending says some are still being asked, and will be ready next time.

func CindyLink(url string) string

CindyLink is the link that adds the gateway at url to Cindy as the provider Magpie, keyed so that its requests are counted as Cindy's.

func CodexDaemonRestarted added in v0.1.1151

func CodexDaemonRestarted()

CodexDaemonRestarted forgets the daemon last found behind, once the user has restarted it.

func CodexOwnOnly added in v0.1.1138

func CodexOwnOnly() bool

CodexOwnOnly reports whether the Codex that asks the gateway for its model list there (/backend-api/codex/models) reaches magpie for account failover alone (FailingOver), to be handed only its own models (#1385). That is this machine's Codex; a request doesn't say whether it came from a Codex in a WSL distro instead, so one there connected to magpie (what magpie set on it, or its joined/beside mark in the stash) keeps magpie's models in the list. No distro is probed for it.

func CodexSubAgentsV1Warning added in v0.1.1224

func CodexSubAgentsV1Warning() string

CodexSubAgentsV1Warning is codexSubAgentsV1Warning for the Codex config here, as the Settings row asks it (the page can't reach the config itself). "" when there is no Codex, or no conflict.

func CursorLocalLaunch added in v0.1.877

func CursorLocalLaunch(app, gw string) string

CursorLocalLaunch is the command that starts the build at app on the gateway at gw, in the shell of this system. Started so, from a shell, the app has the variables; opened from the Dock or Finder it has only the login shell's.

func DesktopConfigs3p added in v0.1.1156

func DesktopConfigs3p(home string) []string

DesktopConfigs3p are the claude_desktop_config.json files Claude Desktop reads in its 3p mode: there its whole userData is Claude-3p, its MCP servers too (%LOCALAPPDATA%\Claude-3p on Windows, and the MSIX package's; Claude-3p beside Claude elsewhere).

func DryRun added in v0.1.780

func DryRun(id string) error

DryRun disconnects an agent in the process DisconnectPreview starts, on the copy under its temporary home.

func DshPatchFiles added in v0.1.182

func DshPatchFiles(dir string) []string

DshPatchFiles are the patch lists of the dsh whose home is dir, as magpie writes them: every profile's (web's first), else config.yaml.

func EndCodexFailover added in v0.1.1193

func EndCodexFailover() error

EndCodexFailover takes account failover out of this machine's Codex as the magpie serving its gateway quits (EndFailover): Codex goes straight to OpenAI again rather than retrying a gateway that's gone. The app-server codex sessions share read the old base URL as it started and keeps it, and KeepCodexDaemonCurrent ends with magpie, so it is restarted here, when no session is on it (thisisvainxx on X: back on its own config, Codex took "several restarts").

func FakeWSL added in v0.1.512

func FakeWSL(probes, roots map[string]string) func()

FakeWSL, for other packages' tests, stands in for wsl.exe on any system: each distro named in probes is installed and running, answers the probe with what probes has for it (wslProbeScript's output: "home:/ home/me\ndir:.codex\n…") and is opened at roots' folder for it. What magpie remembers of WSL is forgotten, before and after; the returned func puts everything back.

func KeepCodexDaemonCurrent added in v0.1.1151

func KeepCodexDaemonCurrent(ctx context.Context)

KeepCodexDaemonCurrent restarts this machine's Codex daemon whenever it is behind what Codex reads at start and no codex session is on it, looking every so often until ctx ends.

func KeepCursorLocalEnv added in v0.1.885

func KeepCursorLocalEnv(context.Context)

KeepCursorLocalEnv sets the variables again when the gateway is served, with its address now, while Cursor Private Inference is connected: the Mac's launchctl ones are gone after a restart, and the port may have changed. The app takes them when it is next opened.

func KeepDshWired added in v0.1.641

func KeepDshWired(ctx context.Context)

KeepDshWired keeps magpie's route in dsh's patch lists as magpie's catalog is now, for as long as magpie serves the gateway. dsh reads a patch list live, so one left behind by something else writing the file — dsh's own Models page, an older magpie — fails every session there with no such configured model until the route is written again. What is written is magpie's own list, never the model a session starts on.

func KeyAt added in v0.1.1159

func KeyAt(gw string) string

KeyAt is keyAt, for what else magpie gives an agent that reaches the gateway at gw (the library's MCP servers relayed through it).

func KimiDir added in v0.1.470

func KimiDir(home string) (dir string, legacy bool)

KimiDir is the folder Kimi Code keeps its config.toml in, and whether it is the old kimi-cli's. kimi-cli (1.50 on) hands over to the new Kimi Code, which reads only ~/.kimi-code: it offers to copy ~/.kimi's config over when it first starts, and once that's done never reads ~/.kimi again, so a model written there is one it never sees (#290). ~/.kimi is kimi-cli's only where the new one isn't: no ~/.kimi-code, nor $KIMI_CODE_HOME.

func MiniMaxDir added in v0.1.535

func MiniMaxDir(home string) string

MiniMaxDir is the folder MiniMax Code keeps its config.yaml in.

func ModelIcon added in v0.1.466

func ModelIcon(model string) string

ModelIcon is the logo of a model's maker, known by its id's (or name's) family alone; "" when it isn't one of them.

func MoveAntigravityEfforts added in v0.1.299

func MoveAntigravityEfforts()

MoveAntigravityEfforts does the same for Antigravity's ids at a level (antigravity/gemini-3.7-flash-high → antigravity/gemini-3.7-flash, at high when the agent's effort is unset and it has that one).

func MoveCursorEfforts added in v0.1.297

func MoveCursorEfforts()

MoveCursorEfforts moves an agent set to one of Cursor's ids at an effort (cursor/grok-4.7-low), from before magpie offered each family as one model, to that model (cursor/grok-4.7) spelled the same way, and — when the agent's effort is unset and it has that one — to the effort the id was at. A model the picker doesn't offer is left: the gateway still takes Cursor's id. It runs at start-up; errors are swallowed as RenameLegacy's.

func MoveOffAccountIDs added in v0.1.399

func MoveOffAccountIDs()

MoveOffAccountIDs moves each of the user's providers saved on a subscription's id (provider.OnAccountIDs) to a free one, the agents on it with it, so the subscription is listed once signed in: a "WorkBuddy" key saved as workbuddy before WorkBuddy's plan was a subscription hid it, and signing in to it said "signed in, but magpie can't list it". Run at start-up; errors are swallowed as RenameLegacy's are.

func Newer added in v0.1.397

func Newer(a, b string) bool

Newer says whether version a is after b: by their numbers, then a release after its pre-releases (1.0.87 after 1.0.87-0).

func Notices added in v0.1.1171

func Notices() []string

Notices are the templates of every notice magpie's own agents give.

func Own added in v0.1.1199

func Own(o Option) bool

Own reports whether o is a model the agent reaches on its own, not one of magpie's catalog: what Picks leaves out on the user's word.

func PluginAgentOf added in v0.1.1161

func PluginAgentOf(spec string) string

PluginAgentOf is the id of the agent the plugin spec adds, "" for none.

func PluginErrors added in v0.1.1161

func PluginErrors() map[string]string

PluginErrors is why an agent a plugin adds isn't listed, by plugin spec: its module didn't load, or its id is a built-in agent's.

func PluginPath added in v0.1.1178

func PluginPath(p string) string

PluginPath is a path an agent plugin gives, as it reads its config's: ~ and $VAR expanded against this user's home.

func Rekey added in v0.1.1169

func Rekey(as []*Agent) (moved []string, err error)

Rekey sets again those of as, taken by OnGateway before LAN sharing was turned on or off or its key made anew, that reach the gateway from beyond loopback (an agent in a WSL distro under NAT): the key they send there is the sharing key while sharing is on (keyAt), so it changed with it. One connected while nothing was shared kept gateway.Token, which the gateway turns away from beyond loopback, after sharing was turned on for it.

func RenameLegacy

func RenameLegacy()

RenameLegacy rewrites what an older dial left in the agents' files so it says magpie: a model spelled dial/… becomes magpie/… (which also writes the magpie provider entry the agent needs), and then the dial entry, Codex's table and catalog file, and Claude Code's token go. It runs at start-up; once nothing says dial any more it changes nothing. Errors are swallowed on purpose — a file magpie cannot rewrite is one the user can still fix from the picker.

func RenameProvider added in v0.1.125

func RenameProvider(from, to string) ([]string, error)

RenameProvider gives a provider another id (provider.Rename) and moves the agents on one of its models to the same model by the new id, each spelled the way the agent spells it, and what else in their configs names it (RenameRefs). It answers the agents it moved.

func Rescan added in v0.1.937

func Rescan()

Rescan forgets what was found of the agents on this machine a while ago, so the next read of them looks again at once: the reader asked for it with the Agents page's refresh (#844), having just installed an agent, or removed one, or changed its settings outside magpie. What is read from an agent's files every time needs nothing forgotten; these are the answers kept for a while because asking was slow (the apps folder, wsl.exe, Alma's server), and where each command was found on PATH (proc.LookPath).

func Rewire added in v0.1.950

func Rewire(as []*Agent) (moved []string, err error)

Rewire puts the gateway's address now into the configs of agents, taken by OnGateway before it moved: each one whose wiring is off now is set again, an agent joined to magpie beside its own models (Codex signed in with ChatGPT) joined again. It says the names of those it moved, and what one it couldn't move failed with. Cursor Private Inference's variables and dsh's routes, which magpie keeps on its own, follow too.

func StandIn added in v0.1.241

func StandIn(agent, model string) string

StandIn is the model Claude Code is set to use in place of one it named that magpie doesn't serve: claude-haiku-4-5-… for a title or a small task goes to its haiku tier's model, and a name of no tier to its main model. For Codex, the model it is set to (codexStandIn). "" when the agent isn't routed through magpie or is neither. For gateway.StandIn.

func StopFakeWSL added in v0.1.1080

func StopFakeWSL(name string)

StopFakeWSL, for other packages' tests, has FakeWSL's distro name stop, as the user stopping it (wsl --shutdown) does: wsl.exe lists it as not running from now on.

func SyncCatalog added in v0.1.54

func SyncCatalog()

SyncCatalog brings every model list magpie wrote into an agent's files up to the catalog as it is now. It is what catalog.Changed is set to, so a provider saved or removed, or a vendor's list fetched anew, reaches the agents; one already running asks for another round rather than waiting. Errors are left for the picker: a file magpie can't write now is one the next change, or a pick, writes.

func Taken added in v0.1.469

func Taken(p string) bool

Taken reports whether something that isn't a folder is where the folder p, or one it is in, would be: nothing can be written under it.

func TuneApply added in v0.1.1158

func TuneApply(home, agent, knob, model, value string) error

TuneApply puts value in as an agent's setting, on the user's say. What was there before is kept, the first time only, for TuneUndo. A variable that takes precedence is an error: the setting would change nothing.

func TuneUndo added in v0.1.1158

func TuneUndo(home, agent, knob, model string) error

TuneUndo puts back what a setting was before tune put one in, unless the user has changed it since: then it is theirs, and only forgotten.

func WSLRunning added in v0.1.1080

func WSLRunning(distro string) bool

WSLRunning is whether the WSL distro runs now (wslUp); false off Windows. What another package does in a distro on its own, rather than at the user's asking, asks it first.

Types

type AdminError added in v0.1.1174

type AdminError struct {
	Agent, Prefix, Command, Via string
	Canceled                    bool
	Err                         error
}

AdminError is an install or update that needs an administrator and didn't get one: the prompt declined (Canceled), or none to be had (Err). The command never ran. Agent is what it installs (an agent, or RTK); Prefix the folder only an administrator can change, "" when not known; Via the tool, npm or winget, whose fix is said.

func (*AdminError) Error added in v0.1.1174

func (e *AdminError) Error() string

func (*AdminError) Unwrap added in v0.1.1174

func (e *AdminError) Unwrap() error

type Agent

type Agent struct {
	ID      string
	Name    string
	Icon    string // bundled icon name (internal/gui/assets/icons)
	Aliases []string
	Bin     string // executable name, used for detection
	Dir     string // config directory, used for detection
	Path    string // config file magpie edits
	// Plugin is the spec of the plugin that adds this agent (plugged.go),
	// "" for magpie's own
	Plugin string
	Fields []Field
	Native *NativeConnection
	// Notice, if set, is advice worth showing after a change: agents that
	// read their config once at start-up need a restart to see it.
	Notice func() string
	// UA is what the agent's User-Agent begins with, lower-case: how the
	// gateway tells its requests from others'
	UA []string
	// ListsModels: the agent's own model menu is magpie's models as picked
	// on the Agents page for it (settings.HiddenModels, PickedModels),
	// though no field of its picks among the catalog: the gateway's
	// /v1/models as asked with its key (Cursor Private Inference), or the
	// list magpie writes into its files, magpieModels(id), where its only
	// field puts magpie in its picker (ZCode, Pencil: #1508)
	ListsModels bool
	// NoNames: the agent's own list doesn't name magpie's models as
	// Names in agents' lists says (provider.Labels): magpie hands it the
	// models' ids alone, its format having no name for one (Goose, Hermes,
	// jcode …), it lists no models of magpie's at all (Gemini CLI, Snow),
	// or it reads the gateway's /v1/models itself, whose display_name is
	// the name alone (Empryo, Cursor Private Inference). The setting says
	// so for each one connected, rather than seeming to do nothing.
	NoNames bool
	// Sync, for an agent that reads magpie's models from a file of its own
	// rather than asking the gateway, rewrites that list as the catalog is
	// now — where magpie wrote one; nothing else changes (see SyncCatalog).
	Sync func() error
	// Unwire, for an agent whose fields' default is the agent as installed
	// rather than what it had before magpie (Codex, Claude Code, Gemini
	// CLI), takes magpie out of its config and puts back what the stash
	// kept: the endpoint, provider and model the user had. Disconnect runs
	// it before the fields' defaults.
	Unwire func() error
	// Restore, for an agent whose files magpie copied aside before its
	// first write (beforeMagpie), puts them back as they were where what
	// Disconnect left says the same. Disconnect runs it last.
	Restore func() error
	// Join, for an agent that can have magpie's models in its own list
	// while it stays on the model it was on (Codex signed in with ChatGPT),
	// connects it so, its model left as its own last pick; false where it
	// can't, and Connect then picks one of magpie's.
	Join func() (bool, error)
	// Joined reports an agent Join connected: magpie is in its config
	// though no field is on one of magpie's models.
	Joined func() bool
	// Beside reports an agent set on one of magpie's models beside its own
	// (Codex by the base URL beside its ChatGPT sign-in), now on one of its
	// own written in by the agent: still connected, as joined, though the
	// change is told as drift (#940).
	Beside func() bool
	// OwnVia is the catalog id magpie serves one of the agent's own models
	// by on a sign-in of the user's, which its model field lists as its
	// own rather than as magpie's ("codex/gpt-5.5" for Codex's gpt-5.5),
	// "" for none: Connect keeps the agent on that model through magpie
	// where it can't Join (#940: Codex went to an unrelated model).
	OwnVia func(model string) string
	// Routed reports that the agent's config sends whatever model it
	// names to magpie's gateway (Codex's openai_base_url or magpie as its
	// provider), so a model's name the gateway takes as a routing group
	// is that group's (#750).
	Routed func() bool
	// FailingOver reports an agent that isn't connected whose requests
	// still go through magpie's gateway, for account failover alone (Codex
	// signed in to ChatGPT with more of its accounts on in magpie, #1385):
	// the Agents page says so and what turns it off, and the gateway lists
	// it only its own models.
	FailingOver func() bool
	// EndFailover takes out what account failover wrote, for magpie
	// quitting: left there, the agent would be pointed at a gateway that
	// no longer answers, and retry till magpie is open again (Leon920717
	// on X). The next start's Sync writes it again.
	EndFailover func() error
	// Follow, for an agent whose own picker moves its main model where
	// magpie keeps other settings following it (Claude Code's /model and
	// its tiers), brings those along to the model picked there. Run as the
	// Agents page is drawn.
	Follow func() error
	// RenameRefs, for an agent whose config names magpie's models beyond
	// its fields (omp's other roles and fallback chains), moves those names
	// off provider from onto to, the rest of each kept; it answers whether
	// any moved. The fields themselves are RenameProvider's.
	RenameRefs func(from, to string) (bool, error)
	// Check, for an agent magpie wires in beyond its model field, says what
	// of that wiring is gone while the model is still one of magpie's —
	// something else rewrote the config — or "" when it is all there
	// (see Drift).
	Check func() string
	// LastUsed, for an agent that keeps a log of its own prompts, is when
	// it was last used — each prompt a request the gateway should have
	// seen, so one it didn't went round magpie (see Drift). Zero if unknown.
	LastUsed func() time.Time
	// Reached, for an agent that logs where its model requests go, is its
	// newest one since a time: when, the address it went to, and whether
	// nothing answered there. Zero if there is none.
	Reached func(since time.Time) (at time.Time, to string, refused bool)
	// WSL is the distro an agent inside WSL lives in, "" for this
	// machine's own (see wsl.go).
	WSL string
	// Home is a WSL agent's $HOME in its distro as magpie opens it
	// (\\wsl.localhost\<distro>\home\me), where its other files are; ""
	// for this machine's, and while the distro is stopped: opening it
	// would start it.
	Home string
	// Gateway is the gateway's address as the agent reaches it, a WSL
	// distro's own way to it; nil is gateway.URL.
	Gateway func() string
	// Import, for an app that takes magpie only through an import link of
	// its own, which the user confirms there (Cindy), is that link; the app
	// has no fields magpie sets. Added says whether it has magpie already.
	Import func() string
	Added  func() bool
	// Launch, for an agent that takes the gateway only from its
	// environment (agy), is the command that starts it on magpie, while
	// it is on one of magpie's models; "" otherwise.
	Launch func() string
	// SplitSuffix, for an agent whose model values carry something of its
	// own after the model that varies from value to value (omp's thinking
	// level, "…:max"), splits a value into the model the picker offers and
	// that suffix, "" when there is none. What matches a value against the
	// picker (drift, Reseat, RenameProvider, Spell) matches the model and
	// puts the suffix back after the one it moves to. A mark that is always
	// the same for a model (Claude Code's [1m]) rides on the option instead.
	// one is false for a value that is no one model but a list the agent
	// falls back through (omp's "a,b"): that is the user's own whatever it
	// names, never taken for one of magpie's models nor moved by what
	// matches the picker (RenameRefs moves the names in it).
	SplitSuffix func(v string) (model, suffix string, one bool)
	// Spelled, when set, says whether a value of the agent's fields is
	// spelled as one of magpie's there (prefixed: it starts with
	// "magpie/", its provider in the agent): one that isn't is a model of
	// one of the agent's own providers, never magpie's, even when magpie
	// has a provider of the same name (OpenHanako's own
	// deepseek/deepseek-v4-pro read as magpie's deepseek, #835).
	Spelled func(v string) bool
	// ModelAliases are the names the agent's model takes besides ids
	// (Claude Code's opus, best, opusplan). One that names a field as well
	// is the model's on the command line: magpie claude opus sets the
	// model, where another field's name alone shows that field.
	ModelAliases []string
	// contains filtered or unexported fields
}

Agent is one supported coding agent.

func All

func All() []*Agent

All returns every agent magpie knows about, detected or not: its own, then those plugins add.

func Clients added in v0.1.43

func Clients() []*Agent

Clients is everyone whose requests the gateway knows by name: every agent, detected or not, and the others. It is what the Usage and Routing views draw a request's client with.

func Detected

func Detected() []*Agent

Detected returns the agents present on this machine, in display order.

func Find

func Find(q string) (*Agent, error)

Find resolves a user-typed name (id, alias, or unique prefix).

func OnGateway added in v0.1.950

func OnGateway() []*Agent

OnGateway are the agents connected to magpie whose configs reach the gateway at its address now: none of their wiring is off.

func (*Agent) AppVersion added in v0.1.1123

func (a *Agent) AppVersion() string

AppVersion is the version of the agent's desktop app installed here, "" for an agent without one magpie knows, one not installed, or an Info.plist that doesn't say (a binary one isn't read).

func (*Agent) Apply added in v0.1.91

func (a *Agent) Apply(key, v string) error

Apply sets one of the agent's fields and remembers it as magpie's, so it can be told apart from what something else writes there later. Every setting of a field by the user goes through here. A field magpie set before that this one moves too is magpie's doing as well: Claude Code's Default takes its tiers and subagent model out with magpie's endpoint, which read as changed outside magpie, and the drift kept the row up among the connected ones while it said Not connected (#834).

func (*Agent) CLI added in v0.1.397

func (a *Agent) CLI() (c CLI, ok bool)

CLI is the agent's CLI: its version, the newest, and whether magpie can update it, with its desktop app's version. ok is false for an agent with neither a CLI magpie knows nor its app.

func (*Agent) CLIMissing added in v0.1.936

func (a *Agent) CLIMissing() bool

CLIMissing reports an agent that is here by its settings alone: its folder or config is, its command-line program isn't — not on PATH nor where users' tools go (proc.FindTool), as after an uninstall that left ~/.dsh behind (#843). Only for an agent that is its CLI and that magpie knows an install command for, so the Agents page can offer it again; one whose desktop app is installed (HasApp) is that app's settings, which need no CLI (DeepSeek Harness Desktop, star on Discord).

func (*Agent) Connect added in v0.1.780

func (a *Agent) Connect() error

func (*Agent) ConnectHow added in v0.1.806

func (a *Agent) ConnectHow() (Connection, error)

ConnectHow is Connect, saying how the model was chosen.

func (*Agent) Detected

func (a *Agent) Detected() bool

Detected reports whether the agent seems to be installed or configured.

func (*Agent) Disconnect added in v0.1.682

func (a *Agent) Disconnect() error

Disconnect takes magpie out of the agent's config and puts back what the user had, the Agents page's "Disconnect from magpie" (Fate on Discord: picking the agent's default did it, but nothing said so). Unwire first, for an agent whose default would leave it as installed; then each field still on magpie goes to its default, as picking it does, and so does each one magpie set that reads as magpie set it (an effort). What magpie remembered setting is forgotten.

func (*Agent) DisconnectOffline added in v0.1.972

func (a *Agent) DisconnectOffline() error

DisconnectOffline restores a native agent's saved files only after an explicit offline choice. Legacy callers of Disconnect keep live semantics.

func (*Agent) Disconnected added in v0.1.1162

func (a *Agent) Disconnected() bool

Disconnected reports whether the user disconnected the agent and hasn't connected it since.

func (*Agent) Drift added in v0.1.91

func (a *Agent) Drift() *Drift

Drift says what, if anything, keeps the agent off what magpie set: its config first (wiring, then each field against magpie's record), then — the config being right — whether its latest use actually came through. A field moved from one magpie model to another (the agent's own picker) isn't drift; one moved off magpie is.

func (*Agent) FailoverSaid added in v0.1.1138

func (a *Agent) FailoverSaid() string

FailoverSaid is, for an agent not connected whose requests still go through magpie for account failover alone (FailingOver), what the CLI and the TUI say of it, the Agents page's line in words of their own: why it goes through magpie, and what turns that off (#1385). "" when it doesn't.

func (*Agent) Field

func (a *Agent) Field(key string) *Field

Field looks a field up by key, in any case, as Find looks an agent up (magpie codex Model was written as the model).

func (*Agent) HasApp added in v0.1.1135

func (a *Agent) HasApp() bool

HasApp reports whether the agent's desktop app is installed here: its bundle on macOS (AppVersion), its executable on Windows.

func (*Agent) InstalledVersion added in v0.1.443

func (a *Agent) InstalledVersion() string

InstalledVersion is what the agent's CLI on PATH says its version is: "" for an agent without a CLI magpie knows, one not on PATH, or one that didn't say. Asked once per binary, like CLI's; nothing is fetched.

func (*Agent) Keep added in v0.1.91

func (a *Agent) Keep()

Keep takes the agent's config as it is now: what magpie set before is forgotten, and no longer said to have been changed.

func (*Agent) ListsFor added in v0.1.1051

func (a *Agent) ListsFor() string

ListsFor is whose model lists an agent's are (provider.CatalogFor): its own id's, or, for one in a WSL distro, its Windows twin's (claude for claude@wsl:Ubuntu). A distro's agent is written magpie's models under that id and sends the gateway that id's key, so the models picked for its row are the ones it is shown only when kept under that id too (#927).

func (*Agent) Models added in v0.1.1118

func (a *Agent) Models() []string

Models are magpie's models the agent is set to, as catalog ids (provider/model, group/<id>), each once: the agent's own models aside. Usage stats count them (internal/stats).

func (*Agent) OwnModels added in v0.1.1199

func (a *Agent) OwnModels(cur map[string]string) bool

OwnModels reports whether one of a's pickers that picks among magpie's catalog offers models of the agent's own too, which Picks can leave out.

func (*Agent) Pick added in v0.1.780

func (a *Agent) Pick(key, v string) error

Pick sets a field from the Agents page's model picker. One of magpie's models picked on an agent not connected yet connects it first, as its switch does, then starts it on that model (the owner: the switch, and a model picked in magpie in one click, both): the agent gets magpie's whole list, and Disconnect puts back what it had before.

func (*Agent) Picks added in v0.1.1199

func (a *Agent) Picks(f Field, cur map[string]string) []Option

Picks are the options f's picker offers: all of them, unless the user asked a's pickers to offer magpie's models alone (#1642, MizuIro-H). Then a field that picks among magpie's catalog leaves out the models the agent reaches on its own (DeepSeek Harness's DeepSeek ones), but for its default and the one it is set to.

func (*Agent) Reapply added in v0.1.91

func (a *Agent) Reapply() error

Reapply sets again what magpie set on the agent: what drifted, else its fields as they read now — for a config taken off magpie in a way no check catches. A replaced field brings back the others magpie set with it. Either way the record is renewed, so a use before now no longer counts.

func (*Agent) Source added in v0.1.780

func (a *Agent) Source() string

Source is what an agent not connected to magpie runs on now: "sub" (a Claude subscription), "chatgpt" (a ChatGPT sign-in), "key" (an API key of its own), "providers:N" (N providers of its own); "" when unknown.

With account discovery off (#1535) no sign-in is read for it: Claude Code's account in .claude.json, Codex's auth.json and OpenCode's are left alone, and only what the agent's own settings say is told.

func (*Agent) Spell added in v0.1.41

func (a *Agent) Spell(key, v string) (string, error)

Spell puts a value typed for field key the way this agent spells it: a catalog model is "copilot/gpt-6" in some agents and "magpie/copilot/gpt-6" in others, and either is taken in both. A value the picker offers as is stays, and so does one it doesn't know (a model the agent reaches on its own that isn't listed); a "magpie/…" value the catalog doesn't have is an error rather than a model the agent would ask its own vendor for. The agent's suffix after the model (SplitSuffix) stays as typed, and so does a list of models, the user's own.

func (*Agent) Stale added in v0.1.780

func (a *Agent) Stale() int

Stale is how many copies of the agent are running that started before magpie last changed what it reads at start: they still have the list they started with until reopened. On Windows only the Codex daemon is told.

func (*Agent) StaleCopies added in v0.1.1110

func (a *Agent) StaleCopies() []StaleCopy

StaleCopies are the copies Stale counts. Reopening one kind doesn't end another: on macOS the Codex app keeps running, its app-server with it, when its window is closed (its window-all-closed doesn't quit a packaged app on darwin), an editor's Codex runs until the editor's window is reloaded, and the CLI's managed daemon runs on with ppid 1 until it is restarted itself. So each is told apart for the row to say which one is left. One app's processes (its app-server, its exec-server) are one copy. One run from another home's agent dir (an app-server daemon Codex left running for a CODEX_HOME of its own, under its packages) reads other files, and isn't counted.

func (*Agent) UpdateCLI added in v0.1.397

func (a *Agent) UpdateCLI() (CLI, error)

UpdateCLI brings the agent's CLI up to date the way it was installed, and says what it is afterwards.

func (*Agent) Values

func (a *Agent) Values() map[string]string

Values reads every field.

func (*Agent) Wired added in v0.1.682

func (a *Agent) Wired() bool

Wired reports whether magpie is in the agent's config: a field on one of magpie's models, or on magpie itself (an app whose one setting is magpie as its provider).

type CLI added in v0.1.397

type CLI struct {
	Version string `json:"version,omitempty"`
	// Latest is the newest there is, from where it would be updated:
	// Homebrew's for one Homebrew installed, npm's otherwise
	Latest string `json:"latest,omitempty"`
	// Via is what updates it: npm, bun, pnpm, brew, or self (its own
	// updater); "" when magpie can't tell how it was installed
	Via string `json:"via,omitempty"`
	// Command is the update as a person would type it
	Command string `json:"command,omitempty"`
	// Update: Latest is after Version, and magpie knows how to get it
	Update bool `json:"update,omitempty"`
	// App is the version of the agent's desktop app installed beside it
	// (the Codex app, AppVersion); shown even with no CLI on PATH
	App string `json:"app,omitempty"`
	// Admin: the update needs an administrator, which Windows asks for
	// first (npm's global folder under Program Files)
	Admin bool `json:"admin,omitempty"`
	// Path is where the CLI was found: the file magpie runs, as PATH
	// reached it
	Path string `json:"path,omitempty"`
	// OffPath says the CLI was found in a folder magpie adds to its own
	// PATH (proc.UserPath: ~/.local/bin, npm's prefixes, nvm's default
	// Node, bun, Homebrew, ...) that a terminal opened now doesn't have,
	// so that `which` there doesn't find it. A row of an agent the user
	// believed uninstalled, with a version and nothing to say where it
	// came from, read as magpie making it up (悠悠哥 on Discord: Codex
	// 0.162.1 listed with no codex on PATH and no ~/.codex). False when
	// the terminal's PATH can't be told.
	OffPath bool `json:"offPath,omitempty"`
}

CLI is what magpie knows of an agent's command-line program.

type Connection added in v0.1.806

type Connection struct {
	How   string `json:"how"`
	Field string `json:"field,omitempty"`
	Value string `json:"value,omitempty"`
}

Connection is how Connect chose the model an agent starts on, for the Agents page to say it (#726: Codex showed its last pick, Claude Code a Sonnet through magpie and Antigravity some model, with nothing saying why). How is one of:

  • "kept": it was connected already, left as it was
  • "again": the models it was on when it was switched off, put back
  • "joined": the model it is on stays its own (Codex signed in with ChatGPT), magpie's models join its list
  • "magpie": an app whose one setting is magpie as its provider
  • "same": the model it is on now, through magpie
  • "alike": the model its alias (opus) stands for, through magpie
  • "default": on its own default with no model set: the first of its own models in its list (the newest, for Claude Code), through magpie
  • "first": magpie has none of the model it is on: the first on the account it is signed in to, else a subscription's, else the first

Field and Value are the field set and what it was set to, where Connect picked one model ("" for kept, again and joined).

type DisconnectPlan added in v0.1.972

type DisconnectPlan struct {
	Files    []PlannedFile
	Settings []PlannedSetting
}

func (*DisconnectPlan) CheckFiles added in v0.1.972

func (p *DisconnectPlan) CheckFiles() error

func (*DisconnectPlan) Preview added in v0.1.972

func (p *DisconnectPlan) Preview() []FileChange

func (*DisconnectPlan) Revision added in v0.1.972

func (p *DisconnectPlan) Revision(agent string) string

Revision binds confirmation to file preconditions and restoration values, without accepting a serialized plan from the client.

type Drift added in v0.1.91

type Drift struct {
	// Kind says what is off:
	//   "unwired"  the model is magpie's but the config no longer sends it
	//              through magpie (Check);
	//   "replaced" a magpie model magpie set was replaced by the agent's own;
	//   "bypassed" the config is right, yet the agent was used since and
	//              nothing of it reached the gateway — it runs on an old
	//              config, or something outside the file overrides it;
	//   "unreachable" the config is right, but the address off loopback
	//              it names the gateway at doesn't answer (#1013);
	//   "proxied"  the config is right, but the agent sends its requests
	//              for the gateway through a proxy (Addr) that doesn't
	//              pass them on to it (codex_proxy.go).
	Kind   string `json:"kind"`
	Field  string `json:"field"`         // the field it shows on
	Now    string `json:"now,omitempty"` // what that field says now
	Want   string `json:"want"`          // what setting it again sets
	Detail string `json:"detail"`        // what exactly is off, for a tooltip
	// Addr is where an unreachable agent is pointed; Move, when set, the
	// address WSL reaches Windows at now, which Reapply points it at.
	Addr string `json:"addr,omitempty"`
	Move string `json:"move,omitempty"`
}

Drift is how an agent differs from what magpie set on it.

type Field

type Field struct {
	Key     string
	Label   string
	Get     func() string
	Set     func(string) error
	Options func(cur map[string]string) []Option
	// Quiet fields are left out of listings while empty: they follow
	// another field until set (Claude Code's per-tier models).
	Quiet bool
	// Follows is the key of the field a Quiet one takes after while empty
	// ("model" for Claude Code's tiers and subagents): a profile's details
	// say so, and Drift reads the field as on that one's model (#1050).
	Follows string
}

Field is one tunable setting of an agent. Set with an empty value puts the field back to the agent's own default: anything magpie wired in (the gateway as a provider, its model catalog) comes out and the key is removed.

type FileChange added in v0.1.780

type FileChange struct {
	Path    string     `json:"path"`              // as the user knows it, ~ for home
	Removed bool       `json:"removed,omitempty"` // the file goes
	Created bool       `json:"created,omitempty"` // the file is made
	Lines   []LineDiff `json:"lines,omitempty"`
}

FileChange is how disconnecting changes one of the agent's files.

func DisconnectPreview added in v0.1.780

func DisconnectPreview(a *Agent, exe string) ([]FileChange, error)

DisconnectPreview is what Disconnect would change in the agent's files, found by running it on a copy. An error is a preview not to be had (the agent's files outside home, a WSL agent): the dialog then says it in words.

type Install added in v0.1.801

type Install struct {
	ID       string       `json:"id"`
	Name     string       `json:"name"`
	Icon     string       `json:"icon,omitempty"`
	Commands []InstallCmd `json:"commands"`
	// Missing: the agent's settings are here but its CLI isn't (CLIMissing),
	// so it is listed on the Agents page and offered here again too
	Missing bool `json:"missing,omitempty"`
}

Install is an agent not on this machine, and how to install it.

func Installs added in v0.1.801

func Installs() []Install

Installs is every agent magpie knows a command for that isn't on this machine, in the Agents page's order.

type InstallCmd added in v0.1.801

type InstallCmd struct {
	// Via is what runs it: script (the vendor's installer, in a shell),
	// powershell (the vendor's installer, on Windows), brew or npm
	Via     string `json:"via"`
	Command string `json:"command"`
	// Node is how the command installs Node.js before npm, where none is
	// here: nvm (Linux), nvm-mac (a Mac, where nvm's installer wants the
	// Xcode Command Line Tools) or winget (Windows); "" when it doesn't
	Node string `json:"node,omitempty"`
	// Admin is npm's global folder, for an npm command run where only an
	// administrator can change it (Windows' Program Files); "" otherwise
	Admin string `json:"admin,omitempty"`
}

InstallCmd is one way to install an agent's CLI.

type LineDiff added in v0.1.780

type LineDiff struct {
	Op   string `json:"op"`
	Text string `json:"text"`
	Was  string `json:"was,omitempty"`
}

LineDiff is a line of a file disconnecting changes: Op is "-" for a line taken out, "+" for one put in, "~" for a line whose value goes back to another (Was is the line now).

type Move added in v0.1.397

type Move struct {
	Agent string `json:"agent"` // the agent's name
	Field string `json:"field"` // model, or a tier (Claude Code's haiku …)
	From  string `json:"from"`  // the catalog model it was on
	To    string `json:"to"`    // the one it is on now; "" its own default
	// Error is why the agent couldn't be moved (its file unwritable, say):
	// it stays on From, which magpie no longer serves
	Error string `json:"error,omitempty"`
}

Move is one of an agent's fields moved off a model magpie no longer serves.

func Reseat added in v0.1.397

func Reseat(change func() error) ([]Move, error)

Reseat makes a change to the providers (change) and moves every agent whose model it stopped serving: to that model from another provider that serves it, else to the agent's own default. It answers the moves. Its error is the change's alone: once the change is made, an agent that can't be moved is a move with its Error and the others are still moved. An error then would say the change failed when it was made — the panel, told so, kept a removed provider's editor open and listed, and a second Remove said there was no such provider.

func (Move) String added in v0.1.397

func (m Move) String() string

String is the move as the CLI and the TUI say it.

type NativeConnection added in v0.1.972

type NativeConnection struct {
	Read           func() NativeState
	Connect        func() error
	Apply          func(key, value string) error
	Stage          func(key, value string) error
	Disconnect     func() (*DisconnectPlan, error)
	Execute        func(*DisconnectPlan) error
	ExecuteOffline func(*DisconnectPlan) error
}

NativeConnection registers a provider independently of model selection. Its plan describes file and runtime changes without performing either.

type NativeField added in v0.1.972

type NativeField struct {
	Value  string `json:"value"`
	Status string `json:"status"`
	Detail string `json:"detail,omitempty"`
}

type NativeState added in v0.1.972

type NativeState struct {
	Provider string                 `json:"provider"`
	Detail   string                 `json:"detail,omitempty"`
	Runtime  string                 `json:"runtime"`
	Fields   map[string]NativeField `json:"fields"`
}

type NoModelsError added in v0.1.1024

type NoModelsError struct{ Agent string }

Connect puts magpie into the agent's config, the Agents page's switch (the owner: an agent is connected to magpie, and its models are picked in the agent): the agent's main field goes to one of magpie's models — the one serving the model it is on now, else the first — or to magpie itself for an app whose one setting is magpie as its provider. Setting it writes the provider and the whole catalog in, so the agent's own model list has every one of magpie's models. An agent that can keep the model it is on (Join) keeps it. An agent already connected is left as it is. NoModelsError is Connect's answer while magpie has no models to give the agent: no provider or subscription has been added yet. The GUI says it in the reader's language by its code (no_models).

func (*NoModelsError) Error added in v0.1.1024

func (e *NoModelsError) Error() string

type OfflineAction added in v0.1.972

type OfflineAction string
const (
	OfflineStage      OfflineAction = "stage"
	OfflineDisconnect OfflineAction = "disconnect"
)

type Option

type Option struct {
	Value string   `json:"value"`
	Label string   `json:"label,omitempty"` // display name, when the value is an id
	Note  string   `json:"note"`
	Icon  string   `json:"icon,omitempty"`  // bundled icon name
	Icons []string `json:"icons,omitempty"` // a routing group's providers' icons, stacked
	Group string   `json:"group,omitempty"` // section header in the picker
	// GroupIcon is the group's own logo in the picker's rail, when it is
	// not the first model's (a provider serving other vendors' models)
	GroupIcon string `json:"groupIcon,omitempty"`
	Ref       string `json:"ref,omitempty"`  // the catalog model, the same in every agent
	Free      bool   `json:"free,omitempty"` // costs its subscription nothing
	// Rate and RateWas are the credits a request costs its subscription,
	// as a multiple, and before a discount running now (Qoder's 0.5×,
	// WorkBuddy's x0.03), when its vendor lists them
	Rate    float64 `json:"rate,omitempty"`
	RateWas float64 `json:"rateWas,omitempty"`
	// Context is the tokens the model takes, when known; the picker marks
	// the large ones
	Context int `json:"context,omitempty"`
	// Takes, on an effort field's default (Value ""), is the level the
	// agent takes for the model with none set (Codex: the catalog entry's
	// default_reasoning_level), shown beside "default"
	Takes string `json:"takes,omitempty"`
	// Direct names who the agent asks for this model itself, on its own
	// sign-in or key, with magpie not in the way ("Anthropic"): its config
	// then names no magpie endpoint, which is right, not a failed setup
	Direct string `json:"direct,omitempty"`
	// Via is a model of the agent's own it asks through magpie's gateway,
	// on its own sign-in all the same (Codex's while routed by its base
	// URL): the picker says via magpie by it, as by a catalog model's
	Via bool `json:"via,omitempty"`
	// Same is a model magpie serves on the very account the agent is
	// signed in to itself, so the agent reaches it on its own too: the
	// picker folds these into one row a click opens (Claude Code, #496)
	Same bool `json:"same,omitempty"`
	// Alias is the value of the option a dated id is another name of
	// (claude-opus-4-5-20251101 → claude-opus-4-5): the picker shows one
	// row for the two, the alias, unless the dated one is the value set
	Alias string `json:"alias,omitempty"`
	// FastFor is the agent a catalog model with a fast mode (provider.
	// CanFast) is switched fast or not for, by the model in the picker
	// (#954): the one whose requests the gateway sends it on; Fast is
	// whether it is now (provider.IsFastPick)
	FastFor string `json:"fastFor,omitempty"`
	Fast    bool   `json:"fast,omitempty"`
	// OwnFast: its fast mode is a GPT model's on a provider the user added
	// by its address (provider.OwnFast, #1615), asked for there as OpenAI's
	// priority, which the provider may or may not serve
	OwnFast bool `json:"ownFast,omitempty"`
	// contains filtered or unexported fields
}

Option is one value the picker offers for a field.

type PlannedFile added in v0.1.972

type PlannedFile struct {
	Path   string
	Before []byte
	After  []byte
}

type PlannedSetting added in v0.1.972

type PlannedSetting struct {
	Key    string
	Before json.RawMessage
	After  json.RawMessage
}

type RuntimeUnavailableError added in v0.1.972

type RuntimeUnavailableError struct {
	Agent     string
	Operation string
	Offline   OfflineAction
	Cause     error
}

RuntimeUnavailableError is created only for an initial runtime read, before the operation has written either runtime settings or local files.

func (*RuntimeUnavailableError) Error added in v0.1.972

func (e *RuntimeUnavailableError) Error() string

func (*RuntimeUnavailableError) Unwrap added in v0.1.972

func (e *RuntimeUnavailableError) Unwrap() error

type StaleCopy added in v0.1.1110

type StaleCopy struct {
	// Kind, for Codex: "app" (the ChatGPT or Codex desktop app, its
	// app-server and helpers), "ide" (an editor extension's), "daemon"
	// (the background app-server the CLI leaves running), "helper" (a
	// subcommand the daemon spawns beside itself, no copy to reopen) or
	// "cli"; "" for other agents; "embedded" is another app's own Codex,
	// named by App
	Kind  string    `json:"kind,omitempty"`
	App   string    `json:"app,omitempty"`
	Since time.Time `json:"since"`
}

StaleCopy is a copy of an agent still running on the list it started with: what kind of copy it is, which says how it is reopened, and when it started.

type TuneSetting added in v0.1.1158

type TuneSetting struct {
	Value string `json:"value,omitempty"` // "" when nothing sets it
	// Where Value comes from: settings (the agent's own config file),
	// model (Claude Code's modelSettings for the model), env
	Source string `json:"source,omitempty"`
	// Locked names the variable that takes precedence over the setting,
	// which tune leaves as it is: putting the advice in would change nothing
	Locked string `json:"locked,omitempty"`
	// Ours: tune put Value in, and Undo puts back what was there
	Ours bool `json:"ours,omitempty"`
}

TuneSetting is one of those settings as an agent has it now.

func TuneRead added in v0.1.1158

func TuneRead(home, agent, knob, model string) (TuneSetting, error)

TuneRead is how an agent has a setting tune advises now.

Jump to

Keyboard shortcuts

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