@mortiseai/stem 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +351 -83
- package/dist/cli.mjs +762 -706
- package/dist/daemon.mjs +502 -446
- package/dist/mcp.mjs +497 -441
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,46 +10,18 @@
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
## 项目状态
|
|
14
|
-
|
|
15
|
-
**Phase 0 · 脚手架骨架**(当前阶段)
|
|
16
|
-
- ✅ 项目结构、构建链、NPM 发布配置就绪
|
|
17
|
-
- ✅ `stem` 可直接启动,终端渲染 Welcome 屏
|
|
18
|
-
- ⬜ MSC Engine 集成(Phase 1+)
|
|
19
|
-
- ⬜ Claude API / 工具 / 命令 / 远程 / 多 Agent(Phase 1-5)
|
|
20
|
-
|
|
21
|
-
架构设计与完整路线图见 [`ARCHITECTURE.md`](./ARCHITECTURE.md)。
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
13
|
## 快速开始
|
|
26
14
|
|
|
27
15
|
### 前置依赖
|
|
28
|
-
- Node.js >= 22
|
|
29
|
-
- Bun https://bun.sh/ 开发模式 & 构建需要
|
|
16
|
+
- Node.js >= 22(源码开发/构建另需 Bun,见 [`MSTEM.md`](./MSTEM.md))
|
|
30
17
|
|
|
31
|
-
### npm
|
|
18
|
+
### npm 快速安装和启动(发布后)
|
|
32
19
|
```bash
|
|
33
20
|
npm install -g @mortiseai/stem
|
|
34
21
|
stem
|
|
35
22
|
```
|
|
36
23
|
macOS / Linux / Windows 均可,仅需 Node >= 22,无需 bun。
|
|
37
24
|
|
|
38
|
-
### 开发模式
|
|
39
|
-
```bash
|
|
40
|
-
bun install
|
|
41
|
-
bun run dev # bun 直跑源码
|
|
42
|
-
STEM_DEV=1 ./bin/stem.mjs # 等效:强制启动器走源码(忽略已构建的 dist)
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### 构建并运行
|
|
46
|
-
```bash
|
|
47
|
-
bun run build
|
|
48
|
-
./dist/cli.mjs
|
|
49
|
-
# 或通过 Node 启动器(自动选 dist,并加载仓库根 .env)
|
|
50
|
-
./bin/stem.mjs
|
|
51
|
-
```
|
|
52
|
-
|
|
53
25
|
### 退出
|
|
54
26
|
在 Welcome 屏按 **q** · **Esc** · 或 **Ctrl+C**。
|
|
55
27
|
|
|
@@ -57,20 +29,40 @@ bun run build
|
|
|
57
29
|
|
|
58
30
|
## 启动方式与命令行参数
|
|
59
31
|
|
|
60
|
-
###
|
|
32
|
+
### 启动范式(npm 全局安装)
|
|
61
33
|
|
|
62
34
|
```bash
|
|
35
|
+
npm install -g @mortiseai/stem
|
|
63
36
|
stem # 默认:agent 模式 · llms.json default 模型 · 系统语言
|
|
64
37
|
stem --lang en # 英文界面
|
|
65
38
|
stem --model BaiLian:glm-5.2 # 会话默认模型(platform:model 完整引用)
|
|
66
39
|
stem --model glm-5.2 # 裸模型 id(目录唯一命中,否则落默认平台)
|
|
67
40
|
stem --lang ja --model BaiLian:glm-5.2 # 组合使用
|
|
68
41
|
stem --resume <sessionId> # 恢复历史会话(续写同一份会话日志)
|
|
69
|
-
stem --tenant acme # 租户模式:全部落盘状态按 tenant/acme 隔离
|
|
70
|
-
stem --tenant acme --user 1000 # 租户内再按用户隔离存储数据:存储根 tenant/acme/1000
|
|
71
42
|
echo "一句话问答" | stem --bare # SIMPLE 模式:stdin → 单次回答(non-TTY)
|
|
72
43
|
```
|
|
73
44
|
|
|
45
|
+
开发启动(仓库源码 `bun run dev`,含 `--tenant`/`--user` 的 npm/bun flag 透传规则)与构建产物启动见 [`MSTEM.md`](./MSTEM.md)。
|
|
46
|
+
|
|
47
|
+
### tenant / user 隔离启动
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
stem # platform 作用域:落盘在 .mstem/platform 与 mstem-storage/platform
|
|
51
|
+
stem --user <用户ID> # platform + 用户隔离:用户空间 mstem-storage/platform/<用户ID>/
|
|
52
|
+
stem --tenant <租户代码> # 租户模式:全部落盘状态按 tenant/<租户代码> 隔离
|
|
53
|
+
stem --tenant <租户代码> --user <用户ID> # 租户内再按用户隔离:用户空间 mstem-storage/tenant/<租户代码>/<用户ID>/
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
四个作用域(platform / platform+user / tenant / tenant+user)两两绝对隔离;租户码/用户码只认启动 flag,不认 env。细则见下文「多租户隔离 · `--tenant`」与「用户隔离 · `--user`」两节。
|
|
57
|
+
|
|
58
|
+
### 空间初始化
|
|
59
|
+
|
|
60
|
+
一条命令升级全局 CLI 并补齐 `.mstem` / `mstem-storage` 目录骨架(platform 空间 + 已存在或 `-t` 指定的 tenant 空间;幂等,绝不覆盖已有文件;租户配置不播种,各租户的 llms.json / settings*.json / mcp.json 需手工放入 `.mstem/tenant/<code>/`,见「多租户隔离」节):
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
./update_pro.sh [目标项目目录] [-u <name>]... [-t <code>]... [--version <ver>] [--skip-upgrade|--skip-space]
|
|
64
|
+
```
|
|
65
|
+
|
|
74
66
|
### 运行形态总览
|
|
75
67
|
|
|
76
68
|
| 形态 | 启动方式 | 说明 |
|
|
@@ -82,7 +74,7 @@ echo "一句话问答" | stem --bare # SIMPLE 模式:stdin → 单次
|
|
|
82
74
|
| MCP stdio server | `./dist/mcp.mjs` | 把 stem 工具集暴露为 MCP server(stdio JSON-RPC),供其他 LLM 客户端接入 |
|
|
83
75
|
| 后台 daemon | `./dist/daemon.mjs [run\|status]` | 无 UI 常驻容器;`status` 输出一行 JSON 状态;SIGTERM/SIGINT 优雅退出 |
|
|
84
76
|
|
|
85
|
-
MCP / daemon
|
|
77
|
+
MCP / daemon 是独立构建产物,与 TUI 共用同一 MSC 容器逻辑(构建方式见 [`MSTEM.md`](./MSTEM.md)「构建与发布」节)。
|
|
86
78
|
|
|
87
79
|
### 无头模式(headless SDK 桥)· `--sdk-url`
|
|
88
80
|
|
|
@@ -123,8 +115,14 @@ stem --telemetry-url ws://127.0.0.1:8123/ws/manual
|
|
|
123
115
|
|
|
124
116
|
- ref 形式:`platform:model`(如 `BaiLian:glm-5.2`,平台名做存在性校验,打错立即报错退出并列出已知平台)或裸模型 id(目录唯一命中,否则落默认平台,不做白名单)
|
|
125
117
|
- 平台与模型目录来自 `.mstem/llms.json`(见下节)
|
|
126
|
-
-
|
|
127
|
-
|
|
118
|
+
- 解析优先级(**`llms.json` 的 `default` 是唯一的持久来源,settings 不参与模型选择**):
|
|
119
|
+
|
|
120
|
+
`/fast` 开启 > **会话级覆盖**(`--model` 启动参数 / 会话内 `/model`,同一个槽)> `llms.json` 的 `default`(工程层 `.mstem/<scope>/` > 用户层 `~/.mstem[/tenant/<code>]/`)> env `STEM_DEF_LLM_MODEL` > 首平台首模型
|
|
121
|
+
|
|
122
|
+
- `/model <ref>` 是**会话级**的:与 `--model` 写同一个槽,**不落盘**,退出即失效,后写胜出
|
|
123
|
+
- `/model default` 清掉该槽(连 `--model` 启动参数一起撤销),回落 `llms.json` 的 `default`
|
|
124
|
+
- **要改持久默认模型,就改 `llms.json` 的 `default`** —— 没有别的持久化入口
|
|
125
|
+
- `/model current` 可查看当前模型与来源
|
|
128
126
|
|
|
129
127
|
### 模型目录 · `.mstem/llms.json`
|
|
130
128
|
|
|
@@ -137,6 +135,64 @@ stem --telemetry-url ws://127.0.0.1:8123/ws/manual
|
|
|
137
135
|
- `vision`:该模型是否支持图片理解(布尔);未声明视为能力未知
|
|
138
136
|
- `default`:全局默认模型;`roles.fast`:`/fast` 快速模式用的模型
|
|
139
137
|
|
|
138
|
+
**per-model 深度思考开关**(`thinking`)—— 控制这个模型默认思不思考:
|
|
139
|
+
|
|
140
|
+
| 字段 | 必填 | 说明 |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| `thinking` | 否 | 布尔。`true` = 默认开启深度思考,`false` = 默认关闭。**不写 = 一个字段都不发**,由网关自己的默认决定 |
|
|
143
|
+
| `thinking_flag` | 否 | 开关字段名,缺省 `enable_thinking`。**仅 `protocol: "openai"` 有效**(见下)。写坏了整条开关不发 |
|
|
144
|
+
| `thinking_budget` | 否 | 思考预算 tokens,仅在 `thinking: true` 时同发;不写则不发 |
|
|
145
|
+
|
|
146
|
+
> **两条协议发的形状不同**(2026-08-08 实测,别改回统一形状):
|
|
147
|
+
>
|
|
148
|
+
> | 协议 | 关 | 开 |
|
|
149
|
+
> |---|---|---|
|
|
150
|
+
> | `openai` | `enable_thinking: false` | `enable_thinking: true` + `thinking_budget` |
|
|
151
|
+
> | `anthropic` | `thinking: {"type":"disabled"}` | `thinking: {"type":"enabled","budget_tokens":N}` |
|
|
152
|
+
>
|
|
153
|
+
> anthropic 侧必须用原生对象:百炼 `/apps/anthropic` 对顶层 `enable_thinking: false` **静默忽略**(照样回 thinking 块),换成 `{"type":"disabled"}` 才真关掉(同一问题 output_tokens 121 → 4);minimax `/anthropic` 同样认这个形状。`budget_tokens` 受网关硬校验 `max_tokens > budget_tokens`(不满足直接 400),本端会自动压到 `max_tokens - 1` 以内。
|
|
154
|
+
>
|
|
155
|
+
> `thinking: false` 也**会发**关闭指令 —— 这一档的主要用途就是关掉 qwen3 / glm 这类默认开思考的模型,不发字段就关不掉。开关按当前模型的目录条目取,子 agent 带自己的 model 时各取各的。
|
|
156
|
+
>
|
|
157
|
+
> 例外:`api.anthropic.com`(及 `*.anthropic.com`)**不发**。官方端点上 `budget_tokens` 必填且有下限、还与 `temperature`/`max_tokens` 互相牵制,这些约束当前配置表达不了;官方端点的推理深度由 `/effort` 的 system prompt 注入承担。
|
|
158
|
+
>
|
|
159
|
+
> 与内置搜索同样是**静默生效**的能力:网关不认时不报错也不降级,配完请实测一次(响应里有没有 `thinking` 块 / `reasoning_content` / `usage.completion_tokens_details.reasoning_tokens`)。写错的值一律静默忽略,`/doctor` 会列出具体问题。
|
|
160
|
+
|
|
161
|
+
**per-model 联网搜索**(`web_search` 工具)—— 各家模型的搜索形态不同,故按模型配置。**不写 `search` = 该模型没有搜索能力,工具压根不上送给模型**(保持"出网搜索由用户显式选择"的默认):
|
|
162
|
+
|
|
163
|
+
| 字段 | 适用档 | 必填 | 说明 |
|
|
164
|
+
|---|---|---|---|
|
|
165
|
+
| `search` | — | — | `model` / `mcp` / `api` |
|
|
166
|
+
| `search_flag` | model | 否 | 请求体上那路布尔开关的字段名,缺省 `enable_search`(百炼 / DashScope 口径);写 `false` 显式关掉这一路(只留工具形状) |
|
|
167
|
+
| `search_model_url` | model | 否 | 写了就走**独立请求档**:搜索单独打这个地址(OpenAI Chat Completions 形状),不再挂在主对话那次请求上。收 baseUrl 或完整 `/chat/completions` 两种写法 |
|
|
168
|
+
| `search_model_key` / `search_model` | model | 否 | 独立请求档的密钥与模型;key 省略回落平台 `apiKey`,model 省略 = 该条目自己的模型 id |
|
|
169
|
+
| `search_model_options` | model | 否 | 独立请求档的 `search_options` 子配置(`enable_source` / `forced_search` / `search_strategy` …),外加 **`enable_thinking`**(见下) |
|
|
170
|
+
| `search_mcp` | mcp | 是 | `"<server>"` 或 `"<server>/<tool>"`;server 名取自 `mcp.json`。只给 server 时自动挑搜索工具,**有歧义会报错并列出候选**,请按提示补 `/<tool>` |
|
|
171
|
+
| `search_mcp_key` | mcp | 否 | 该 MCP server 的鉴权密钥。**与平台 `apiKey` 可以不是同一把** —— 托管搜索服务常另发一把(百炼:对话走 Token Plan 的 `sk-sp-`,联网搜索认另一把)。这里只是声明,真正送进请求头/环境变量要在 `mcp.json` 里用 `$<平台>$<模型>$search_mcp_key` 引用,见下方「MCP 配置」节。值为全大写下划线时视为 **env 变量名** |
|
|
172
|
+
| `search_api_url` | api | 是 | 三方搜索 API 端点。按 host 自动识别 `api.tavily.com` / `google.serper.dev` / `api.search.brave.com`;**其它 host 走通用约定形状**:`POST {query, max_results}` → `{results:[{title,url,snippet}]}` |
|
|
173
|
+
| `search_api_key` | api | 否 | **可不设**(自建 / 免 key 端点不会带 `Authorization`)。上面三家已知 vendor 没有免 key 模式,缺 key 会直接报错。值为全大写下划线时视为 **env 变量名**——`llms.json` 随仓库提交,密钥请优先用 env 变量名或写在 `~/.mstem/llms.json` |
|
|
174
|
+
|
|
175
|
+
> `search: "model"` = **这个模型自己会搜,把它打开**。各家开法不同,所以这一档**两路都发**,网关各取所需、忽略不认识的那一路:
|
|
176
|
+
>
|
|
177
|
+
> 1. 以 `web_search_20250305` 工具形状上送(Anthropic 直连及兼容该 type 的网关)
|
|
178
|
+
> 2. 请求体加 `enable_search: true`(百炼 qwen 口径;字段名用 `search_flag` 改,`search_flag: false` 关掉这一路)
|
|
179
|
+
>
|
|
180
|
+
> 唯一例外:`api.anthropic.com`(及 `*.anthropic.com`)**不加第 2 路** —— 官方端点对未知顶层字段直接 400。搜与不搜由模型自己按问题判断,结果内联在回复里,客户端不派发 `web_search`。两路都不被网关认时表现为**静默无搜索**——不报错、也不降级,配完请实测一次。
|
|
181
|
+
>
|
|
182
|
+
> **搜索请求的深度思考 = 独立一档,由 `search_model_options.enable_thinking` 定**,与模型条目上那个管主对话的 `thinking` 互不相干 —— 独立请求档打的是 `search_model_url`,可能是另一家网关、另一个模型,没理由跟着对话走。不写 = 不发这个字段,跟搜索端点自己的默认。
|
|
183
|
+
>
|
|
184
|
+
> 注意**配置位置与线上位置不一致**:配置写在 `search_model_options` 里(它属于搜索端点这一摊),本端在拼请求体时会把它**提到顶层** —— 网关不读 `search_options` 子对象里的开关。自己手写 curl 时别搞错层级。
|
|
185
|
+
>
|
|
186
|
+
> **建议配 `false`**:这一轮的任务只是把网关注入的「参考资料」整理成一段带来源的摘要,推理链对结果没有贡献,只是让用户干等。2026-08-08 同一条搜索实测 —— 开:**84.9s** / completion 4040 tokens(其中 reasoning 3853);关:**3.9s** / 178 tokens,正文长度与注入的 `prompt_tokens` 都没变。排查「搜索怎么这么慢」先看日志 `modelSearch:done` 那行的 `thinking` 字段。
|
|
187
|
+
>
|
|
188
|
+
> 反过来,写进**内联档**的 `search_options.enable_thinking` 是无效的:那个对象进的是主对话那次请求,那里的思考由模型条目上的 `thinking` 管。`/doctor` 会点名这种层级错。
|
|
189
|
+
>
|
|
190
|
+
> **mcp 档的密钥不写进 `mcp.json`** —— 用 `$<平台>:apiKey`(平台那把)或 `$<平台>$<模型>$search_mcp_key`(这个 server 专用的那把)引用回 `llms.json`,见下方「MCP 配置」节。
|
|
191
|
+
>
|
|
192
|
+
> 配置写错(如 `search` 拼错、`mcp` 档漏了 `search_mcp`)一律静默降级为"该模型无搜索能力",`/doctor` 会列出具体问题。
|
|
193
|
+
>
|
|
194
|
+
> **免 key 的 DuckDuckGo / Brave SERP 抓取已于 2026-08-08 移除**,`STEM_WEBSEARCH_ENABLED` / `STEM_WEBSEARCH_PROVIDER` 两个 env 及同名 settings 一并删除。此前开启过 `STEM_WEBSEARCH_ENABLED=1` 的用户升级后会失去搜索能力,需改配 `search`。
|
|
195
|
+
|
|
140
196
|
完整示例:
|
|
141
197
|
|
|
142
198
|
```json
|
|
@@ -147,7 +203,7 @@ stem --telemetry-url ws://127.0.0.1:8123/ws/manual
|
|
|
147
203
|
"baseUrl": "https://api.anthropic.com",
|
|
148
204
|
"apiKeyEnv": "STEM_LLM_ANTHROPIC_API_KEY",
|
|
149
205
|
"models": {
|
|
150
|
-
"claude-sonnet-4-6": { "context": "200k" },
|
|
206
|
+
"claude-sonnet-4-6": { "context": "200k", "search": "model" },
|
|
151
207
|
"claude-haiku-4-5": { "context": "200k" }
|
|
152
208
|
}
|
|
153
209
|
},
|
|
@@ -160,7 +216,14 @@ stem --telemetry-url ws://127.0.0.1:8123/ws/manual
|
|
|
160
216
|
"qwen3.8-max-preview": {
|
|
161
217
|
"context": "1M",
|
|
162
218
|
"vision": true,
|
|
163
|
-
"pricing": { "input": 1.3, "output": 7.8, "cacheRead": 0.26, "cacheWrite": 0 }
|
|
219
|
+
"pricing": { "input": 1.3, "output": 7.8, "cacheRead": 0.26, "cacheWrite": 0 },
|
|
220
|
+
"search": "model"
|
|
221
|
+
},
|
|
222
|
+
"glm-5.2": {
|
|
223
|
+
"context": "1M",
|
|
224
|
+
"search": "api",
|
|
225
|
+
"search_api_url": "https://api.tavily.com/search",
|
|
226
|
+
"search_api_key": "STEM_TAVILY_API_KEY"
|
|
164
227
|
}
|
|
165
228
|
}
|
|
166
229
|
},
|
|
@@ -170,7 +233,7 @@ stem --telemetry-url ws://127.0.0.1:8123/ws/manual
|
|
|
170
233
|
"apiKeyEnv": "STEM_LLM_MINIMAX_API_KEY",
|
|
171
234
|
"authHeader": "bearer",
|
|
172
235
|
"models": {
|
|
173
|
-
"MiniMax-M2.7": { "context": "200k" },
|
|
236
|
+
"MiniMax-M2.7": { "context": "200k", "search": "mcp", "search_mcp": "brave/brave_web_search" },
|
|
174
237
|
"MiniMax-M2.7-highspeed": { "context": "200k" }
|
|
175
238
|
}
|
|
176
239
|
},
|
|
@@ -190,6 +253,219 @@ stem --telemetry-url ws://127.0.0.1:8123/ws/manual
|
|
|
190
253
|
}
|
|
191
254
|
```
|
|
192
255
|
|
|
256
|
+
### MCP 配置 · `.mstem/mcp.json`
|
|
257
|
+
|
|
258
|
+
MCP server 清单,搜索顺序:`$STEM_MCP_CONFIG_PATH` → `~/.mstem/mcp.json`(用户层)→ `<cwd>/.mstem/<scope>/mcp.json`(工程层,高优先)。顶层键 `servers`(也兼容 CC 布局的 `mcpServers`)。
|
|
259
|
+
|
|
260
|
+
每项是**本地 stdio** 或**远程 HTTP** 二选一:
|
|
261
|
+
|
|
262
|
+
| 形态 | 写法 | 说明 |
|
|
263
|
+
|---|---|---|
|
|
264
|
+
| stdio | `{ command, args?, env? }` | spawn 子进程 |
|
|
265
|
+
| 远程 | `{ type: "http", url, headers? }` | Streamable HTTP(现行 spec);`"streamable-http"` 同义 |
|
|
266
|
+
| 远程 | `{ type: "sse", url, headers? }` | 旧的 HTTP+SSE 传输。服务方尚未全部迁完,**百炼联网搜索目前只能走这一档** |
|
|
267
|
+
|
|
268
|
+
> 不写 `type` 但有 `url` 时按路径推断:`/sse` 结尾当 SSE,其余当 Streamable HTTP。认不出来的条目(既无 `command` 又无 `url`、`url` 不是 http(s)、`type: "ws"`)会被跳过并在日志里记一条 `mcp:config` warn ——**不再静默丢弃**。
|
|
269
|
+
|
|
270
|
+
**`env` 与 `headers` 的值都支持引用 `llms.json`**,两种语法:
|
|
271
|
+
|
|
272
|
+
| 语法 | 取什么 |
|
|
273
|
+
|---|---|
|
|
274
|
+
| `$<平台>:apiKey` / `$<平台>:baseUrl` | 平台级 —— 该平台的对话密钥 / baseUrl |
|
|
275
|
+
| `$<平台>$<模型>$search_mcp_key` | 模型级 —— 该模型条目上的 MCP 专用密钥(另有 `search_api_key` / `search_model_key`) |
|
|
276
|
+
|
|
277
|
+
模型级用 `$` 而非 `:` 作分隔,是因为模型 id 里 `.` / `-` 很常见(`MiniMax-M3`、`qwen3.8-max`),再用 `:` 会与平台级写法撞上。**引用可以内嵌在字符串中间**,所以 `"Bearer $x:apiKey"` 这种写法是合法的。
|
|
278
|
+
|
|
279
|
+
```json
|
|
280
|
+
{
|
|
281
|
+
"servers": {
|
|
282
|
+
"minimax": {
|
|
283
|
+
"command": "uvx",
|
|
284
|
+
"args": ["minimax-coding-plan-mcp", "-y"],
|
|
285
|
+
"env": {
|
|
286
|
+
"MINIMAX_API_KEY": "$minimax$MiniMax-M3$search_mcp_key",
|
|
287
|
+
"MINIMAX_API_HOST": "https://api.minimaxi.com"
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
"bailian-websearch": {
|
|
291
|
+
"type": "sse",
|
|
292
|
+
"url": "https://dashscope.aliyuncs.com/api/v1/mcps/WebSearch/sse",
|
|
293
|
+
"headers": {
|
|
294
|
+
"Authorization": "Bearer $bailian$qwen3.8-max$search_mcp_key"
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
- 引用在**建连之前**解析,所以对所有 MCP 消费方一致生效:`MCPTool`、`web_search` 的 mcp 档、`/mcp` 面板
|
|
302
|
+
- 平台 / 模型的密钥本身可以是字面量或 env 变量名,引用侧不用关心 —— 走的是同一套凭据解析。平台名与模型名都**大小写不敏感**
|
|
303
|
+
- 平台名未知、模型名未知、字段与该模型的 `search` 档不匹配、或拿不到可用密钥 → **不建连**,直接报错点名问题(带着一个字面量 `"$minimax:apiKey"` 去连,只会换来上游一句语义不明的鉴权错)。这类失败**不落连接失败缓存**,改完 `llms.json` 下次调用即可重试
|
|
304
|
+
- 报错文案里**不会出现密钥本身**,`/mcp` 面板也只展示 url / command,不展示 `headers` / `env`
|
|
305
|
+
- 要写以 `$` 开头的字面量值,用 `$$` 转义(`"$$FOO"` → `$FOO`)
|
|
306
|
+
|
|
307
|
+
**MCP 工具动态注册**:连接成功的 server,其工具会自动注册成 `mcp__<server>__<tool>` 形态直接进工具池(与 ClaudeCode 命名约定一致),模型可直接调用、权限规则可精确到单个工具。启动时连不上的 server 不注册,其工具仍可经 `MCPTool({server, tool, input})` 元工具兜底调用。
|
|
308
|
+
|
|
309
|
+
| 配置项 | 默认 | 说明 |
|
|
310
|
+
|---|---|---|
|
|
311
|
+
| `mcp.dynamicTools` | `true` | 关掉则不做动态注册,退回纯 `MCPTool` 元工具 |
|
|
312
|
+
| `mcp.dynamicToolsMaxTools` | `64` | 注册总数上限,溢出的工具仍可经元工具调用 |
|
|
313
|
+
|
|
314
|
+
工具名里的非法字符会被清洗成 `_`(server 名 `my.browser` → `mcp__my_browser__*`),实际名字可经 `/mcp` 面板查看。
|
|
315
|
+
|
|
316
|
+
### 浏览器自动化 · `/chrome`
|
|
317
|
+
|
|
318
|
+
让模型能启动 Chrome、导航、点击、填表、截图、读控制台与网络请求。
|
|
319
|
+
|
|
320
|
+
实现走 MCP 路线:把 Google 官方的 [`chrome-devtools-mcp`](https://github.com/ChromeDevTools/chrome-devtools-mcp) 作为 stdio MCP server 接入,其工具按上节规则动态注册成 `mcp__chrome-devtools__<tool>`。stem 不自己驱动浏览器 —— 可执行文件发现、profile 管理、attach 与退出清理都由 chrome-devtools-mcp 用 puppeteer-core 完成,因此 stem 侧**零新依赖**。
|
|
321
|
+
|
|
322
|
+
#### 快速开始
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
/chrome setup # 写入 ~/.mstem/mcp.json
|
|
326
|
+
/chrome connect # 连接(首次会用 npx 拉包,约 30–60 秒)
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
然后直接说「打开 example.com,告诉我页面标题」即可。
|
|
330
|
+
|
|
331
|
+
前置条件:Node ≥ 20.19(`^20.19 || ^22.12 || >=23`)、已安装 Chrome、首次运行需联网。
|
|
332
|
+
|
|
333
|
+
#### 子命令
|
|
334
|
+
|
|
335
|
+
| 命令 | 说明 |
|
|
336
|
+
|---|---|
|
|
337
|
+
| `/chrome` | 状态卡:配置来源、命令行、连接状态、本机 Chrome、Node 版本。**不发起连接** |
|
|
338
|
+
| `/chrome setup [flags]` | 幂等合并写入 mcp.json(保留同级其他 server) |
|
|
339
|
+
| `/chrome connect` | 断开重连并刷新动态工具目录 |
|
|
340
|
+
| `/chrome tools [过滤词]` | 列出工具(人读版) |
|
|
341
|
+
| `/chrome remove` | 从所属配置层删除条目 |
|
|
342
|
+
|
|
343
|
+
`setup` 的可选参数:
|
|
344
|
+
|
|
345
|
+
| 参数 | 作用 |
|
|
346
|
+
|---|---|
|
|
347
|
+
| `--headless` | 无头模式。默认**不开** —— 参考体验是用户看着 Chrome 自己动 |
|
|
348
|
+
| `--isolated` | 每次用全新临时 profile。默认**不开** —— 默认持久 profile 保留登录态,「在我账号里填这个表」才可用 |
|
|
349
|
+
| `--channel <c>` | `stable` / `beta` / `canary` / `dev` |
|
|
350
|
+
| `--executable-path <p>` | 指定 Chrome 可执行文件 |
|
|
351
|
+
| `--browser-url <url>` | attach 到已在运行的 Chrome(需其带 `--remote-debugging-port` 启动) |
|
|
352
|
+
| `--project` | 写到 `<cwd>/.mstem/mcp.json` 而非用户级 |
|
|
353
|
+
| `--force` | 条目已存在且与默认不同时才需要 |
|
|
354
|
+
|
|
355
|
+
#### 写入的配置
|
|
356
|
+
|
|
357
|
+
```json
|
|
358
|
+
{
|
|
359
|
+
"servers": {
|
|
360
|
+
"chrome-devtools": {
|
|
361
|
+
"command": "npx",
|
|
362
|
+
"args": ["-y", "chrome-devtools-mcp@latest",
|
|
363
|
+
"--viewport", "1280x720",
|
|
364
|
+
"--screenshotFormat", "webp",
|
|
365
|
+
"--screenshotMaxWidth", "1568"]
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
几个参数是踩过坑才定下来的,手改时别删:
|
|
372
|
+
|
|
373
|
+
- **`-y`** —— MCP 的 stdio transport 以 `shell: false` spawn 子进程。没有 `-y` 时 npx 会把 "Ok to proceed?" 提示写进握手用的同一条 stdio,连接挂死直到超时,然后落成 failed
|
|
374
|
+
- **Windows 上是 `npx.cmd`** —— 同样因为 `shell: false`,`"npx"` 在 Windows 会 ENOENT。`/chrome setup` 会按平台自动写对
|
|
375
|
+
- **`--screenshotFormat webp` + `--screenshotMaxWidth 1568`** —— 截图直接决定 token 成本,这是主要的尺寸控制手段。stem 侧还有一道 5MB 兜底,超限会丢图并提示改用 webp
|
|
376
|
+
|
|
377
|
+
配置改动**无需重启** —— MCP 配置按 mtime/size 指纹热更新。
|
|
378
|
+
|
|
379
|
+
#### 权限
|
|
380
|
+
|
|
381
|
+
动态注册后每个浏览器动作都是独立工具名,可精确配规则(写在 `.mstem/settings.local.json` 或 `~/.mstem.json`):
|
|
382
|
+
|
|
383
|
+
```json
|
|
384
|
+
{
|
|
385
|
+
"permissions": {
|
|
386
|
+
"allow": ["mcp__chrome-devtools__*"],
|
|
387
|
+
"ask": [
|
|
388
|
+
"mcp__chrome-devtools__evaluate_script",
|
|
389
|
+
"mcp__chrome-devtools__upload_file"
|
|
390
|
+
]
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
裁决优先级 `deny > ask > allow`,所以上面的 `ask` 条目会压过通配 `allow` —— 常规操作免弹卡,执行任意 JS 与上传文件仍逐次确认。
|
|
396
|
+
|
|
397
|
+
工具名尾部 `*` 是前缀匹配,为动态 MCP 工具新增的语法;带括号的 pattern(如 `Bash(npm *)`)语义不变。注意兜底路径:未注册成功的工具经 `MCPTool` 调用时,按字面名 `MCPTool` 鉴权,`mcp__*` 规则对它不生效。
|
|
398
|
+
|
|
399
|
+
#### 故障排查
|
|
400
|
+
|
|
401
|
+
`/chrome` 与 `/chrome connect` 会把常见错误翻成一句能照做的提示:
|
|
402
|
+
|
|
403
|
+
| 现象 | 处理 |
|
|
404
|
+
|---|---|
|
|
405
|
+
| `spawn npx ENOENT` | 装 Node.js 并确保在 PATH 上 |
|
|
406
|
+
| 首次 connect 超时 | 正在下载包。重试,或先跑 `npx -y chrome-devtools-mcp@latest --help` 预热 |
|
|
407
|
+
| `Could not find Chrome` | 装 Chrome,或 `/chrome setup --executable-path <路径>` |
|
|
408
|
+
| `SingletonLock` / profile 被占用 | `/chrome setup --isolated` |
|
|
409
|
+
| `ECONNREFUSED`(用了 `--browser-url`) | 目标端口没有 Chrome 在监听,先带 `--remote-debugging-port` 启动 Chrome |
|
|
410
|
+
| 会话中途杀掉 Chrome 后一直报错 | 等 60 秒(连接失败缓存 TTL)或直接 `/chrome connect` |
|
|
411
|
+
| 模型说「我截图了」但描述不出画面 | 图没送到。查日志 `mcpTool:result` 的 `images` 字段是否为 0 |
|
|
412
|
+
|
|
413
|
+
#### 实现要点
|
|
414
|
+
|
|
415
|
+
改动集中在这几处,排查问题时按图索骥(均在 `src/stem-msce/msc-project/mai-cli-server/` 下):
|
|
416
|
+
|
|
417
|
+
- `logic/services/mcp-logic/source-stem/clientManager.ts` — `McpContentBlock` 类型、tool annotations、失败态 TTL、连接/调用超时、`reconnectServer` / `disconnectServer`
|
|
418
|
+
- `logic/tools/mcp-tool-logic/source/mcpToolCore.ts` — `splitContent()` 把 MCP image 块抬成 `ToolInvokeResult.imageBlocks`(此前一律降级成字面量 `[image]`,截图直接蒸发)
|
|
419
|
+
- `logic/tools/_shared/mcpToolCatalog.ts` — 动态注册目录:名称清洗、`readOnlyHint` → `isReadOnly`、描述截断、数量上限、总开关
|
|
420
|
+
- `logic/tools/registry.ts` — `ToolDescriptor.mcp` 字段、`getTool` 兜底查目录、`buildToolDefs` 追加
|
|
421
|
+
- `logic/session/session-logic/MaiCliServerSessionLogic.ts` — 构建 toolDefs 前刷新目录(带 3s 预算,连不上不阻塞会话);派发时把扁平入参包回 `{server, tool, input}`
|
|
422
|
+
- `logic/core/permission-logic/source/permissionRules.ts` — 工具名尾 `*` 前缀匹配
|
|
423
|
+
- `logic/core/system-prompt-logic/source/systemPrompt.ts` — `buildBrowserPromptSection()`,仅在工具池真有浏览器工具时注入「先取快照拿 uid、再按 uid 操作」的用法约定
|
|
424
|
+
|
|
425
|
+
自检脚本 `bun scripts/smoke-chrome-mcp.ts`(不需要 Chrome、不需要联网)覆盖截图直通、动态注册、连接失败 TTL、配置写入器与权限通配。
|
|
426
|
+
|
|
427
|
+
### 阿里百炼联网搜索 · `/bailian`
|
|
428
|
+
|
|
429
|
+
把百炼托管的联网搜索 MCP 接进来,作为 `search: "mcp"` 档的后端。与 `/chrome` 同构,差别在于它是**远程 SSE** server 而非本地 stdio。
|
|
430
|
+
|
|
431
|
+
#### 快速开始
|
|
432
|
+
|
|
433
|
+
```
|
|
434
|
+
/bailian setup # 写入 mcp.json(条目名 bailian-websearch)
|
|
435
|
+
/bailian connect # 连接
|
|
436
|
+
/bailian tools # 确认工具已就绪(只列工具,不计搜索次数)
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
再到 `llms.json` 给模型加上路由:
|
|
440
|
+
|
|
441
|
+
```json
|
|
442
|
+
"qwen3.8-max": {
|
|
443
|
+
"search": "mcp",
|
|
444
|
+
"search_mcp": "bailian-websearch/bailian_web_search",
|
|
445
|
+
"search_mcp_key": "sk-ws-…"
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
`search_mcp_key` 与平台 `apiKey` **不是同一把** —— 实测平台的 Token Plan key(`sk-sp-`)打这个端点直接 `401 InvalidApiKey`,要用开通联网搜索时拿到的那把。`setup` 默认写平台级引用,发现模型上已有 `search_mcp_key` 时用 `--model <id>` 让它改写成模型级引用。
|
|
450
|
+
|
|
451
|
+
#### 子命令
|
|
452
|
+
|
|
453
|
+
| 命令 | 说明 |
|
|
454
|
+
|---|---|
|
|
455
|
+
| `/bailian` | 状态卡:配置来源、端点、引用能否解析、连接状态、平台密钥、**当前模型的搜索是否真的指向本 server**。不发起连接 |
|
|
456
|
+
| `/bailian setup [flags]` | 幂等合并写入 mcp.json(保留同级其他 server) |
|
|
457
|
+
| `/bailian connect` | 断开重连并刷新动态工具目录 |
|
|
458
|
+
| `/bailian tools [过滤词]` | 列出工具(人读版) |
|
|
459
|
+
| `/bailian remove` | 从所属配置层删除条目 |
|
|
460
|
+
|
|
461
|
+
`setup` 的可选参数:`--platform <id>`(引用哪个平台,缺省 `bailian`)、`--model <id>`(改用该模型的 `search_mcp_key`)、`--project`(写工程层)、`--force`(覆盖已有条目)。
|
|
462
|
+
|
|
463
|
+
> 端点用 `/WebSearch/mcp`(Streamable HTTP,与官方文档一致)。百炼在 2026-08-08 ~ 08-10 间完成了 SSE → Streamable HTTP 的服务端迁移(此前 `/mcp` 回 405、只有 `/sse` 通;迁移后倒过来,`/sse` 只回空流)。若连接报 `Invalid content type` 一类错,先用 curl 对照两个路径确认服务端协议,再改 `type`。套餐专属 workspace 域名(`llm-<id>.<region>.maas.aliyuncs.com`)下同路径也可用。
|
|
464
|
+
>
|
|
465
|
+
> 连上之后 `bailian_web_search` **不会**出现在模型的工具池里 —— 被 `search_mcp` 指为搜索后端的工具由 `hideSearchBackends` 摘掉,搜索统一走 `web_search` 门面按当前模型路由。这是预期行为,不是漏注册。
|
|
466
|
+
>
|
|
467
|
+
> 计费:前 2000 次调用免费,之后按次计。`/bailian tools` 只列工具不计费,是验证连通性最便宜的一步。
|
|
468
|
+
|
|
193
469
|
### 业务模式 · `--agent` / `--coding` / `--fde`
|
|
194
470
|
|
|
195
471
|
Stem 的三种核心业务模式:
|
|
@@ -215,11 +491,8 @@ Stem 的三种核心业务模式:
|
|
|
215
491
|
settings `env` 段里的 `STEM_TENANT` 一律被入口清除,不作为输入
|
|
216
492
|
(`STEM_TENANT` 是内部传播通道,不要手工设置)。自拉起 stem 子进程时须显式
|
|
217
493
|
转发 `--tenant`。
|
|
218
|
-
- **经 npm/bun
|
|
219
|
-
|
|
220
|
-
中继)或 `npm run dev -- --tenant acme`。npm 下的空格形式
|
|
221
|
-
`npm run dev --tenant acme` 值会被 npm 吞成布尔 — CLI 检测到后直接报错
|
|
222
|
-
退出(绝不静默落个人模式)。
|
|
494
|
+
- **经 npm/bun 脚本启动**(仓库源码开发):flag 透传规则见
|
|
495
|
+
[`MSTEM.md`](./MSTEM.md)「开发启动」节。
|
|
223
496
|
- **租户码**:1–64 字符,小写字母/数字开头,允许 `.` `_` `-`(拒绝大写与路径
|
|
224
497
|
分隔符);非法直接报错退出。
|
|
225
498
|
|
|
@@ -274,10 +547,9 @@ Stem 的三种核心业务模式:
|
|
|
274
547
|
优先级最高,但不落盘,退出即消失,Group 的配置文件一个字节都不会被改。
|
|
275
548
|
- 用户码格式与租户码相同(1–64 字符,小写字母/数字开头,允许 `.` `_` `-`);
|
|
276
549
|
非法直接报错退出,与 `--tenant` 是否合法/是否传入无关。
|
|
277
|
-
- **不做 npm 中继**(与 `--tenant` 的 `npm_config_tenant` 中继不同):
|
|
278
|
-
`
|
|
279
|
-
|
|
280
|
-
`npm run dev -- --user <id>` 透传。
|
|
550
|
+
- **不做 npm 中继**(与 `--tenant` 的 `npm_config_tenant` 中继不同):npm
|
|
551
|
+
脚本下需要 `--user` 时用 `npm run dev -- --user <id>` 透传,细则见
|
|
552
|
+
[`MSTEM.md`](./MSTEM.md)「开发启动」节。
|
|
281
553
|
|
|
282
554
|
举例:`stem --user 1000` → 用户空间 `<cwd>/mstem-storage/platform/1000/`
|
|
283
555
|
(会话/洞察/skills/agents/workflows/memory/plugins 全部在内),llms/settings
|
|
@@ -339,14 +611,16 @@ CLI 的全部运行时开关统一放 settings 级联的 **`env` 段**(不再使
|
|
|
339
611
|
|---|---|
|
|
340
612
|
| `STEM_DEF_LANGUAGE` | 界面语言 `zh\|en\|ja\|ko`(`--lang` 的兜底) |
|
|
341
613
|
| `STEM_DEF_MODE` | 业务模式(`--agent` 等的兜底) |
|
|
342
|
-
| `STEM_DEF_LLM_MODEL` |
|
|
614
|
+
| `STEM_DEF_LLM_MODEL` | 模型引用;优先级低于 `--model` / `/model` **与 `llms.json` 的 `default`** —— 只在没配 `default` 时兜底 |
|
|
343
615
|
| `STEM_DEBUG` | `1` = 渲染 tool-use/tool-result 调试卡片 + Debug banner |
|
|
344
616
|
| `STEM_API_RETRY_INTERVAL_MS` | LLM 网络错误重试间隔(默认 60000,最多 10 次) |
|
|
345
617
|
| `STEM_API_TIMEOUT_MS` | LLM 请求全局超时(平台未配 `timeoutMs` 时生效,默认 600000) |
|
|
346
618
|
| `STEM_PERMISSION_MODE` | 工具权限模式 `default\|allow\|plan\|bypassPermissions\|...` |
|
|
619
|
+
| `STEM_MCP_CONNECT_TIMEOUT_MS` | MCP server 连接超时(默认 120000;首次 npx 拉包 + 浏览器冷启动可能很久) |
|
|
620
|
+
| `STEM_MCP_CALL_TIMEOUT_MS` | MCP 单次工具调用超时(默认 120000;性能 trace 一类会很慢) |
|
|
621
|
+
| `STEM_MCP_FAILED_TTL_MS` | MCP 连接失败后多久允许重试(默认 60000) |
|
|
347
622
|
| `STEM_LOOP_CRON_ENABLED` | `1` = 开启 `/loop` 后台 cron(默认关,opt-in 安全门) |
|
|
348
623
|
| `STEM_HUD_SHOW_USAGE` / `STEM_HUD_SHOW_WEEK` | HUD 用量行 / 本周行显示开关 |
|
|
349
|
-
| `STEM_WEBSEARCH_ENABLED` / `STEM_WEBSEARCH_PROVIDER` | web_search 总开关 / provider(`auto\|duckduckgo\|brave-web`) |
|
|
350
624
|
| `STEM_TELEMETRY` | `1` = 无头模式开启 telemetry 观测帧(`--sdk-telemetry` 的 env 形式) |
|
|
351
625
|
| `STEM_TELEMETRY_WS` | telemetry WS tap 地址 |
|
|
352
626
|
| `STEM_STORAGE_DIR` | 用户空间根(缺省 `<cwd>/mstem-storage`;优先于 settings `storage.dir`;自动追加作用域+用户段 —— `platform/<user>`,租户模式下 `tenant/<code>/<user>`)。只承载 `--user` 用户空间;Group 数据(session/causal/distill/evolve)落 STEM_DIR 项目配置根,跟随 `STEM_DIR` 重定向 |
|
|
@@ -364,41 +638,39 @@ CLI 的全部运行时开关统一放 settings 级联的 **`env` 段**(不再使
|
|
|
364
638
|
|
|
365
639
|
---
|
|
366
640
|
|
|
367
|
-
##
|
|
641
|
+
## 配置结构说明
|
|
368
642
|
|
|
369
|
-
|
|
370
|
-
bun run build # 生成 dist/cli.mjs
|
|
371
|
-
npm pack # 本地产出 tarball,检查清单
|
|
372
|
-
# → tar -tf *.tgz 应含 bin/ dist/ preload.ts README.md LICENSE
|
|
643
|
+
两个配置根均落在目标项目目录下,可用 env `STEM_DIR` / `STEM_STORAGE_DIR` 重定向(见「运行时开关」):
|
|
373
644
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
`prepublishOnly` 会在发布前自动触发一次 `bun run build`。
|
|
378
|
-
|
|
379
|
-
---
|
|
380
|
-
|
|
381
|
-
## 目录结构速览
|
|
645
|
+
- **`.mstem/`** — 项目配置根:配置文件 + Group 数据(session/causal/distill/evolve 等)
|
|
646
|
+
- **`mstem-storage/`** — 用户空间根:只承载 `--user` 用户隔离数据,不放配置文件
|
|
382
647
|
|
|
383
648
|
```
|
|
384
|
-
|
|
385
|
-
├──
|
|
386
|
-
├──
|
|
387
|
-
│ ├──
|
|
388
|
-
│ ├──
|
|
389
|
-
│ ├──
|
|
390
|
-
│
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
├──
|
|
396
|
-
├──
|
|
397
|
-
|
|
398
|
-
└──
|
|
649
|
+
.mstem/ # 项目配置根(STEM_DIR)
|
|
650
|
+
├── platform/ # platform 作用域(./update_pro.sh 或 scripts/update-platform-space.sh 补齐+播种)
|
|
651
|
+
│ ├── settings.json # 团队项目级配置(随仓库提交)
|
|
652
|
+
│ ├── settings.local.json # 个人项目级配置(应 gitignore;env 段放运行时开关)
|
|
653
|
+
│ ├── llms.json # 多平台 LLM 目录(见「模型目录」节)
|
|
654
|
+
│ ├── mcp.json # MCP server 配置
|
|
655
|
+
│ └── agents/ causal/ distill/ evolve/ memory/ plugins/ session/ skills/ workflows/
|
|
656
|
+
└── tenant/<租户代码>/ # tenant 作用域:结构同 platform,首启自动建目录
|
|
657
|
+
└── … # 但配置文件不播种,需手工放置
|
|
658
|
+
|
|
659
|
+
mstem-storage/ # 用户空间根(STEM_STORAGE_DIR)
|
|
660
|
+
├── platform/
|
|
661
|
+
│ ├── common/ # 未指定 --user 时的公共用户空间
|
|
662
|
+
│ └── <用户ID>/ # stem --user <用户ID> 的用户空间
|
|
663
|
+
│ └── agents/ causal/ … workflows/(九个子目录同上)
|
|
664
|
+
└── tenant/<租户代码>/
|
|
665
|
+
├── common/ # 租户内公共用户空间
|
|
666
|
+
└── <用户ID>/ # stem --tenant <code> --user <id> 的用户空间
|
|
399
667
|
```
|
|
400
668
|
|
|
401
|
-
|
|
669
|
+
- 四个作用域(platform / platform+user / tenant / tenant+user)两两绝对隔离,详见「多租户隔离」「用户隔离」两节
|
|
670
|
+
- 会话聊天文件默认落 `.mstem/platform/session/<sessionId>.jsonl`;`--user` 时落 `mstem-storage/platform/<用户ID>/session/`
|
|
671
|
+
- settings 级联(对**全部 settings key** 生效,不止 `env`/权限段):`.mstem/settings.local.json` > `.mstem/settings.json` > `~/.mstem.json`(用户全局)——「当前工程 `.mstem` 配置 > `~/.mstem.json`」
|
|
672
|
+
- 写入侧防遮蔽:设置默认仍落用户层(`theme`/`advisor` 这类跨工程偏好),但工程层已定义该 key 时就地改工程层;取消设置则两侧一起清
|
|
673
|
+
- 骨架补齐与配置播种统一走 `./update_pro.sh`(幂等,绝不覆盖已有文件,见「空间初始化」节)
|
|
402
674
|
|
|
403
675
|
---
|
|
404
676
|
|
|
@@ -415,7 +687,3 @@ mortise_ai_cli_project/
|
|
|
415
687
|
|
|
416
688
|
---
|
|
417
689
|
|
|
418
|
-
## 开发约定
|
|
419
|
-
|
|
420
|
-
见 [`AGENTS.md`](./AGENTS.md) 和 [`ARCHITECTURE.md`](./ARCHITECTURE.md) 的「开发约定」章节。
|
|
421
|
-
|