agent

package
v0.1.784 Latest Latest
Warning

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

Go to latest
Published: Oct 3, 2026 License: MIT Imports: 53 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 CindyScheme = "cindy://provider/import?"

CindyScheme begins every Cindy import link.

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

This section is empty.

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 DesktopConfig3p added in v0.1.474

func DesktopConfig3p(home string) string

DesktopConfig3p is the claude_desktop_config.json Claude Desktop reads in its 3p mode: there its whole userData is Claude-3p, its MCP servers too (%LOCALAPPDATA%\Claude-3p on Windows, 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 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 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 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 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 Running

func Running(patterns ...string) bool

Running reports whether a process whose command line matches any pattern (an extended regexp, as for pgrep -f) is alive. Unknown on Windows.

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 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.

Types

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
	Fields  []Field
	// 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
	// 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
	// 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
	// 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)
	// 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.

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 (*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.

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. ok is false for an agent without a CLI magpie knows.

func (*Agent) Connect added in v0.1.780

func (a *Agent) Connect() error

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.

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) 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) Field

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

Field looks a field up by key.

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) 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) 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.

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. Zero where it can't be told (Windows).

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"`
}

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

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.
	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
}

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), for a profile's details to say so.
	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 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 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"`
	// 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"`
	// 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"`
	// contains filtered or unexported fields
}

Option is one value the picker offers for a field.

Jump to

Keyboard shortcuts

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