provider: Antigravity's models at several levels are one model with those levels (#150: 统一 Antigravity 模型变体与客户端推理档位的映射) — Antigravity lists gemini-3.7-flash-low, -medium and -high as three models, "Gemini 3.7 Flash (Low)" …, so a client picked a level twice: once in the model, once as its reasoning effort, with no telling which won. Now ids that are one id with an effort word after it (-low, -medium, -high, -extra-low, -minimal, -xhigh) are one model, gemini-3.7-flash "Gemini 3.7 Flash", declaring only the levels there are (low,medium,high; Gemini 3.1 Pro low,high), so Codex, Pi and the rest get reasoning levels for it. The level a variant is at is what Antigravity's name says where each name says a different one (gemini-3.5-flash-extra-low is "(Low)", gemini-3.5-flash-low "(Medium)"), else its id's word; a model only named like another (gemini-3-flash-agent, "Gemini 3.5 Flash (High)"; gemini-pro-agent) or with no sibling (gpt-oss-120b-medium) stays as listed. A request asks for the variant the effort picks, an effort it lacks at the nearest level (xhigh is high; medium on low/high is high); with none asked, the id without a level if Antigravity has one, else high — what Gemini 3 does unasked — else the highest. A variant's request carries no thinkingLevel, so the id and the level can't disagree. An old variant id still works: as named with no effort, and moved to the effort asked for. Saved picks and agents set to a variant (antigravity/gemini-3.7-flash-high) are moved to the family, at that level where the agent's own is unset, as Cursor's are. For Gemini 3 models without variants, medium now goes as medium to Flash (which takes minimal/low/medium/high) and still as high to Pro (low/high). Tested against the list an account's fetchAvailableModels gave (sandbox cache)

This commit is contained in:
yetone committed 2026-09-28 20:10:00 +08:00
1 parent 89bfde701f
commit 2baad5366c
8 files changed
+498 -6

No files matched your search

