Skip to content

Commit 9f6138a

Browse files
authored
fix(managed-agent): Share the home-expansion and spawn-environment rules of the compatibility evaluation (#12934)
* fix(managed-agent): Share the home-expansion and spawn-environment rules of the compatibility evaluation Takes the non-blocking suggestions from the review and verification of #12903. - storage-paths-lite exports expandsAgainstHome. resolveConfigPathLite and the evaluation share it, and a test pins the lite helper to core's Storage.getGlobalQwenDir for every form of QWEN_HOME. - passedEnvironment models what Node's spawn passes on: every enumerable key, inherited ones included, whose value is not undefined, as a string, and on Windows the first spelling of each name in sorted order. It throws for an environment a session host would not receive as it is: a missing one, a variable that cannot be read, a Symbol value or a NUL byte, and a name that contains `=`. - The evaluation reads the environment through it once, and every rule reads that copy. The placeholder view leaves out names like an array index, for which a host's process.env returns nothing, and on Windows finds any spelling for lookups and `in` tests. - The evaluation reads the environment and then the home directory, resolving it as settings loading does, before it checks the locations, and reports "the home directory or the environment could not be read" when either fails. - Two false compatible answers are gone: a name such as QWEN_HOME=alt, which a host receives as QWEN_HOME=alt=home, is now unknown, and a placeholder such as $0, which a host keeps as it is, now stays too. - Both design docs describe the rules, gain the new row, and ask M6 to spawn the hosts with the variables the evaluation read. Part of #12737 and #12380. * fix(managed-agent): Log why the home or environment could not be read, and share the home resolution Takes the three suggestions of the /review on #12934 and closes the oracle gap its triage raised. - R1-1: the evaluation logs the error behind "the home directory or the environment could not be read" through a debug logger, and the errors passedEnvironment raises about a variable name it and leave its value out. - R1-2: settings.ts exports resolveHomeDirectory, which settings loading and the evaluation both use, and a snapshot test pins that settings loading throws when the home directory cannot be resolved. - R1-3: readEnvironmentVariable documents that for process.env itself it reads the current process's own value. - A test spawns a real Node process and checks that it reads what the view says, for inherited keys, null, empty and undefined values, two spellings of one name and names like an array index. Part of #12737 and #12380.
1 parent a77d80d commit 9f6138a

8 files changed

Lines changed: 727 additions & 108 deletions

‎docs/design/2026-09-27-ordinary-host-managed-engine.md‎

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -318,19 +318,28 @@ approval mode. The runtime carries:
318318
and system settings files and the extension store; without `QWEN_HOME`, the
319319
user directory is under the process's home directory. It is also the only
320320
source for placeholders, and it already carries the user-level `.env` values
321-
the runtime applied. On Windows, the locating names and the placeholders are
322-
read as a spawned session host sees them: case-insensitively, and of several
323-
spellings the first in sorted order. A location that depends on the working
324-
directory is `unknown`, because a session host resolves it against its own:
325-
a relative path, on Windows also a path without a drive root or a UNC server
326-
and share, and a value that is not a string. So is an empty or relative home
327-
directory, which settings loading resolves both for the user directory and
328-
to tell whether the workspace is the home directory. `QWEN_HOME` may be `~`
329-
or start with `~/` or `~\`, which expands against the home directory. In the
330-
daemon, the locating variables and `HOME` are excluded from workspace
331-
overlays, and on Windows an overlay does not replace the `USERPROFILE` that
332-
gives the home directory once it is set, so they equal the daemon's own; in a
333-
session host, they are its own environment.
321+
the runtime applied. The evaluation reads it once, and M6 must spawn the
322+
hosts with the same variables. The locating variables and the placeholders
323+
are read as a spawned session host receives them: as strings; on Windows,
324+
case-insensitively, and of several spellings the first in sorted order. A
325+
variable named like an array index, such as `0`, is left out, since a Node
326+
process gets no value for such a name from `process.env`. An environment
327+
that a host would not receive as it is cannot be read: a missing one, for
328+
which spawn passes on its own; a variable that cannot be read, a Symbol value
329+
or a NUL byte, which spawn refuses or which cuts the value short; and a name
330+
that reaches the host as another name because it contains `=`. A location
331+
that depends on the working directory is `unknown`, because a session host
332+
resolves it against its own: a relative path, and on Windows also a path
333+
without a drive root or a UNC server and share. So is an empty or relative
334+
home directory, which settings loading resolves both for the user directory
335+
and to tell whether the workspace is the home directory. A home directory
336+
that cannot be looked up cannot be read, and neither can any other that
337+
cannot be resolved, for example because it does not exist. `QWEN_HOME` may
338+
be `~` or start with `~/` or `~\`, which expands against the home
339+
directory. In the daemon, the locating variables and `HOME` are excluded
340+
from workspace overlays, and on Windows an overlay does not replace the
341+
`USERPROFILE` that gives the home directory once it is set, so they equal
342+
the daemon's own; in a session host, they are its own environment.
334343
- **Project MCP file.** In strict mode, a read failure of `.mcp.json` is kept as
335344
an error instead of counting as an absent file. In both modes, a file that
336345
does not parse, has no `mcpServers` object or holds an entry that is not an
@@ -371,6 +380,7 @@ The first condition that applies decides the result:
371380
| `--restore-ask-user-question` is forwarded | deferred |
372381
| Another argument is forwarded | unknown |
373382
| The running workspace holds MCP servers outside its files | deferred |
383+
| The home directory or the environment cannot be read | unknown |
374384
| A settings location in the environment depends on the working directory | unknown |
375385
| A settings layer cannot be read | unknown |
376386
| MCP servers in any settings layer, or `mcp.serverCommand` | deferred |

‎docs/design/2026-09-27-ordinary-host-managed-engine.zh-CN.md‎

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -251,15 +251,20 @@ M3 把 B2d 设计中的配置兼容契约实现为 CLI 配置层中的一个函
251251
- **环境。** 传入的环境就是该 runtime 的会话宿主所用的环境。其中的 `QWEN_HOME` 与
252252
两个系统 settings 路径用来定位用户与系统 settings 文件和 extension store;没有
253253
`QWEN_HOME` 时,用户目录位于进程的 home 目录下。它也是占位符的唯一来源,并且已经
254-
包含 runtime 应用的用户级 `.env` 值。在 Windows 上,定位变量名和占位符按被 spawn 的
255-
会话宿主所见的方式读取:不区分大小写,多种拼写并存时取排序最前的一种。依赖工作目录
256-
的位置判为 `unknown`,因为会话宿主会按它自己的工作目录解析:相对路径,Windows 上
257-
没有盘符根或没有 UNC 服务器与共享名的路径,以及不是字符串的值。为空或为相对路径的
258-
home 目录同样如此,settings 加载既用它确定用户目录,也用它判断工作区是否就是 home
259-
目录。`QWEN_HOME` 可以是 `~`,或以 `~/`、`~\` 开头,它按 home 目录展开。在
260-
daemon 中,这些定位变量和 `HOME` 不允许被工作区覆盖,Windows 上给出 home 目录的
261-
`USERPROFILE` 一旦设置也不会被覆盖所替换,所以它们与 daemon 自己的值相同;在会话
262-
宿主中,它们就是宿主自己的环境。
254+
包含 runtime 应用的用户级 `.env` 值。评估只读取它一次,M6 必须用同样的变量 spawn
255+
宿主。定位变量和占位符按被 spawn 的会话宿主收到的方式读取:取字符串形式;在
256+
Windows 上不区分大小写,多种拼写并存时取排序最前的一种。名字像数组下标的变量(例如
257+
`0`)不计入,因为 Node 进程从 `process.env` 取不到这类名字的值。会话宿主无法原样
258+
收到的环境判为无法读取:缺失的环境,spawn 会改为传递它自己的环境;无法读取的变量、
259+
Symbol 值或 NUL 字节,spawn 会拒绝,或者值会在该处被截断;以及因为含有 `=` 而让
260+
宿主收到另一个名字的变量名。依赖工作目录的位置判为 `unknown`,因为会话宿主会按
261+
它自己的工作目录解析:相对路径,以及 Windows 上既没有盘符根、也没有 UNC 服务器与
262+
共享名的路径。为空或为相对路径的 home 目录同样如此,settings 加载既用它确定用户
263+
目录,也用它判断工作区是否就是 home 目录。无法查到的 home 目录判为无法读取,其他
264+
无法解析的 home 目录(例如不存在)同样如此。`QWEN_HOME` 可以是 `~`,或以 `~/`、
265+
`~\` 开头,它按 home 目录展开。在 daemon 中,这些定位变量和 `HOME` 不允许被工作区
266+
覆盖,Windows 上给出 home 目录的 `USERPROFILE` 一旦设置也不会被覆盖所替换,所以
267+
它们与 daemon 自己的值相同;在会话宿主中,它们就是宿主自己的环境。
263268
- **项目 MCP 文件。** 严格模式下,`.mcp.json` 的读取失败会作为错误保留,而不是当作
264269
文件不存在。两种模式下,无法解析、没有 `mcpServers` 对象,或含有不是对象的条目的
265270
文件都算错误。
@@ -293,6 +298,7 @@ M3 把 B2d 设计中的配置兼容契约实现为 CLI 配置层中的一个函
293298
| 转发了 `--restore-ask-user-question` | deferred |
294299
| 转发了其他参数 | unknown |
295300
| 运行中的工作区持有配置文件之外的 MCP 服务器 | deferred |
301+
| 无法读取 home 目录或环境 | unknown |
296302
| 环境中的某个 settings 位置依赖工作目录 | unknown |
297303
| 某一层 settings 无法读取 | unknown |
298304
| 任何一层 settings 中有 MCP 服务器,或配置了 `mcp.serverCommand` | deferred |

0 commit comments

Comments
 (0)