* feat(agent): add Reasonix Studio 2.x integration
* agent: select Reasonix planner independently from executor
* agent: restore Reasonix when its credential file was initially absent
* agent: preserve Reasonix planner choices and model controls
Keep configured and saved native planner references selectable, and retain full model picker behavior and role labels. Exercise native planner restoration and both browser layouts. Separate temporary CLI first-launch work from version and intentional timeout assertions.
* test(gui): follow the current Reasonix connection flow
* test(gui): open connected Reasonix details before checking roles
* agentenv: isolate Reasonix configuration in test sandboxes
* gui: restore Reasonix roles without disconnecting the other
* gui: Reasonix's role labels read in every language, and its confirm names them
The review found four things on the Reasonix roles' strings.
"executor" and "planner" are field labels like any other, so t() translates
them; they had no i18n entry in any language, so the zh, ja and de windows
showed the English key. They now read 执行模型/规划模型, 実行モデル/計画モデル
and Executor-Modell/Planner-Modell, beside model/effort/thinking in each block.
The confirm said "neither Executor nor Plan" — names that appear nowhere in
the UI — and its zh text said "Executor 和 Plan", while the fields are labelled
executor and planner. It now names the labels, and each language names its own
translation of them (zh 执行模型和规划模型, ja 実行モデルも計画モデル, de vom
Executor- bzw. Planner-Modell).
Both new strings had a zh entry and no ja or de entry, which is what turned
gui-ja and gui-de red: every zh key needs its ja and de with the same
placeholders. "restore the previous {field} selection" also started at column
0 in i18n.js; it is indented with the lines around it now.
reasonix-roles.test.cjs asserts the rendered label, so its zh expectations name
执行模型/规划模型; the en ones stay as they were, the en label being the key.
Its en assertions on the confirm's first sentence are unchanged, and the
reworded second sentence is not asserted.
Verified on this head: gui-ja passes 3/3 and gui-de 3/3 (every zh string has
its ja and de, same placeholders and tags); reasonix-roles.test.cjs passes 8/8
in Chromium and WebKit, en and zh, window (1000px) and panel (440px); go vet
./... and the four builds pass. The rest of internal/gui/tests was run and
compared against a clean main worktree at e0bf3e61: the same assertions fail on
both, and none of them is about i18n or Reasonix (see the PR comment).
* edit: a TOML value is written the way TOML reads it, preserving or not
setTOMLTop spelled every value with strconv.Quote. Go's escapes are not TOML's:
a control byte came out as \x01, and TOML defines no \x escape, so a preserving
edit wrote a config.toml that no parser reads — an agent's own value could not
be written at all. Worse, it only failed at writeTOML, after the edit was
already decided.
The preserving path and the path that adds a key now both use tomlLiteral, as
SetTOMLKey already did, so both write the same encoding. tomlString spells a
basic string from the TOML spec (\b \t \n \f \r \" \\, \uXXXX for the other
control bytes and DEL, everything else as it is, non-ASCII included). A byte
that is not valid UTF-8 has no TOML escape, so it is left as it is and writeTOML
refuses the file, rather than the value being quietly replaced by U+FFFD.
TestTOMLPreservingKeepsAValueTOMLCanParse parses the file back with the TOML
parser and compares the value, and TestTOMLPreservingAndPlainAgreeOnEncoding
puts the same value through both paths and compares the bytes. TestSetTOMLKeepsCRLF's
"top = \"9\"" is "top = 9" now: an int is a TOML integer, the same value
SetTOMLKey writes for it, and no caller but that test passes anything but a
string here.
Verified on this head: go test ./internal/edit/ passes whole; the two new tests
and TestSetTOMLKeepsCRLF pass under -race -count=10; go vet ./... and the four
builds pass. TestTOMLBigFileParsesInLinearTime fails under -race -count=10 on
this head and on a clean main at e0bf3e61 alike (a 1s budget against ~1.2s under
the detector); it passes without -race.
---------
Co-authored-by: h4rK8s <63350517+h4rk8s@users.noreply.github.com>
85 lines
4.3 KiB
Go
85 lines
4.3 KiB
Go
// Package agentenv names the environment variables through which an agent
|
|
// really installed on this machine reaches magpie: the one list a test
|
|
// sandbox clears, so that a test meets only the agents it put there itself.
|
|
//
|
|
// A sandbox fakes magpie's own folders through HOME, USERPROFILE and the XDG
|
|
// variables (appdir decides those), and an agent's folder through the
|
|
// variable that agent's own CLI reads before it falls back to its folder in
|
|
// the home: CLAUDE_CONFIG_DIR, CODEX_HOME, QODER_CONFIG_DIR and the rest.
|
|
// Any one of them left over from the developer's shell points magpie at a
|
|
// real agent, whose sessions and settings a test then reads — and, where
|
|
// magpie writes what it wires, writes into (#522). Qoder's CLI sets both of
|
|
// its variables for every child it starts, and magpie manages Qoder, so
|
|
// writing magpie from inside Qoder was one way to meet this.
|
|
//
|
|
// Each package keeps its own sandbox, because a test in internal/sessions
|
|
// cannot import internal/agent, which imports it, to ask which variables
|
|
// there are; this is the list they all clear. A name here is one magpie
|
|
// reads, and TestVarsAreRead says so, so a variable magpie stopped reading
|
|
// does not linger — that is how GEMINI_CLI_HOME and OPENCODE_CONFIG came to
|
|
// be cleared by a sandbox while OPENCODE_CONFIG_DIR, the one magpie reads,
|
|
// was not. What is deliberately not here: APPDATA and LOCALAPPDATA, Windows'
|
|
// folders rather than an agent's, which a sandbox sets to a folder of its
|
|
// own instead of clearing; and CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC, a
|
|
// behaviour flag whose one test sets it on purpose to read what magpie then
|
|
// does.
|
|
package agentenv
|
|
|
|
// Vars are the variables, by the agent they belong to. A sandbox clears all
|
|
// of them, and sets back the ones its own fixtures need.
|
|
var Vars = []string{
|
|
// Claude Code, Codex and Copilot CLI
|
|
"CLAUDE_CONFIG_DIR", "CODEX_HOME", "COPILOT_HOME",
|
|
// Claude Code's temp folder, the images its sessions were given
|
|
"CLAUDE_CODE_TMPDIR",
|
|
// Zed, or a fork of it that keeps its settings (ZedG)
|
|
"MAGPIE_ZED_BIN", "MAGPIE_ZED_CONFIG_DIR", "MAGPIE_ZED_PROCESS_NAMES",
|
|
// Codex's state database, when it is kept apart from CODEX_HOME
|
|
"CODEX_SQLITE_HOME",
|
|
// Gemini CLI's session/config home
|
|
"GEMINI_CLI_HOME",
|
|
// Cline: its folder, its data, its sessions and its MCP settings file
|
|
"CLINE_DIR", "CLINE_DATA_DIR", "CLINE_SESSION_DATA_DIR", "CLINE_MCP_SETTINGS_PATH",
|
|
// Pi and its forks (OmO, Senpi), whose profile and config are apart
|
|
"PI_CODING_AGENT_DIR", "PI_CODING_AGENT_SESSION_DIR", "PI_CONFIG_DIR", "PI_PROFILE",
|
|
"OMO_CODING_AGENT_DIR", "SENPI_CODING_AGENT_DIR",
|
|
// omp
|
|
"OMP_PROFILE",
|
|
// Qoder's two builds, the global site's and China's
|
|
"QODER_CONFIG_DIR", "QODERCN_CONFIG_DIR",
|
|
// Grok: its home, and where its CLI is installed
|
|
"GROK_HOME", "GROK_BIN_DIR",
|
|
// Kimi Code and its shared folder
|
|
"KIMI_CODE_HOME", "KIMI_SHARE_DIR",
|
|
// MiMo Code, MiniMax Code, OpenHanako, Hermes, dsh, WorkBuddy, CodeBuddy Code
|
|
"MIMOCODE_HOME", "MINIMAX_DATA_DIR", "HANA_HOME", "HERMES_HOME", "DSH_HOME",
|
|
"WORKBUDDY_CONFIG_DIR", "CODEBUDDY_CONFIG_DIR",
|
|
// Reasonix Studio and its native CLI share this config home
|
|
"REASONIX_HOME",
|
|
// Mister Morph's config file
|
|
"MISTER_MORPH_CONFIG",
|
|
// T3 Code's base folder (its settings in userdata/)
|
|
"T3CODE_HOME",
|
|
// AtomCode's config folder
|
|
"ATOMCODE_HOME",
|
|
// Cursor's CLI: its config folder (its chats) and its data folder
|
|
"CURSOR_CONFIG_DIR", "CURSOR_DATA_DIR",
|
|
// OpenCode and OpenChamber
|
|
"OPENCODE_CONFIG_DIR", "OPENCODE_DB", "OPENCHAMBER_DATA_DIR",
|
|
// Droid's home, Windsurf's API server, ZCode's credential seed: what
|
|
// makes an account or an installation visible that the test didn't make
|
|
"FACTORY_HOME_OVERRIDE", "WINDSURF_API_SERVER_URL", "ZCODE_CREDENTIAL_SECRET",
|
|
}
|
|
|
|
// NotPaths are the Vars that name no folder or file under the working
|
|
// folder: a profile's name, a server's address, a secret, or a name its
|
|
// agent puts under a folder of its own (PI_CONFIG_DIR under the home,
|
|
// OPENCODE_DB in OpenCode's data folder), which a relative value is meant
|
|
// for.
|
|
var NotPaths = map[string]bool{
|
|
"PI_PROFILE": true, "OMP_PROFILE": true,
|
|
"WINDSURF_API_SERVER_URL": true, "ZCODE_CREDENTIAL_SECRET": true,
|
|
"PI_CONFIG_DIR": true, "OPENCODE_DB": true,
|
|
"MAGPIE_ZED_BIN": true, "MAGPIE_ZED_PROCESS_NAMES": true,
|
|
}
|