profiles: a profile's details list all four of Claude Code's tiers, one left to follow the main model saying so (#480: opus and fable, following it, were missing from the details, so they read as not set) — a tier is a quiet field, empty while it follows the main model, and the details left every empty quiet field out. A field now says which one it follows (Follows, "model" for the tiers), and the details keep such a field, empty, with it; the panel shows it as "follows the main model (<model>)" (跟随主模型(…)), or "follows the main model" when the main is the agent's default. Other quiet fields (Codex's sign-in, subagents) are still left out while empty, and what applying a profile writes is unchanged. TestDetails and profile-details.test.cjs (Chromium, WebKit, en, zh) fail without the change. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass.
This commit is contained in:
8 files changed
+65
-19
No files matched your search
@@ -53,6 +53,9 @@ type Field struct {
|
||||
// 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
|
||||
}
|
||||
|
||||
// Agent is one supported coding agent.
|
||||
|
||||
@@ -573,7 +573,7 @@ func claudeIn(at place) *Agent {
|
||||
}}
|
||||
for _, tier := range claudeTiers {
|
||||
fields = append(fields, Field{
|
||||
Key: tier, Label: tier, Quiet: true,
|
||||
Key: tier, Label: tier, Quiet: true, Follows: "model",
|
||||
// empty while the tier follows the main model
|
||||
Get: func() string {
|
||||
if w := env(tierEnv(tier)); routed() && w != env("ANTHROPIC_MODEL") {
|
||||
|
||||
@@ -589,7 +589,10 @@ function profileDetail(p, footed) {
|
||||
};
|
||||
for (const f of g.fields || []) {
|
||||
const effort = f.key === "effort" || f.label === "effort" || f.label === "thinking";
|
||||
// a Claude Code tier left to follow the main model (#480), and to what
|
||||
const main = f.follows && g.fields.find((x) => x.key === f.follows && !x.hidden)?.value;
|
||||
if (f.hidden) row(t(f.label), "••••••", "pd-hidden");
|
||||
else if (f.follows) row(t(f.label), main ? t("follows the main model ({model})", { model: main }) : t("follows the main model"), "pd-default");
|
||||
else if (!f.value) row(t(f.label), t("agent default"), "pd-default");
|
||||
else row(t(f.label), effort ? effortName({ value: f.value }) : f.value);
|
||||
}
|
||||
|
||||
@@ -295,6 +295,8 @@ const I18N = {
|
||||
"Apply": "应用",
|
||||
"Apply {name} to the agents": "将 {name} 应用到各 Agent",
|
||||
"No agent settings saved": "没有保存任何 Agent 的设置",
|
||||
"follows the main model": "跟随主模型",
|
||||
"follows the main model ({model})": "跟随主模型({model})",
|
||||
"•••••• looks like a key or a token, and is not shown": "•••••• 看起来是密钥或令牌,不予显示",
|
||||
"Updated {name} to the current setup": "已用当前配置更新 {name}",
|
||||
"Profile name": "方案名称",
|
||||
|
||||
@@ -347,8 +347,9 @@ field; Escape closes it; in English and Chinese.
|
||||
window and in the tray panel with its list scrolled to its end: the details
|
||||
open, applying nothing and leaving the chip where it was on the screen; they
|
||||
list each agent by name, its fields under their labels (an empty one as the
|
||||
agent's default, one that reads as a key as dots) and what the Library gives
|
||||
it; a second click closes them; their Apply applies the profile, once; ↻ and
|
||||
agent's default, one that reads as a key as dots, all four of Claude Code's
|
||||
tiers, one left empty as following the main model and which that is, #480)
|
||||
and what the Library gives it; a second click closes them; their Apply applies the profile, once; ↻ and
|
||||
× stay on the chip. In Chromium and WebKit, English and Chinese:
|
||||
|
||||
```sh
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
// could be seen only by applying it over the setup in use). A click on the
|
||||
// chip opens its details: the agents' names, each one's fields under their
|
||||
// labels (an empty one the agent's default, one that reads as a key shown as
|
||||
// dots), what the Library gives it; nothing is applied, and the chip clicked
|
||||
// dots, each of Claude Code's four tiers, one left to follow the main model
|
||||
// saying so and what that is: #480, they were missing), what the Library
|
||||
// gives it; nothing is applied, and the chip clicked
|
||||
// stays where it is on the screen. Apply in the details applies it, once; a
|
||||
// second click on the chip closes them; ↻ and × are still on the chip. In the
|
||||
// window and in the tray panel (its list scrolled to its end), in English
|
||||
@@ -23,7 +25,14 @@ const work = {
|
||||
name: "work", summary: "codex gpt-5",
|
||||
library: { servers: 1, skills: 1, instructions: true },
|
||||
agents: [
|
||||
{ id: "claude", name: "Claude Code", fields: [{ key: "model", label: "model", value: "" }], servers: ["github"], skills: ["review"] },
|
||||
{ id: "claude", name: "Claude Code", fields: [
|
||||
{ key: "model", label: "model", value: "magpie/a/main" },
|
||||
{ key: "opus", label: "opus", value: "", follows: "model" },
|
||||
{ key: "sonnet", label: "sonnet", value: "magpie/a/s" },
|
||||
{ key: "haiku", label: "haiku", value: "", follows: "model" },
|
||||
{ key: "fable", label: "fable", value: "", follows: "model" },
|
||||
{ key: "subagent", label: "subagents", value: "" },
|
||||
], servers: ["github"], skills: ["review"] },
|
||||
{ id: "codex", name: "Codex", fields: [
|
||||
{ key: "model", label: "model", value: "magpie/openai/gpt-5" },
|
||||
{ key: "effort", label: "effort", value: "high" },
|
||||
@@ -57,8 +66,8 @@ function server(lang, calls, count) {
|
||||
}
|
||||
|
||||
const words = {
|
||||
en: { model: "model", effort: "effort", high: "high", def: "agent default", servers: "MCP servers", skills: "Skills", instructions: "Instructions", on: "on", apply: "Apply" },
|
||||
zh: { model: "模型", effort: "推理强度", high: "高", def: "Agent 默认值", servers: "MCP 服务器", skills: "技能", instructions: "指令", on: "开启", apply: "应用" },
|
||||
en: { model: "model", effort: "effort", high: "high", def: "agent default", follows: "follows the main model (magpie/a/main)", subagents: "subagents", servers: "MCP servers", skills: "Skills", instructions: "Instructions", on: "on", apply: "Apply" },
|
||||
zh: { model: "模型", effort: "推理强度", high: "高", def: "Agent 默认值", follows: "跟随主模型(magpie/a/main)", subagents: "子 agent", servers: "MCP 服务器", skills: "技能", instructions: "指令", on: "开启", apply: "应用" },
|
||||
};
|
||||
|
||||
for (const engine of (process.env.BROWSER ? [process.env.BROWSER] : ["chromium", "webkit"])) {
|
||||
@@ -125,7 +134,7 @@ for (const engine of (process.env.BROWSER ? [process.env.BROWSER] : ["chromium",
|
||||
assert.equal(read.apply, w.apply);
|
||||
assert(read.inView, "the details must be drawn");
|
||||
assert.deepEqual(read.groups, [
|
||||
{ name: "Claude Code", rows: [`${w.model}=${w.def}`, `${w.servers}=github`, `${w.skills}=review`] },
|
||||
{ name: "Claude Code", rows: [`${w.model}=magpie/a/main`, `opus=${w.follows}`, "sonnet=magpie/a/s", `haiku=${w.follows}`, `fable=${w.follows}`, `${w.subagents}=${w.def}`, `${w.servers}=github`, `${w.skills}=review`] },
|
||||
{ name: "Codex", rows: [`${w.model}=magpie/openai/gpt-5`, `${w.effort}=${w.high}`, "token=••••••", `${w.instructions}=${w.on}`] },
|
||||
]);
|
||||
|
||||
|
||||
@@ -24,13 +24,16 @@ type Group struct {
|
||||
Instructions bool `json:"instructions,omitempty"`
|
||||
}
|
||||
|
||||
// Item is one saved field. Value "" is the agent's own default. Hidden is a
|
||||
// value that looked like a key or a token: it is left out, never shown.
|
||||
// Item is one saved field. Value "" is the agent's own default, or, with
|
||||
// Follows, the key of the field it takes after (a Claude Code tier that
|
||||
// follows the main model, #480). Hidden is a value that looked like a key or
|
||||
// a token: it is left out, never shown.
|
||||
type Item struct {
|
||||
Key string `json:"key"`
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
Hidden bool `json:"hidden,omitempty"`
|
||||
Key string `json:"key"`
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
Follows string `json:"follows,omitempty"`
|
||||
Hidden bool `json:"hidden,omitempty"`
|
||||
}
|
||||
|
||||
// secretName is a field named for a credential; secretValue a value that
|
||||
@@ -47,8 +50,11 @@ func secret(key, label, value string) bool {
|
||||
}
|
||||
|
||||
// Details is what p holds, by agent: the agents magpie knows in the order it
|
||||
// lists them, then any it no longer does by id. A quiet field left empty (it
|
||||
// follows another one) is left out, as the agent's row leaves it out.
|
||||
// lists them, then any it no longer does by id. A quiet field left empty is
|
||||
// left out, as the agent's row leaves it out, unless it says which field it
|
||||
// follows: Claude Code's four tiers are all there, one that follows the main
|
||||
// model saying so (#480: opus and fable, following it, were missing, and
|
||||
// read as not set). What applying the profile writes is unchanged.
|
||||
func Details(p Profile) []Group {
|
||||
known := map[string]*agent.Agent{}
|
||||
var order []string
|
||||
@@ -117,6 +123,9 @@ func Details(p Profile) []Group {
|
||||
}
|
||||
seen[f.Key] = true
|
||||
if f.Quiet && v == "" {
|
||||
if f.Follows != "" {
|
||||
g.Fields = append(g.Fields, Item{Key: f.Key, Label: f.Label, Follows: f.Follows})
|
||||
}
|
||||
continue
|
||||
}
|
||||
add(f.Key, f.Label, v)
|
||||
|
||||
@@ -11,7 +11,9 @@ import (
|
||||
|
||||
// A profile's details are its fields by agent, in the order magpie lists the
|
||||
// agents and their fields, under the agents' names and the fields' labels,
|
||||
// with what the library gives each; an empty quiet field is left out, an
|
||||
// with what the library gives each; an empty quiet field is left out, but
|
||||
// Claude Code's four tiers are all there, an empty one following the main
|
||||
// model (#480: opus and fable, following it, were missing); an
|
||||
// agent magpie no longer knows comes last by id, and nothing that reads as a
|
||||
// key or a token is in them (#467).
|
||||
func TestDetails(t *testing.T) {
|
||||
@@ -23,6 +25,9 @@ func TestDetails(t *testing.T) {
|
||||
"codex.login": "", // quiet: follows the model
|
||||
"claude.model": "opus",
|
||||
"claude.haiku": "",
|
||||
"claude.opus": "",
|
||||
"claude.sonnet": "magpie/a/s",
|
||||
"claude.fable": "",
|
||||
"gone.model": "m",
|
||||
"gone.apiKey": "plain-looking",
|
||||
"codex.extra": "sk-live-abcdef",
|
||||
@@ -67,8 +72,22 @@ func TestDetails(t *testing.T) {
|
||||
if _, ok := item(codex, "login"); ok {
|
||||
t.Error("an empty quiet field is left out")
|
||||
}
|
||||
if _, ok := item(claude, "haiku"); ok {
|
||||
t.Error("an empty tier is left out")
|
||||
var tiers []string
|
||||
for _, it := range claude.Fields {
|
||||
switch it.Key {
|
||||
case "opus", "sonnet", "haiku", "fable":
|
||||
tiers = append(tiers, it.Key)
|
||||
want := Item{Key: it.Key, Label: it.Key, Follows: "model"}
|
||||
if it.Key == "sonnet" {
|
||||
want = Item{Key: "sonnet", Label: "sonnet", Value: "magpie/a/s"}
|
||||
}
|
||||
if it != want {
|
||||
t.Errorf("tier %+v, want %+v", it, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !slices.Equal(tiers, []string{"opus", "sonnet", "haiku", "fable"}) {
|
||||
t.Errorf("tiers %v, want all four in Claude Code's order, an empty one following the model", tiers)
|
||||
}
|
||||
for _, c := range []struct {
|
||||
g Group
|
||||
|
||||
Reference in new issue
Block a user