+5
View File
@@ -70,6 +70,11 @@ func RenameLegacy() {
// Cursor's id. It runs at start-up; errors are swallowed as RenameLegacy's.
func MoveCursorEfforts() { moveEfforts("cursor", provider.CursorBase) }
// 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 MoveAntigravityEfforts() { moveEfforts("antigravity", provider.AntigravityBase) }
func moveEfforts(pid string, base func(string) (string, string, bool)) {
offers := func(opts []Option, v string) bool {
return slices.ContainsFunc(opts, func(o Option) bool { return o.Value == v })
+65 -6
View File
@@ -4,6 +4,8 @@ import (
"encoding/json"
"regexp"
"strings"
"github.com/yetone/magpie/internal/provider"
)
// ---- Google Code Assist (provider side) --------------------------------------
@@ -21,10 +23,58 @@ const skipSignature = "skip_thought_signature_validator"
var unsafeToolID = regexp.MustCompile(`[^a-zA-Z0-9_-]`)
// antigravityVariants are the ids of Antigravity's a model magpie offers
// stands for, by level, and antigravityBaseOf the model and level one of
// those ids is; vars so tests can stand in for them.
var (
antigravityVariants = provider.AntigravityVariants
antigravityBaseOf = provider.AntigravityBase
)
// antigravityModelID is Antigravity's id for a model magpie offers, at
// the effort asked for: the family's variant at it, or at the nearest
// level it has; with none asked, the family's default. One of
// Antigravity's own ids at a level (gemini-3.7-flash-low, one an agent
// was set to before) goes as it is when no effort is asked, and else is
// the family's variant at the effort asked for: the effort the client
// picks wins, as it does for the family. Any other id goes as it is.
func antigravityModelID(model, effort string) string {
if base, _, ok := antigravityBaseOf(model); ok {
if effort == "" {
return model
}
model = base
}
vs, ok := antigravityVariants(model)
if !ok {
return model
}
if effort == "" {
return vs[""]
}
var levels []string
for _, l := range effortRank {
if vs[l] != "" {
levels = append(levels, l)
}
}
if id := vs[fitEffort(effort, levels)]; id != "" {
return id
}
return vs[""]
}
// buildCodeAssist builds the Code Assist envelope for a request, for the
// app the account belongs to: "gemini" or "antigravity".
// app the account belongs to: "gemini" or "antigravity". On Antigravity a
// model that is a family of levels is asked for as the variant the effort
// picks (antigravityModelID).
func buildCodeAssist(r *Request, model, agent string) []byte {
ag := agent == "antigravity"
fixed := false // the id says the level it thinks at
if ag {
model = antigravityModelID(model, r.Effort)
_, _, fixed = antigravityBaseOf(model)
}
claude := strings.Contains(strings.ToLower(model), "claude")
toolID := func(id string) string {
if !ag || id == "" {
@@ -142,7 +192,7 @@ func buildCodeAssist(r *Request, model, agent string) []byte {
if len(r.Stop) > 0 {
gen["stopSequences"] = r.Stop
}
if tc := thinkingConfig(r, model, claude); tc != nil {
if tc := thinkingConfig(r, model, claude, fixed); tc != nil {
gen["thinkingConfig"] = tc
// Claude's answer has to have room past its thinking
if b, ok := tc["thinkingBudget"].(int); ok && claude && gen["maxOutputTokens"] == nil {
@@ -157,8 +207,14 @@ func buildCodeAssist(r *Request, model, agent string) []byte {
}
// thinkingConfig says how hard the model should think: a level for
// Gemini 3, a budget for the rest. A model that doesn't think gets none.
func thinkingConfig(r *Request, model string, claude bool) map[string]any {
// Gemini 3, a budget for the rest. A model that doesn't think gets none,
// and one whose id already says its level (fixed: Antigravity's
// gemini-3.7-flash-low) gets no level that could say otherwise — the
// effort asked for picked that id.
//
// Gemini 3's levels: Flash takes minimal, low, medium and high, so medium
// goes as medium; Pro takes low and high only, so medium goes up to high.
func thinkingConfig(r *Request, model string, claude, fixed bool) map[string]any {
m := strings.ToLower(model)
if strings.HasPrefix(m, "gpt-oss") || claude && !strings.Contains(m, "thinking") {
return nil
@@ -171,10 +227,13 @@ func thinkingConfig(r *Request, model string, claude bool) map[string]any {
}
tc := map[string]any{"includeThoughts": true}
if strings.HasPrefix(m, "gemini-3") || strings.HasPrefix(m, "gemini-pro-agent") {
if r.Effort != "" {
if r.Effort != "" && !fixed {
level := "high"
if r.Effort == "low" {
switch {
case r.Effort == "low":
level = "low"
case r.Effort == "medium" && strings.Contains(m, "flash"):
level = "medium"
}
tc["thinkingLevel"] = level
}
+51
View File
@@ -4,6 +4,8 @@ import (
"encoding/json"
"strings"
"testing"
"github.com/yetone/magpie/internal/catalog"
)
func codeAssistRequest(t *testing.T) *Request {
@@ -141,3 +143,52 @@ func TestCodeAssistDecoder(t *testing.T) {
t.Errorf("error events = %+v", errs)
}
}
// On Antigravity a model that is one of its families of levels is asked
// for as the variant the effort picks, with no thinking level that could
// say otherwise; an old variant id moves with an effort asked; Gemini 3
// Flash takes medium as medium, Pro as high.
func TestCodeAssistAntigravityLevels(t *testing.T) {
t.Setenv("XDG_CACHE_HOME", t.TempDir())
var raw []catalog.Model
for _, l := range []string{"gemini-3-flash - Gemini 3 Flash", "gemini-3.1-pro-high - Gemini 3.1 Pro (High)", "gemini-3.1-pro-low - Gemini 3.1 Pro (Low)",
"gemini-3.7-flash-high - Gemini 3.7 Flash (High)", "gemini-3.7-flash-low - Gemini 3.7 Flash (Low)",
"gemini-3.7-flash-medium - Gemini 3.7 Flash (Medium)", "gemini-pro-agent - Gemini 3.1 Pro (High)"} {
id, name, _ := strings.Cut(l, " - ")
raw = append(raw, catalog.Model{ID: id, Name: name})
}
if err := catalog.SaveLive("antigravity", "", raw); err != nil {
t.Fatal(err)
}
// level: "" no thinkingConfig, "-" thinking at the level the id says
for _, c := range []struct{ model, effort, agent, want, level string }{
{"gemini-3.7-flash", "low", "antigravity", "gemini-3.7-flash-low", "-"},
{"gemini-3.7-flash", "medium", "antigravity", "gemini-3.7-flash-medium", "-"},
{"gemini-3.7-flash", "xhigh", "antigravity", "gemini-3.7-flash-high", "-"},
{"gemini-3.7-flash", "", "antigravity", "gemini-3.7-flash-high", ""},
{"gemini-3.7-flash-high", "", "antigravity", "gemini-3.7-flash-high", ""},
{"gemini-3.7-flash-high", "low", "antigravity", "gemini-3.7-flash-low", "-"},
{"gemini-3.1-pro", "medium", "antigravity", "gemini-3.1-pro-high", "-"},
{"gemini-3-flash", "medium", "antigravity", "gemini-3-flash", "medium"},
{"gemini-pro-agent", "medium", "antigravity", "gemini-pro-agent", "high"},
{"gemini-pro-agent", "low", "antigravity", "gemini-pro-agent", "low"},
{"gemini-3.7-flash", "low", "gemini", "gemini-3.7-flash", "low"},
} {
r := codeAssistRequest(t)
r.Thinking, r.Effort = false, c.effort
var env struct {
Model string `json:"model"`
Request map[string]any `json:"request"`
}
json.Unmarshal(buildCodeAssist(r, c.model, c.agent), &env)
gen, _ := env.Request["generationConfig"].(map[string]any)
tc, _ := gen["thinkingConfig"].(map[string]any)
level, _ := tc["thinkingLevel"].(string)
if tc != nil && level == "" {
level = "-"
}
if env.Model != c.want || level != c.level {
t.Errorf("%s at %q on %s: %s at %q, want %s at %q", c.model, c.effort, c.agent, env.Model, level, c.want, c.level)
}
}
}
+241
View File
@@ -0,0 +1,241 @@
package provider
// Antigravity lists some models once for each thinking level it serves
// them at ("gemini-3.7-flash-low", "-medium", "-high", named "Gemini 3.7
// Flash (Low)" …), so a client that also picks an effort had two to pick.
// magpie offers such a family as one model ("gemini-3.7-flash", "Gemini
// 3.7 Flash") with the levels there are, and the gateway asks for the id
// the effort picks (AntigravityVariants).
//
// A family is only ids that are one id with an effort word after it: a
// model named like another but with an id of its own (gemini-3-flash-agent
// is "Gemini 3.5 Flash (High)") stays a model of its own, as does an id
// with no sibling. Which level a variant is at is what Antigravity's name
// for it says, where the names say one each: gemini-3.5-flash-extra-low is
// "(Low)" and gemini-3.5-flash-low "(Medium)"; else its id's word.
import (
"slices"
"strings"
"github.com/yetone/magpie/internal/catalog"
)
// antigravityEffortWords are the effort words of Antigravity's ids and the
// level each is, longest first.
var antigravityEffortWords = []struct{ word, level string }{
{"extra-high", "xhigh"}, {"extra-low", "minimal"}, {"xhigh", "xhigh"}, {"minimal", "minimal"},
{"low", "low"}, {"medium", "medium"}, {"high", "high"},
}
// splitAntigravityID is an id's family and the level its effort word
// says, "" when it has none.
func splitAntigravityID(id string) (family, effort string) {
for _, e := range antigravityEffortWords {
if b, ok := strings.CutSuffix(id, "-"+e.word); ok && b != "" {
return b, e.level
}
}
return id, ""
}
// antigravityNameEffort is the level a name's last words in brackets say
// ("Gemini 3.7 Flash (High)" is high), and the name without them.
func antigravityNameEffort(name string) (string, string) {
open := strings.LastIndex(name, " (")
if open < 0 || !strings.HasSuffix(name, ")") {
return name, ""
}
var level string
switch strings.ToLower(name[open+2 : len(name)-1]) {
case "minimal", "extra low":
level = "minimal"
case "low":
level = "low"
case "medium":
level = "medium"
case "high":
level = "high"
case "extra high", "xhigh":
level = "xhigh"
default:
return name, ""
}
return strings.TrimSpace(name[:open]), level
}
// antigravityFamily is one family of Antigravity's ids.
type antigravityFamily struct {
id string
variants []catalog.Model
efforts []string // each variant's level, "" for the id without one
}
// byEffort is the id for each level, "" the one asked for when no level
// is: the family's id without an effort word, if Antigravity has it; else
// high — what Gemini 3 thinks at when it isn't told a level, so a client
// that asks for none gets what the plain model would give; else the
// highest there is.
func (f *antigravityFamily) byEffort() map[string]string {
out := map[string]string{}
for i, v := range f.variants {
if _, ok := out[f.efforts[i]]; !ok {
out[f.efforts[i]] = v.ID
}
}
if _, ok := out[""]; !ok {
out[""] = out["high"]
for i := len(cursorLevelRank) - 1; i >= 0 && out[""] == ""; i-- {
out[""] = out[cursorLevelRank[i]]
}
}
return out
}
// model is the family as one model: named as Antigravity names its
// variants, without the level; with the levels there are, lowest first;
// holding what the least of its variants does.
func (f *antigravityFamily) model() catalog.Model {
def := f.byEffort()[""]
var m catalog.Model
for _, v := range f.variants {
if v.ID == def {
m = v
}
}
m.ID, m.Efforts = f.id, nil
m.Name, _ = antigravityNameEffort(m.Name)
if m.Name == "" || m.Name == def {
m.Name = f.id
}
for _, v := range f.variants {
if v.Context > 0 && (m.Context == 0 || v.Context < m.Context) {
m.Context = v.Context
}
if v.Output > 0 && (m.Output == 0 || v.Output < m.Output) {
m.Output = v.Output
}
m.Images = m.Images && v.Images
}
for _, l := range cursorLevelRank {
if slices.Contains(f.efforts, l) {
m.Efforts = append(m.Efforts, l)
}
}
return m
}
// antigravityFamilies are the families of a list of Antigravity's ids, in
// the order the list first has them.
func antigravityFamilies(raw []catalog.Model) []*antigravityFamily {
var out []*antigravityFamily
by := map[string]*antigravityFamily{}
for _, m := range raw {
id, _ := splitAntigravityID(m.ID)
f := by[id]
if f == nil {
f = &antigravityFamily{id: id}
by[id] = f
out = append(out, f)
}
f.variants = append(f.variants, m)
}
for _, f := range out {
// the level each name says, if each says one and no two the same
var named []string
for _, v := range f.variants {
if _, l := antigravityNameEffort(v.Name); l != "" && !slices.Contains(named, l) {
named = append(named, l)
}
}
for _, v := range f.variants {
_, l := splitAntigravityID(v.ID)
if len(named) == len(f.variants) {
_, l = antigravityNameEffort(v.Name)
}
f.efforts = append(f.efforts, l)
}
}
return out
}
// collapseAntigravityModels is Antigravity's list with each family one
// model; a family of one keeps Antigravity's id and name.
func collapseAntigravityModels(raw []catalog.Model) []catalog.Model {
var out []catalog.Model
for _, f := range antigravityFamilies(raw) {
if len(f.variants) == 1 {
out = append(out, f.variants[0])
continue
}
out = append(out, f.model())
}
return out
}
// AntigravityVariants are Antigravity's ids a model magpie offers stands
// for, by level, "" the default; ok is false for a model that stands for
// none, which goes to Antigravity as it is.
func AntigravityVariants(model string) (map[string]string, bool) {
raw, _, ok := catalog.Live("antigravity")
if !ok {
return nil, false
}
return antigravityVariantsIn(raw, model)
}
func antigravityVariantsIn(raw []catalog.Model, model string) (map[string]string, bool) {
for _, f := range antigravityFamilies(raw) {
if f.id == model && len(f.variants) > 1 {
return f.byEffort(), true
}
}
return nil, false
}
// AntigravityBase is the model magpie offers for one of Antigravity's ids
// at a level ("gemini-3.7-flash-low" is gemini-3.7-flash at low), and that
// level; ok is false for an id that is itself a model magpie offers, or
// that no family has. Picks and agents' models saved before the families
// were one model still name these ids.
func AntigravityBase(id string) (base, effort string, ok bool) {
raw, _, live := catalog.Live("antigravity")
if !live {
return "", "", false
}
return antigravityBaseIn(raw, id)
}
func antigravityBaseIn(raw []catalog.Model, id string) (string, string, bool) {
for _, f := range antigravityFamilies(raw) {
if len(f.variants) < 2 {
continue
}
for i, v := range f.variants {
if v.ID == id && id != f.id {
return f.id, f.efforts[i], true
}
}
}
return "", "", false
}
// antigravityPicks are the user's picks of Antigravity's models with each
// of its ids at a level the model magpie offers for it, in the order
// picked, each once.
func antigravityPicks(ids []string) []string {
raw, _, live := catalog.Live("antigravity")
if !live {
return ids
}
var out []string
for _, id := range ids {
if base, _, ok := antigravityBaseIn(raw, id); ok {
id = base
}
if !slices.Contains(out, id) {
out = append(out, id)
}
}
return out
}
@@ -0,0 +1,128 @@
package provider
import (
"path/filepath"
"strings"
"testing"
"github.com/yetone/magpie/internal/catalog"
)
// antigravityListed is what Antigravity's fetchAvailableModels said for
// an account (2026-09), as magpie keeps it.
var antigravityListed = []catalog.Model{
{ID: "claude-opus-4-6-thinking", Name: "Claude Opus 4.6 (Thinking)", Images: true},
{ID: "gemini-3-flash", Name: "Gemini 3 Flash", Images: true},
{ID: "gemini-3-flash-agent", Name: "Gemini 3.5 Flash (High)", Images: true},
{ID: "gemini-3.1-pro-high", Name: "Gemini 3.1 Pro (High)", Images: true},
{ID: "gemini-3.1-pro-low", Name: "Gemini 3.1 Pro (Low)", Images: true},
{ID: "gemini-3.5-flash-extra-low", Name: "Gemini 3.5 Flash (Low)", Images: true},
{ID: "gemini-3.5-flash-lite", Name: "Gemini 3.5 Flash Lite", Images: true},
{ID: "gemini-3.5-flash-low", Name: "Gemini 3.5 Flash (Medium)", Images: true},
{ID: "gemini-3.7-flash-high", Name: "Gemini 3.7 Flash (High)", Images: true},
{ID: "gemini-3.7-flash-low", Name: "Gemini 3.7 Flash (Low)", Images: true},
{ID: "gemini-3.7-flash-medium", Name: "Gemini 3.7 Flash (Medium)", Images: true},
{ID: "gemini-3.7-flash-tiered", Images: true},
{ID: "gemini-pro-agent", Name: "Gemini 3.1 Pro (High)", Images: true},
{ID: "gpt-oss-120b-medium", Name: "GPT-OSS 120B (Medium)"},
}
// Antigravity's ids of one model at several levels are one model with
// those levels; a model with one id, or only a name like another's,
// stays as Antigravity lists it.
func TestCollapseAntigravityModels(t *testing.T) {
var got []string
for _, m := range collapseAntigravityModels(antigravityListed) {
got = append(got, m.ID+"|"+m.Name+"|"+strings.Join(m.Efforts, ","))
}
want := []string{
"claude-opus-4-6-thinking|Claude Opus 4.6 (Thinking)|",
"gemini-3-flash|Gemini 3 Flash|",
"gemini-3-flash-agent|Gemini 3.5 Flash (High)|",
"gemini-3.1-pro|Gemini 3.1 Pro|low,high",
// the names say the levels: extra-low is Low, low is Medium
"gemini-3.5-flash|Gemini 3.5 Flash|low,medium",
"gemini-3.5-flash-lite|Gemini 3.5 Flash Lite|",
"gemini-3.7-flash|Gemini 3.7 Flash|low,medium,high",
"gemini-3.7-flash-tiered||",
"gemini-pro-agent|Gemini 3.1 Pro (High)|",
"gpt-oss-120b-medium|GPT-OSS 120B (Medium)|",
}
if strings.Join(got, "\n") != strings.Join(want, "\n") {
t.Fatalf("got\n%s\nwant\n%s", strings.Join(got, "\n"), strings.Join(want, "\n"))
}
for model, want := range map[string]string{
"gemini-3.7-flash": "=gemini-3.7-flash-high high=gemini-3.7-flash-high low=gemini-3.7-flash-low medium=gemini-3.7-flash-medium",
"gemini-3.5-flash": "=gemini-3.5-flash-low low=gemini-3.5-flash-extra-low medium=gemini-3.5-flash-low",
"gemini-3.1-pro": "=gemini-3.1-pro-high high=gemini-3.1-pro-high low=gemini-3.1-pro-low",
} {
vs, ok := antigravityVariantsIn(antigravityListed, model)
var got []string
for _, l := range []string{"", "high", "low", "medium"} {
if vs[l] != "" {
got = append(got, l+"="+vs[l])
}
}
if !ok || strings.Join(got, " ") != want {
t.Errorf("%s: %v %v, want %s", model, got, ok, want)
}
}
for _, id := range []string{"gemini-3-flash", "gpt-oss-120b-medium", "gemini-3.7-flash-high", "gemini-pro-agent"} {
if _, ok := antigravityVariantsIn(antigravityListed, id); ok {
t.Errorf("%s has variants", id)
}
}
// with an id of no level, it is the default
vs, _ := antigravityVariantsIn([]catalog.Model{{ID: "m-low"}, {ID: "m"}, {ID: "m-high"}}, "m")
if vs[""] != "m" || vs["low"] != "m-low" {
t.Errorf("variants %v", vs)
}
// without high, the highest there is
vs, _ = antigravityVariantsIn([]catalog.Model{{ID: "m-low"}, {ID: "m-medium"}}, "m")
if vs[""] != "m-medium" {
t.Errorf("variants %v", vs)
}
}
// Picks saved before the levels were one model name Antigravity's own
// ids: each is the model magpie offers for it now, once, where it was;
// the rest stay.
func TestAntigravityLegacyPicks(t *testing.T) {
home := t.TempDir()
t.Setenv("HOME", home)
t.Setenv("XDG_CONFIG_HOME", filepath.Join(home, ".config"))
t.Setenv("XDG_CACHE_HOME", filepath.Join(home, ".cache"))
if err := catalog.SaveLive("antigravity", "", antigravityListed); err != nil {
t.Fatal(err)
}
for id, want := range map[string]string{"gemini-3.7-flash-high": "gemini-3.7-flash|high", "gemini-3.5-flash-extra-low": "gemini-3.5-flash|low",
"gemini-3.5-flash-low": "gemini-3.5-flash|medium", "gemini-3.1-pro-low": "gemini-3.1-pro|low"} {
if b, e, ok := AntigravityBase(id); !ok || b+"|"+e != want {
t.Errorf("%s: %s|%s %v, want %s", id, b, e, ok, want)
}
}
for _, id := range []string{"gemini-3.7-flash", "gpt-oss-120b-medium", "gemini-pro-agent", "gemini-3-flash-agent", "nope-low"} {
if b, _, ok := AntigravityBase(id); ok {
t.Errorf("%s taken for %s", id, b)
}
}
legacy := []string{"gemini-3.7-flash-high", "gemini-3-flash", "gemini-3.7-flash-low", "gemini-pro-agent", "gemini-3.5-flash-low", "gone-model"}
want := "gemini-3.7-flash,gemini-3-flash,gemini-pro-agent,gemini-3.5-flash,gone-model"
if got := strings.Join(antigravityPicks(legacy), ","); got != want {
t.Fatalf("picks %s, want %s", got, want)
}
// what the account lists is the families, with their levels
p := Provider{ID: "antigravity"}
var got []string
for _, m := range p.Available() {
if strings.HasPrefix(m.ID, "gemini-3.7") {
got = append(got, m.ID+"|"+strings.Join(p.Efforts(m.ID), ","))
}
}
if strings.Join(got, " ") != "gemini-3.7-flash|low,medium,high gemini-3.7-flash-tiered|" {
t.Fatalf("available %v", got)
}
}
+4
View File
@@ -45,6 +45,10 @@ func (p Provider) Available() []catalog.Model {
live = collapseCursorModels(withCursorContexts(live))
case "devin":
live = withDevinContexts(live)
case "antigravity":
// after its names are filled in, and so that a family's
// levels aren't taken off by a known model of its id
return collapseAntigravityModels(catalog.Decorate(live, known))
}
return catalog.Decorate(live, known)
}
+3
View File
@@ -213,6 +213,9 @@ func All() []Provider {
if a.ID == "cursor" { // picked before its efforts were one model
a.Models = cursorPicks(a.Models)
}
if a.ID == "antigravity" { // picked before its levels were one model
a.Models = antigravityPicks(a.Models)
}
out = append(out, a)
}
return out
+1
View File
@@ -105,6 +105,7 @@ func run(args []string) error {
settings.Migrate()
agent.RenameLegacy()
agent.MoveCursorEfforts()
agent.MoveAntigravityEfforts()
// a provider added, edited or removed, or a list fetched anew, reaches
// the model lists agents keep in files of their own
catalog.Changed = agent.SyncCatalog