pi-multi-viewers 0.8.3 → 0.9.0
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/AGENTS.md +2 -2
- package/README.md +43 -17
- package/docs/design.md +3 -2
- package/extensions/multi-viewers/index.ts +4 -3
- package/meeting_fs.py +80 -36
- package/meeting_loop.py +34 -15
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -68,7 +68,7 @@ tests/ 测试(unittest discover tests)
|
|
|
68
68
|
|
|
69
69
|
| 档 | 唤醒命令 | 用途 |
|
|
70
70
|
|---|---|---|
|
|
71
|
-
| **mc-tools**(默认) | 四个 `--no-*` + `-e <MC 的 subagent-entry.js>` | 给 agents **按需检索项目背景**(`ctx_search`)——背景蒸馏机制已移除,这是其补充通道。**允许而非要求 MC**:找不到 MC → 降级为零扩展 + 一行可见说明(`ctx_search` 本次不可用)|
|
|
71
|
+
| **mc-tools**(默认) | 四个 `--no-*` + `-e <MC 的 subagent-entry.js>` + `-e <pi-mcp-adapter 入口>` | 给 agents **按需检索项目背景**(`ctx_search`)——背景蒸馏机制已移除,这是其补充通道。**允许而非要求 MC**:找不到 MC → 降级为零扩展 + 一行可见说明(`ctx_search` 本次不可用)|
|
|
72
72
|
| **none** | 四个 `--no-*` | 零扩展、**零依赖**(无 MC 的机器/CI 用这档)|
|
|
73
73
|
| **all** | 不加任何 `--no-*`(pi 默认发现)| A/B 实验与显式 opt-in |
|
|
74
74
|
|
|
@@ -85,7 +85,7 @@ tests/ 测试(unittest discover tests)
|
|
|
85
85
|
生产基线:本场 strict=1、n=19,唤醒启动段中位 **0.68s**、收尾中位 0.04s ✓)。
|
|
86
86
|
**入口解析 fail-fast**(`meeting_fs.resolve_mc_tools_entry`:从 pi 的 packages 找 MC 包 →
|
|
87
87
|
读它声明的扩展入口 → 取同目录的 subagent-entry.js);缺 MC 时**可见降级**为零扩展。
|
|
88
|
-
**依赖边界**:mc-tools
|
|
88
|
+
**依赖边界**:mc-tools 档的**两份入口各自独立**(MC 的 ctx_search、MCP adapter 的 web_search 等)——**允许而非要求**,缺谁少谁、都**可见**
|
|
89
89
|
(打印一行"本次按零扩展运行:ctx_search 不可用";无静默铁律);`none` 档零依赖
|
|
90
90
|
(无 MC 的机器/CI 显式选它)。**测试/探针保真**:设 `MV_MC_TOOLS_STRICT=1` →
|
|
91
91
|
缺 MC 即报错退出(否则测试可能在"没装 MC"下通过而 ctx_search 从未生效)。
|
package/README.md
CHANGED
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
# pi-multi-viewers
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
各带一份视角任务书(效率/简单/铁律/……),在 meeting 协议下交锋、
|
|
5
|
-
修正、收敛,产出一份共识结果。
|
|
3
|
+
**多视角协同分析(Pi 插件)**:把当前 pi 会话 fork 成 N 个视角 agent,让它们带着你的真实上下文互相交锋,产出共识与分歧。
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
## 它想解决什么问题
|
|
6
|
+
|
|
7
|
+
需要考虑多种因素或者准则时,LLM 容易出现**逐渐忽略其中一部分因素**的问题。
|
|
8
|
+
|
|
9
|
+
对策是**给每个因素一条独立的会话**:一个视角 agent 只关注一个因素(效率 / 简单 / 铁律 / …),
|
|
10
|
+
各带一份视角任务书与独立发言配额,并且必须对其它视角的观点表态(认同或反驳,都要用自己的论据)。
|
|
11
|
+
这样**只要这些会话存在,对应因素就不会被忽略**——关注不靠提醒模型"别忘了 X",
|
|
12
|
+
而是让每个 X 有一个独立的载体。
|
|
13
|
+
|
|
14
|
+
讨论由代码驱动的 meeting 协议推进(发言配额 → 冻结 → 轮转表态 → 共识收束),
|
|
15
|
+
最终产出 `result.md`:**共识结论 + 明确否决项(含理由与重估触发条件)+ 各自保留的分歧**。
|
|
16
|
+
|
|
17
|
+
## 什么时候值得跑
|
|
18
|
+
|
|
19
|
+
**当一个问题需要长思考、并且需要在长思考过程中保持若干个视角的关注时**,可以尝试使用。
|
|
20
|
+
|
|
21
|
+
(反过来说:查一个事实、跑一条命令、一两分钟能自己确认的问题,直接问 pi 更快。)
|
|
22
|
+
|
|
23
|
+
## 代价
|
|
24
|
+
|
|
25
|
+
一次分析通常 **12–35 分钟**(3 视角、20–50 次唤醒,正常情况)。这个区间**不含**异常外溢:
|
|
26
|
+
provider 连续失败或扩展异常时可能显著更久(历史场次里出现过 55 分钟与 73 分钟)。
|
|
27
|
+
它换来的不是"更快",而是"多几个独立立场 + 一份可复查的分歧记录"。
|
|
10
28
|
|
|
11
29
|
## 核心机制(2026-09-09/10 实测验证,见 docs/examples/first-experiment)
|
|
12
30
|
|
|
@@ -67,16 +85,20 @@ ls ~/.pi/agent/npm/node_modules/pi-multi-viewers/scripts/mv.sh
|
|
|
67
85
|
|
|
68
86
|
## 用法
|
|
69
87
|
|
|
70
|
-
|
|
71
|
-
/multi-viewers-setup # ① 建视角(首次使用先跑这个;prompt:先建议 → 你定 → 落盘 → 给你审)
|
|
72
|
-
/multi-viewers "<主题>" # ② 分析(extension:生成 spec → 弹窗门禁 → 启动 → 预填观看命令)
|
|
73
|
-
/multi-viewers-say "<文本>" # ③ 插话(分析进行中;extension:零 LLM 直接写入 human 消息)
|
|
74
|
-
/multi-viewers-finish # ④ 收尾(extension:查状态 → 确认 → 清理,报告随清理打印)
|
|
75
|
-
```
|
|
88
|
+
**接口总表**(pi 内 1 个 prompt + 3 个命令;终端侧另有等价 CLI):
|
|
76
89
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
90
|
+
| 入口 | 形态 | 作用 |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| `/multi-viewers-setup` | prompt | 建视角(建议 → 你定 → `--set-viewer` 落盘 → 给你审) |
|
|
93
|
+
| `/multi-viewers "<主题>"` | extension | 分析:prepare → **暂停点弹窗** → start → 交付观看命令 |
|
|
94
|
+
| `/multi-viewers-finish` | extension | 收尾:status → 确认 → cleanup(报告随清理打印并落盘) |
|
|
95
|
+
| `/multi-viewers-say "<文本>"` | extension | 插话(human 消息,各视角可见可回应) |
|
|
96
|
+
| `scripts/mv.sh <子命令>` | CLI | 终端侧等价入口(`--prepare` / `--start` / `--status` / `--view` / `--say` / `--report` / `--wait` / `--cleanup` / `--viewers` / `--set-viewer`)——pi 内命令内部也走它 |
|
|
97
|
+
|
|
98
|
+
按使用顺序:先 `/multi-viewers-setup` 建视角(一次就够),之后 `/multi-viewers "<主题>"` 跑分析,
|
|
99
|
+
分析进行中用 `/multi-viewers-say` 插话,结束后 `/multi-viewers-finish` 收尾。
|
|
100
|
+
**后三个是 extension**(流程完全由代码执行、零 LLM:跑命令、门禁弹窗、观看命令交付、状态判据
|
|
101
|
+
都走退出码/机器标记行,不靠 LLM 转述);**`setup` 是 prompt**——写视角是内容工作,本就需要 LLM 参与。
|
|
80
102
|
|
|
81
103
|
**目录可以省略**:`--view`/`--say`/`--status`/`--report`/`--wait`/`--cleanup`
|
|
82
104
|
不带目录时自动定位"本 session 当前分析"(只匹配 `mv-<sessionId>-*` 最新;**未匹配
|
|
@@ -98,8 +120,9 @@ scripts/mv.sh --start <spec目录> # 启动(自动挂载主 sessi
|
|
|
98
120
|
# 可选:--fork-mode compaction|budget|full(见上表;一般不调)
|
|
99
121
|
# 可选:--max-meeting 15 --max-rr 7 --stall-timeout 600
|
|
100
122
|
# (配额:建环境时固化进 protocol.json,之后不可改;meeting 配额是"每 agent")
|
|
101
|
-
# 可选:--extension-policy mc-tools|none|all(默认 mc-tools =
|
|
102
|
-
#
|
|
123
|
+
# 可选:--extension-policy mc-tools|none|all(默认 mc-tools = 零扩展 + 两份只读工具入口:
|
|
124
|
+
# MC 的 ctx_search 与 MCP adapter 的 web 检索等;none = 零扩展、零依赖;
|
|
125
|
+
# all = 走 pi 默认发现。两份入口各自独立,缺谁少谁且可见降级)
|
|
103
126
|
# 高级:--agents "a,b" 起一次性视角(不建 viewers/ 时用;prompt 入口不传它)
|
|
104
127
|
|
|
105
128
|
# 观看:--start 会输出可直接执行的 !! 流式观看命令(复制执行)
|
|
@@ -156,6 +179,9 @@ frontmatter 字段、写文件路径、独立参与者纪律。
|
|
|
156
179
|
| 完全一次性(项目还没建 viewers/) | `mv.sh --prepare "<主题>" --agents "a,b"`(wrapper 高级用法) |
|
|
157
180
|
|
|
158
181
|
## 架构
|
|
182
|
+
与 [pi-agents-helper](https://github.com/maxdai/pi-agents-helper)(多方
|
|
183
|
+
讨论达成共识,agent 无主上下文)平行演化;共享 meeting 协议核心
|
|
184
|
+
(core/fs/engine),差异在初始化层。
|
|
159
185
|
|
|
160
186
|
```
|
|
161
187
|
meeting_core.py 纯判定(冻结级联/RR/聚合 + 状态机词汇常量)
|
package/docs/design.md
CHANGED
|
@@ -495,7 +495,7 @@ commit 是溯源记录、本节是长期引用点——不并存两份权威值
|
|
|
495
495
|
|
|
496
496
|
| 档 | 唤醒命令 | 语义 |
|
|
497
497
|
|---|---|---|
|
|
498
|
-
| `mc-tools`(默认) | 四个 `--no-*` + `-e <MC subagent-entry.js
|
|
498
|
+
| `mc-tools`(默认) | 四个 `--no-*` + `-e <MC subagent-entry.js>` + `-e <pi-mcp-adapter 入口>`(任一份入口缺失即**部分降级**:缺谁少谁、都可见;全缺 = 等价 none)
|
|
499
499
|
| `none` | 四个 `--no-*` | 零扩展:最快、**零依赖** |
|
|
500
500
|
| `all` | 不加任何 `--no-*` | pi 默认发现(A/B 与显式 opt-in)|
|
|
501
501
|
|
|
@@ -517,6 +517,7 @@ commit 是溯源记录、本节是长期引用点——不并存两份权威值
|
|
|
517
517
|
spawn cwd 全截断("无 MC 的机器"上产出残缺命令)。**登记行**:首唤打
|
|
518
518
|
`扩展策略: 声明=X 生效=Y strict=0|1 [降级原因=…]`——报告据此给"声明 vs 生效"
|
|
519
519
|
(E2;与"档位"同型),否则降级只在 loop log 里、产品面看不见。
|
|
520
|
+
**两份入口(2026-09-25 用户裁决 B)**:`mc-tools` 除 MC 的只读检索工具外,再显式 `-e` 加载 **pi-mcp-adapter**(web_search / web_reader / zread 等 MCP 工具)——`--no-extensions` 关的是**扩展发现**,显式路径照常生效(pi `--help` 原文),不加载就等于 agents 完全失去联网检索能力。两份入口**各自独立降级**(缺谁少谁、都可见),不新增档位(保持简单)。
|
|
520
521
|
|
|
521
522
|
**依赖边界(用户 2026-09-14 定)**:mc-tools **允许而非要求** MC——缺 MC 时
|
|
522
523
|
**降级为零扩展并按 none 运行**,但**必须可见**(打印一行"mc-tools 档未生效
|
|
@@ -591,7 +592,7 @@ commit 是溯源记录、本节是长期引用点——不并存两份权威值
|
|
|
591
592
|
|---|---|---|---|
|
|
592
593
|
| `setEditorText` 预填 | 仅 TUI | 0 | TUI 便利(能直接回车跑) |
|
|
593
594
|
| `notify` | 全模式 | 0 | 即时反馈(pi-web 关掉弹窗即消失) |
|
|
594
|
-
| `pi.sendMessage`(custom_message) | 全模式 | ~百 token(主 session)+ 随 fork 进每场分析 | 持久留痕(pi-web
|
|
595
|
+
| `pi.sendMessage`(custom_message) | 全模式 | ~百 token(主 session)+ 随 fork 进每场分析 | 持久留痕(pi-web 渲染为折叠块、需点击;起 0.5.19 实时出现) |
|
|
595
596
|
| `ctx.ui.setWidget` | 全模式 | 0(纯 UI) | 运行期常驻可见(一眼看到、不需点击) |
|
|
596
597
|
三条注记:① `sendMessage` 的 custom_message **会随 fork 进每场分析各视角的上下文**
|
|
597
598
|
(fork 源在首唤由主 session 条目构建,不做类型过滤)——~2 行/场,有界;不为它加
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
* ① `ctx.ui.setEditorText` 预填输入框——TUI 便利(能直接回车跑);pi-web 忽略
|
|
21
21
|
* ② `notify`——即时反馈;pi-web 上关掉弹窗即消失
|
|
22
22
|
* ③ `pi.sendMessage`(custom_message)——**持久留痕**,跨重启仍在;但 pi-web 渲染为
|
|
23
|
-
* **折叠的** `multi-viewers (click to expand)
|
|
23
|
+
* **折叠的** `multi-viewers (click to expand)`,需点击展开(pi-web 0.5.19 起**实时出现**,
|
|
24
|
+
* 此前只在重载后可见);且随 fork 进入每场分析上下文
|
|
24
25
|
* ④ `ctx.ui.setWidget`——**运行期常驻可见**(一眼看到、不需点击;MC 待办用的同一通道)
|
|
25
26
|
*/
|
|
26
27
|
|
|
@@ -104,8 +105,8 @@ export default function register(pi: any) {
|
|
|
104
105
|
content: `多视角分析已启动:${dir}\n观看命令(复制执行,不进 LLM):\n${watch}`,
|
|
105
106
|
display: true,
|
|
106
107
|
});
|
|
107
|
-
// 常驻面板:
|
|
108
|
-
//
|
|
108
|
+
// 常驻面板:custom_message 在 pi-web 是**折叠块**(要点击才展开;0.5.19 起实时出现)
|
|
109
|
+
// → 观看命令还需要一条**一眼可见、不需点击**的常驻出口。
|
|
109
110
|
// setWidget 正是这个语义(pi-web 注释:"Persistent widget panel … Not a popup";
|
|
110
111
|
// 主 pi 的 magic-context 待办面板用的就是它)。
|
|
111
112
|
ctx.ui.setWidget("multi-viewers", [
|
package/meeting_fs.py
CHANGED
|
@@ -126,32 +126,24 @@ def _package_dir(source, agent_dir):
|
|
|
126
126
|
return cand if os.path.isdir(cand) else None
|
|
127
127
|
|
|
128
128
|
|
|
129
|
-
def
|
|
130
|
-
"""
|
|
131
|
-
|
|
132
|
-
为什么这样解析而不硬编码路径:MC 自己就是用"主入口的**兄弟文件**"
|
|
133
|
-
(其源码 `resolveSiblingEntryPath("subagent-entry.js")`)定位它。我们的
|
|
134
|
-
等价做法 = 从 pi 的注册表(settings.json.packages)找到 MC 包目录 → 读它
|
|
135
|
-
package.json 声明的扩展入口(`pi.extensions[0]`)→ 取同目录下的
|
|
136
|
-
subagent-entry.js。
|
|
129
|
+
def _packages_of(pkg_name, agent_dir=None):
|
|
130
|
+
"""在 pi 的 packages 里找声明了 `pkg_name` 的包:返回 (candidates, err)。
|
|
137
131
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
在严格态(`MV_MC_TOOLS_STRICT=1`,测试/探针保真)直接报错。
|
|
132
|
+
candidates = [(source, pkg_dir)],按注册顺序(可能有多个候选——包名相同但
|
|
133
|
+
来源不同);err = 读注册表失败的原因(此时 candidates 为空)。
|
|
141
134
|
|
|
142
|
-
|
|
143
|
-
`
|
|
135
|
+
**匹配按裸包名精确相等**(F5):不能用子串——`in` 会把
|
|
136
|
+
`@cortexkit/pi-magic-context-legacy` 也命中。npm 源直接比裸名;路径源读
|
|
137
|
+
它的 package.json.name。**遍历全部候选**(F6:首个匹配不可解析时继续看
|
|
138
|
+
后面的候选,否则"装着也报没装",文案误导)。
|
|
144
139
|
"""
|
|
145
140
|
agent_dir = agent_dir or pi_agent_dir()
|
|
146
141
|
try:
|
|
147
142
|
with open(os.path.join(agent_dir, "settings.json"), encoding="utf-8") as f:
|
|
148
143
|
pkgs = json.load(f).get("packages") or []
|
|
149
144
|
except (OSError, ValueError) as e:
|
|
150
|
-
return
|
|
151
|
-
|
|
152
|
-
# F6:遍历**全部**候选、取首个可解析(此前首个匹配失败即停,装着 MC 也会
|
|
153
|
-
# 报"没装",文案误导)
|
|
154
|
-
seen = [] # 候选(source 字符串)——用于错误文案,说明"试过哪些"
|
|
145
|
+
return [], f"读不到 pi 的 packages({agent_dir}/settings.json): {e}"
|
|
146
|
+
out = []
|
|
155
147
|
for entry in pkgs:
|
|
156
148
|
source = _entry_source(entry)
|
|
157
149
|
if not source:
|
|
@@ -167,32 +159,80 @@ def resolve_mc_tools_entry(agent_dir=None):
|
|
|
167
159
|
name = json.load(f).get("name") or ""
|
|
168
160
|
except (OSError, ValueError):
|
|
169
161
|
name = ""
|
|
170
|
-
if name
|
|
171
|
-
|
|
172
|
-
|
|
162
|
+
if name == pkg_name:
|
|
163
|
+
out.append((source, pkg_dir))
|
|
164
|
+
return out, ""
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _declared_extensions(pkg_name, agent_dir=None):
|
|
168
|
+
"""包名 → (pkg_dir, exts, err):包目录 + 它自己声明的扩展入口(已归一为列表)。
|
|
169
|
+
|
|
170
|
+
exts 来自包 package.json 的 `pi.extensions`(判据**由上游声明**,不硬编码布局);
|
|
171
|
+
字符串形态归一为单元素列表(F7:直接取首字符会解析出错误路径)。
|
|
172
|
+
"""
|
|
173
|
+
cands, err = _packages_of(pkg_name, agent_dir)
|
|
174
|
+
if err:
|
|
175
|
+
return None, [], err
|
|
176
|
+
if not cands:
|
|
177
|
+
return None, [], f"packages 里没有 {pkg_name}(pi install npm:{pkg_name})"
|
|
178
|
+
last = ""
|
|
179
|
+
for source, pkg_dir in cands:
|
|
173
180
|
try:
|
|
174
181
|
with open(os.path.join(pkg_dir, "package.json"), encoding="utf-8") as f:
|
|
175
182
|
man = json.load(f)
|
|
176
183
|
except (OSError, ValueError) as e:
|
|
177
|
-
|
|
184
|
+
last = f"读不到 {pkg_name} 的 package.json: {e}"
|
|
185
|
+
continue
|
|
178
186
|
exts = (man.get("pi") or {}).get("extensions")
|
|
179
|
-
if isinstance(exts, str):
|
|
187
|
+
if isinstance(exts, str):
|
|
180
188
|
exts = [exts]
|
|
181
189
|
if not isinstance(exts, list) or not exts or not all(
|
|
182
190
|
isinstance(x, str) and x for x in exts):
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
191
|
+
last = f"{pkg_name} 的 pi.extensions 形态不可用(版本不兼容?)"
|
|
192
|
+
continue
|
|
193
|
+
return pkg_dir, exts, ""
|
|
194
|
+
return None, [], (last or f"{pkg_name} 的扩展入口不可用")
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def resolve_mc_tools_entry(agent_dir=None):
|
|
198
|
+
"""解析 MC 的**只读工具入口**(`dist/subagent-entry.js`)——"mc-tools" 档用。
|
|
199
|
+
|
|
200
|
+
路径推理:MC 自己就是用"主入口的**兄弟文件**"(其源码
|
|
201
|
+
`resolveSiblingEntryPath("subagent-entry.js")`)定位它;我们等价地读它声明的
|
|
202
|
+
扩展入口(`pi.extensions[0]`),再取同目录下的 subagent-entry.js。
|
|
203
|
+
|
|
204
|
+
失败语义(与 resolve_mcp_adapter_entry 同):任一步缺失返回 `(None, 原因)`
|
|
205
|
+
——**由调用方按策略决定**:loop 生产态做**可见降级**,严格态
|
|
206
|
+
(`MV_MC_TOOLS_STRICT=1`)直接报错。
|
|
207
|
+
"""
|
|
208
|
+
pkg_dir, exts, err = _declared_extensions(MC_PACKAGE, agent_dir)
|
|
209
|
+
if err:
|
|
210
|
+
return None, err
|
|
211
|
+
cand = os.path.normpath(
|
|
212
|
+
os.path.join(pkg_dir, os.path.dirname(exts[0]), "subagent-entry.js"))
|
|
213
|
+
if os.path.isfile(cand):
|
|
214
|
+
return cand, ""
|
|
215
|
+
return None, (f"{MC_PACKAGE} 的只读工具入口不存在"
|
|
216
|
+
f"({os.path.relpath(cand, pkg_dir)})——上游版本可能改了布局")
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def resolve_mcp_adapter_entry(agent_dir=None):
|
|
220
|
+
"""解析 MCP adapter 的扩展入口(它声明的 `pi.extensions[0]`)——mc-tools 第二份。
|
|
221
|
+
|
|
222
|
+
为什么需要:MCP 工具(web_search / web_reader / zread…)由 pi-mcp-adapter
|
|
223
|
+
提供,而 `--no-extensions` 关掉的是**扩展发现**——显式 `-e` 路径照常生效
|
|
224
|
+
(pi --help 原文)。不显式加载 = agents 完全没有联网检索能力。
|
|
225
|
+
|
|
226
|
+
与 MC 的差别:这里要的**就是主入口本身**(它注册 MCP 工具),不取兄弟文件。
|
|
227
|
+
"""
|
|
228
|
+
pkg_dir, exts, err = _declared_extensions(MCP_ADAPTER_PACKAGE, agent_dir)
|
|
229
|
+
if err:
|
|
230
|
+
return None, err
|
|
231
|
+
cand = os.path.normpath(os.path.join(pkg_dir, exts[0]))
|
|
232
|
+
if os.path.isfile(cand):
|
|
233
|
+
return cand, ""
|
|
234
|
+
return None, (f"{MCP_ADAPTER_PACKAGE} 声明的入口不存在"
|
|
235
|
+
f"({os.path.relpath(cand, pkg_dir)})")
|
|
196
236
|
|
|
197
237
|
|
|
198
238
|
def pi_agent_dir():
|
|
@@ -778,6 +818,10 @@ def mc_tools_strict():
|
|
|
778
818
|
# 入口是它的内部文件,路径解析见 resolve_mc_tools_entry 的 docstring)
|
|
779
819
|
MC_PACKAGE = "@cortexkit/pi-magic-context"
|
|
780
820
|
|
|
821
|
+
# MCP 工具(web_search / web_reader / zread…)的提供者——mc-tools 档的第二份入口。
|
|
822
|
+
# 名字**由 pi 的注册表给**(settings.json.packages),这里只做精确匹配用。
|
|
823
|
+
MCP_ADAPTER_PACKAGE = "pi-mcp-adapter"
|
|
824
|
+
|
|
781
825
|
FORK_MODES = ("budget", "compaction", "full")
|
|
782
826
|
DEFAULT_FORK_MODE = "budget"
|
|
783
827
|
#
|
package/meeting_loop.py
CHANGED
|
@@ -330,19 +330,36 @@ def _build_wake_cmd(workdir, agent, sid, cfg, fork_source, fork_cwd,
|
|
|
330
330
|
if extension_policy == "none":
|
|
331
331
|
cmd += no_ext
|
|
332
332
|
elif extension_policy == "mc-tools":
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
333
|
+
# mc-tools = 零扩展 + 显式加载**两份只读工具入口**(2026-09-25 用户裁定 B:
|
|
334
|
+
# 并入默认档,不再新增档位——保持简单):
|
|
335
|
+
# ① MC 的 subagent-entry(只注册工具、不装 hook)→ ctx_search
|
|
336
|
+
# ② MCP adapter(web_search / web_reader / zread 等 MCP 工具)
|
|
337
|
+
# 为什么必须显式 -e:`--no-extensions` 关的是**发现**,显式路径照常生效
|
|
338
|
+
# (pi --help 原文);MCP 工具此前因发现被关而对 agents 完全不可用。
|
|
339
|
+
# 两份入口**各自独立**降级(允许而非要求)——缺哪个就少哪个,都**可见**。
|
|
340
|
+
resolved = [] # [(label, entry)]
|
|
341
|
+
missing = [] # [(label, err)]
|
|
342
|
+
for label, resolver in (
|
|
343
|
+
("ctx_search(MC 只读检索)", meeting_fs.resolve_mc_tools_entry),
|
|
344
|
+
("MCP 工具(web_search 等)", meeting_fs.resolve_mcp_adapter_entry)):
|
|
345
|
+
entry, err = resolver()
|
|
346
|
+
if entry:
|
|
347
|
+
resolved.append((label, entry))
|
|
348
|
+
else:
|
|
349
|
+
missing.append((label, err))
|
|
350
|
+
cmd += no_ext
|
|
351
|
+
for _label, entry in resolved:
|
|
352
|
+
cmd += ["-e", entry]
|
|
353
|
+
if missing:
|
|
354
|
+
reason = ";".join(f"{label} 不可用({err})" for label, err in missing)
|
|
355
|
+
if meeting_fs.mc_tools_strict():
|
|
356
|
+
# 严格模式(测试/探针保真):缺入口即响,不降级——否则测试可能在
|
|
357
|
+
# "没装某入口"的环境里通过,而该工具从未生效
|
|
358
|
+
log(agent, f"[fatal] mc-tools 档入口解析失败(严格模式):{reason}")
|
|
359
|
+
raise RuntimeError(f"mc-tools 档不可用: {reason}")
|
|
360
|
+
# 允许而非要求:缺入口 → 少一份 -e,但**可见**
|
|
361
|
+
downgrade_reason = reason
|
|
362
|
+
effective_policy = ("mc-tools" if resolved else "none")
|
|
346
363
|
# "all":不加任何 --no-*(走 pi 默认发现)
|
|
347
364
|
if first_wake:
|
|
348
365
|
# 登记行(观测面的稳定字段;报告据此给"声明 vs 生效")。只在首唤打:
|
|
@@ -351,8 +368,10 @@ def _build_wake_cmd(workdir, agent, sid, cfg, fork_source, fork_cwd,
|
|
|
351
368
|
f" strict={int(meeting_fs.mc_tools_strict())}"
|
|
352
369
|
+ (f" 降级原因={downgrade_reason}" if downgrade_reason else ""))
|
|
353
370
|
if downgrade_reason:
|
|
354
|
-
|
|
355
|
-
|
|
371
|
+
log(agent, f"mc-tools 档{'部分' if resolved else '完全'}未生效"
|
|
372
|
+
f"({downgrade_reason})——本次按"
|
|
373
|
+
f"{'已解析的入口' if resolved else '零扩展'}运行:"
|
|
374
|
+
f"缺失的工具在本次分析中不可用")
|
|
356
375
|
model = cfg.get("model") or ""
|
|
357
376
|
if model:
|
|
358
377
|
cmd += ["--model", model]
|
package/package.json
CHANGED