pi-multi-viewers 0.10.1 → 0.10.2
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 +12 -5
- package/README.md +2 -2
- package/docs/design.md +75 -24
- package/docs/reviews/2026-09-30-builtin-mcp-audit.md +284 -0
- package/docs/reviews/README.md +1 -0
- package/docs/test-methodology.md +67 -0
- package/meeting_fs.py +16 -30
- package/meeting_loop.py +42 -32
- package/observability.py +14 -2
- package/package.json +1 -1
- package/start_discussion.py +2 -2
package/AGENTS.md
CHANGED
|
@@ -69,10 +69,14 @@ tests/ 测试(unittest discover tests)
|
|
|
69
69
|
|
|
70
70
|
| 档 | 唤醒命令 | 用途 |
|
|
71
71
|
|---|---|---|
|
|
72
|
-
| **mc-tools**(默认) | 四个 `--no-*` + `-e <MC 的 subagent-entry.js>` + `-e
|
|
72
|
+
| **mc-tools**(默认) | 四个 `--no-*` + `-e <MC 的 subagent-entry.js>` + `-e builtin:mcp`(pi **内置** MCP,零第三方依赖) | 给 agents **按需检索**(`ctx_search` 查项目历史、web_search 等查外部)——协议模板里带一句「需要项目历史时用 ctx_search」的条件指引(仅在工具真到位时出现)。**降级/严格/生效值语义见 docs/design.md 决策 20 的语义清单**(唯一权威段) |
|
|
73
73
|
| **none** | 四个 `--no-*` | 零扩展、**零依赖**(无 MC 的机器/CI 用这档) |
|
|
74
74
|
| **all** | 不加任何 `--no-*`(pi 默认发现)| A/B 实验与显式 opt-in |
|
|
75
75
|
|
|
76
|
+
**前提与盲区**(细节见 docs/design.md 决策 20「平台能力的可见性」):本档依赖 **pi ≥ 0.99**
|
|
77
|
+
(更早版本 = 未知 builtin 名 → 硬失败 rc=1,不是静默降级);**非交互模式下 MCP server 状态
|
|
78
|
+
不进任何产物**(失败只走 no-op 的 notify)⇒ `rc=0` ≠ 工具可用,报告固定写一行「平台能力:未观测」。
|
|
79
|
+
|
|
76
80
|
为什么**不能**用插件全档(`all`):两类插件在**我们这种 session 形态**上都是分钟级负担、
|
|
77
81
|
且都在关键路径上(loop 等进程退出才继续)——
|
|
78
82
|
· **AFT**:大 session 上进程退出前多活数分钟(承重 = **生产真场** e2e21 收尾占
|
|
@@ -88,10 +92,13 @@ MC 自己的 `context.db` #916;上限**可调**:主 pi 抬到 131072 后首
|
|
|
88
92
|
**无** historian/压缩执行钩子 → historian **0**(真场:e2e24 0/3、e2e25 0);
|
|
89
93
|
`ctx_search` 实测可用 ✓;成本**未测得显著差异**(受控探针 n 小、组内方差>组间差 ✗;
|
|
90
94
|
生产基线:本场 strict=1、n=19,唤醒启动段中位 **0.68s**、收尾中位 0.04s ✓)。
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
`
|
|
94
|
-
|
|
95
|
+
**入口解析**:MC 那份由 `meeting_fs.resolve_mc_tools_entry` 解析(从 pi 的 packages
|
|
96
|
+
找包 → 读它自己声明的 `pi.extensions`,不硬编码布局 → 取同目录 subagent-entry.js);
|
|
97
|
+
MCP 那份是**常量** `meeting_fs.BUILTIN_MCP_ENTRY = "builtin:mcp"`(pi 0.99+ 自带,
|
|
98
|
+
无需解析——第三方 `pi-mcp-adapter` 依赖已删,2026-09-30)。为什么必须显式 `-e`:
|
|
99
|
+
`--no-extensions` 关的是"扩展发现**与内置扩展**"(pi --help 原文)。
|
|
100
|
+
**降级/严格模式/生效值语义**见 docs/design.md 决策 20 的语义清单——
|
|
101
|
+
本文件不复述(本周刚付过一次漂移的账)。
|
|
95
102
|
|
|
96
103
|
**主 pi 完全不受影响**(只改我们 spawn 的 agent 进程命令行;主 pi 的 MC/历史学家照常)。
|
|
97
104
|
|
package/README.md
CHANGED
|
@@ -122,8 +122,8 @@ scripts/mv.sh --start <spec目录> # 启动(自动挂载主 sessi
|
|
|
122
122
|
# 可选:--max-meeting 15 --max-rr 7 --stall-timeout 600
|
|
123
123
|
# (配额:建环境时固化进 protocol.json,之后不可改;meeting 配额是"每 agent")
|
|
124
124
|
# 可选:--extension-policy mc-tools|none|all(默认 mc-tools = 零扩展 + 两份只读工具入口:
|
|
125
|
-
# MC 的 ctx_search
|
|
126
|
-
# all = 走 pi
|
|
125
|
+
# MC 的 ctx_search + pi 内置 MCP 的 web 检索等;none = 零扩展、零依赖;
|
|
126
|
+
# all = 走 pi 默认发现。MCP 那份是内置、恒在;MC 那份缺了则可见降级)
|
|
127
127
|
# 高级:--agents "a,b" 起一次性视角(不建 viewers/ 时用;prompt 入口不传它)
|
|
128
128
|
|
|
129
129
|
# 观看:--start 会输出可直接执行的 !! 流式观看命令(复制执行)
|
package/docs/design.md
CHANGED
|
@@ -510,7 +510,7 @@ commit 是溯源记录、本节是长期引用点——不并存两份权威值
|
|
|
510
510
|
|
|
511
511
|
| 档 | 唤醒命令 | 语义 |
|
|
512
512
|
|---|---|---|
|
|
513
|
-
| `mc-tools`(默认) | 四个 `--no-*` + `-e <MC subagent-entry.js>` + `-e
|
|
513
|
+
| `mc-tools`(默认) | 四个 `--no-*` + `-e <MC subagent-entry.js>` + `-e builtin:mcp`(MC 入口缺 → 可见降级:少它那份 `-e`、生效=none;内置 MCP 恒在)
|
|
514
514
|
| `none` | 四个 `--no-*` | 零扩展:最快、**零依赖** |
|
|
515
515
|
| `all` | 不加任何 `--no-*` | pi 默认发现(A/B 与显式 opt-in)|
|
|
516
516
|
|
|
@@ -518,19 +518,19 @@ commit 是溯源记录、本节是长期引用点——不并存两份权威值
|
|
|
518
518
|
是必要的 background 补充”):**fork 本身是主背景通道**(agent 继承主会话的开发
|
|
519
519
|
上下文),覆盖面受 ① fork 源内容 ② `budget` 裁剪比例限制;`ctx_search` 是它的
|
|
520
520
|
**兜底**(按需检索记忆/文档/历史)。MC 的
|
|
521
|
-
**两份入口的结构句与检测覆盖面(2026-09-27
|
|
521
|
+
**两份入口的结构句与检测覆盖面(2026-09-27 复盘审计订正;2026-09-30 第二份改为内置)**:
|
|
522
522
|
- **MC 的 `subagent-entry.js`**(MC 自己给"搜索类子代理"用的入口)= **工具注册 +
|
|
523
523
|
两个生命周期钩子**(`session_start` 开 DB / `session_shutdown` 关 DB),**没有**
|
|
524
524
|
historian / 压缩 / 打标类执行钩子 → agents 得到按需检索能力(memories / docs /
|
|
525
525
|
历史)。*(此前写作"只注册工具、不装任何 hook",与上游 0.43.2 源码不符,已订正。)*
|
|
526
526
|
**检测覆盖面三面**:唤醒命令的**启动段**、报告**收尾列**、MC 自己的 `context.db` 账本。
|
|
527
|
-
- **pi
|
|
528
|
-
`
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
- **重估触发**:上游 MC /
|
|
533
|
-
(MC:`session_start`/`session_shutdown` 内是否新增工作;
|
|
527
|
+
- **pi 内置 MCP 扩展**(`builtin:mcp`)= 在 **`session_start`** 建立连接(上游
|
|
528
|
+
`extensions/mcp/index.ts` 的 `pi.on("session_start", …)`),连接后按服务器逐个
|
|
529
|
+
`registerTool` ⇒ 成本落在**启动段**("首个 prompt 最多等 10 秒"是上游文档写明的
|
|
530
|
+
上界;先前第三方 adapter 的实测边际是 **+0.2s/唤**,内置实现**待测**——见 §假与口径)。
|
|
531
|
+
**兜底 = 换档**(要零扩展就显式 `--extension-policy none`)。
|
|
532
|
+
- **重估触发**:上游 MC / pi **升版**,或报告**启动段/尾列出现分钟级离群** → 重核
|
|
533
|
+
(MC:`session_start`/`session_shutdown` 内是否新增工作;MCP:连接时长与工具注册数)。
|
|
534
534
|
- **探针双向句**:那 2 臂探针(16–23s vs 4s)验的是**能力与卡死/收尾**,
|
|
535
535
|
**不测钩子成本**。
|
|
536
536
|
|
|
@@ -539,7 +539,6 @@ commit 是溯源记录、本节是长期引用点——不并存两份权威值
|
|
|
539
539
|
首次真场 strict=1、n=19 → 唤醒启动段中位 **0.68s**、收尾中位 0.04s ✓)。
|
|
540
540
|
*口径项*:fork 源随唤醒增长(构建时 606 条 / 1.40MB → 读数时 729–800 条 /
|
|
541
541
|
1.44–1.56MB)——**是文件在涨,不是时间在涨**,不构成成本项。
|
|
542
|
-
|
|
543
542
|
**入口解析 fail-fast**(`resolve_mc_tools_entry`:pi 的 packages → MC 包 →
|
|
544
543
|
它声明的扩展入口 → 同目录 `subagent-entry.js`);缺 MC 时**可见降级**(严格模式
|
|
545
544
|
`MV_MC_TOOLS_STRICT=1` 才报错退出)。
|
|
@@ -562,21 +561,73 @@ commit 是溯源记录、本节是长期引用点——不并存两份权威值
|
|
|
562
561
|
这是**能力取舍判据、不是性能开关**;数据来源 = 各场 `result.md` 的元信息
|
|
563
562
|
(不设计数器/监控)。*前置限定不可省*——否则会把"需求不在窗口内"当成负证据
|
|
564
563
|
(2026-09-27 那场即此情形:主题所需事实全在窗口/仓库内)。
|
|
565
|
-
**两份入口(2026-09-25 用户裁决 B
|
|
566
|
-
|
|
564
|
+
**两份入口(2026-09-25 用户裁决 B;2026-09-30 第二份改为 pi 内置 MCP)**:
|
|
565
|
+
`mc-tools` 除 MC 的只读检索工具外,再显式 `-e builtin:mcp` 得到 MCP 工具
|
|
566
|
+
(web_search / web_reader / zread 等)。**为什么改成内置**(用户 2026-09-30:
|
|
567
|
+
"pi 原生支持 mcp,不需要另装插件了"):pi 0.99+ 自带 MCP 扩展,配置写在
|
|
568
|
+
`~/.pi/agent/mcp.json`(或项目 `.pi/mcp.json`)、工具名 `mcp__<server>__<tool>`;
|
|
569
|
+
换成它 = **去掉一个第三方依赖**(`pi-mcp-adapter` 的入口解析/版本漂移全没了,
|
|
570
|
+
`resolve_mcp_adapter_entry` 已删)。
|
|
567
571
|
**语义清单(唯一权威段,别处引用不复述)**:
|
|
568
|
-
①
|
|
569
|
-
|
|
570
|
-
② `MV_MC_TOOLS_STRICT=1`(测试/探针保真)→
|
|
571
|
-
(否则测试可能在"
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
572
|
+
① MC 入口**允许而非要求**:解析失败不阻断分析(缺 MC 的机器照样跑)——它现在是
|
|
573
|
+
**唯一"可失败"的入口**;内置 MCP 是常量入口、恒在(无第三方、无解析)。
|
|
574
|
+
② `MV_MC_TOOLS_STRICT=1`(测试/探针保真)→ MC 入口缺失即报错退出
|
|
575
|
+
(否则测试可能在"没装 MC"的环境里通过,而 `ctx_search` 从未生效);
|
|
576
|
+
内置 MCP 不参与该判定(名字由 pi 注册、无需解析)。
|
|
577
|
+
③ 生效值语义:**以本档核心能力为准** —— MC 入口解析成功 → `生效=mc-tools`;失败 →
|
|
578
|
+
`生效=none`(本档核心 `ctx_search` 缺失);原因字段 = **入口层失败原因**(不写
|
|
579
|
+
工具可用性——那件事本档观测不到,见下方「平台能力的可见性」)。
|
|
580
|
+
*(旧措辞「部分降级仍生效=mc-tools」出自「两份入口都会被解析」的时代,已随第二份
|
|
581
|
+
改为常量入口而失效。)*
|
|
582
|
+
④ 为什么必须显式 `-e`:`--no-extensions` 关的是"扩展发现**与内置扩展**"
|
|
583
|
+
(pi `--help` 原文)——内置 MCP 也在关停范围,不显式加载 = agents 完全没有
|
|
584
|
+
MCP 工具;`-e <path>` 接受 `builtin:<name>`(同一份 help)。上游证据:
|
|
585
|
+
`core/extensions/index.ts` 里 `{ name: "mcp", builtin: true }`,
|
|
586
|
+
`core/resource-loader.ts` 在 `noExtensions` 时只保留 CLI 显式 `-e` 的扩展。
|
|
587
|
+
⑤ **exposure 是配置层的事**(不属于本档):`mcp.json` 里每个 server 的
|
|
588
|
+
`exposure` 默认 `codemode`(工具**不声明给模型**,只能从 codemode 脚本调用);
|
|
589
|
+
要让 agents 直接调用(= 先前 adapter 的体验)需在 `mcp.json` 写
|
|
590
|
+
`"exposure": "direct"`——**该文件是用户级配置、也影响主 pi**(2026-09-30 用户裁
|
|
591
|
+
决选 direct)。
|
|
592
|
+
不新增档位(保持简单)。`none` 零依赖(无 MC 的机器/CI 显式选它)。
|
|
593
|
+
**实测(2026-09-30 真场,38 次唤醒)**:内置 MCP 的**每唤醒成本 ≈ 0**——逐唤醒「启
|
|
594
|
+
动前」中位 **−1.3s**(测量偏置)、三 agent 合计 −2s / −5s / +19s、收尾中位 1.1s;
|
|
595
|
+
对照 AFT 的 445.9s 与 MC historian 的 447s,差三个数量级。**唯一离群**:一次 19s 启
|
|
596
|
+
动(未归因,不排除 provider 抖动)。
|
|
597
|
+
**平台能力的可见性(2026-09-30 自审产出;含 4 条不变式)**:
|
|
598
|
+
- **前提**:本档依赖 **pi ≥ 0.99**(`builtin:mcp` 与 `pi mcp` 子命令自该版本)。更早的 pi
|
|
599
|
+
**不是静默降级而是硬失败**:未知 builtin 名 → `Unknown built-in extension`
|
|
600
|
+
(`core/resource-loader.ts`)→ 诊断 → `process.exit(1)`(`main.ts`)⇒ 每次唤醒 rc=1。
|
|
601
|
+
- **不变式 ①**:`meeting_loop` 里**两处守卫拦不同失效模式**,不可互相替代 —— 值域检查拦
|
|
602
|
+
**元组之外**的值;`else: raise` 拦**元组之内、但无分支**的值。别再当死代码删(删掉后新
|
|
603
|
+
策略值会静默落进 `all` 档 = 分钟×N 级且无信号)。
|
|
604
|
+
- **不变式 ②**:分析目录里的**运行快照只含 4 个模块**(meeting_loop / fs / core / engine),
|
|
605
|
+
**`observability` 不在快照里** ⇒ `--report` 永远用**主仓今天**的代码读**当时** writer 写的
|
|
606
|
+
loop 日志(reader/writer 跨版本解耦)。判定条件必须容得下"旧 writer 产出 d==e 且
|
|
607
|
+
reason 非空"(`or reason` 的存在理由)。
|
|
608
|
+
- **不变式 ③**:**非交互模式下 MCP server 状态不进任何产物** —— 单 server 失败被
|
|
609
|
+
`Promise.allSettled` 吞掉,只走 `ctx.ui.notify`,而 `--mode json --print` 用的是
|
|
610
|
+
`noOpUIContext`(`core/extensions/runner.ts` 的 `notify: () => {}`);session 也不落工具集。
|
|
611
|
+
⇒ **`rc=0` ≠ 工具可用**;报告写「**平台能力:未观测**」(缺席≠0 的同一纪律)。
|
|
612
|
+
- **登记(既定路径,非行动项)**:真需要机器判据时用 **`pi mcp list --json`**(上游**文档化
|
|
613
|
+
结构接口**;字段 `state`/`tools`/`error`,任一 enabled server 非 `connected` ⇒ 非零退出)。
|
|
614
|
+
触发条件 = 用户报"agents 没搜到" 或 上游给出其它可读信号。做法:`--start` 时采一次 → 落盘
|
|
615
|
+
分析目录 → `--report` 读文件(**探针在组合层/start 层,不进 observability**——报告必须
|
|
616
|
+
保持"零插桩事后推导")。代价:常态 **+2.7–2.8s**(n=3;复测 4.6s),最坏 +T(我们侧
|
|
617
|
+
超时,T ≤ 10s)。取值三态 **ok / 异常(点名 server)/ 未验证**,**禁写"不可用"**(我们只知
|
|
618
|
+
"此刻不可达")。护栏:超时 + fail-open、可注入(默认单测不得真连远端)、采样须以
|
|
619
|
+
**cwd = fork_cwd** 启动(项目级 `.pi/mcp.json` + trust 会改变 agents 所见,别把近似当事实)、
|
|
620
|
+
文档写明只覆盖**持续性**失效(不覆盖"启动正常、中途断")。**两条已证伪的错路**:成功路径
|
|
621
|
+
stderr(notify 是 no-op)与 session 文件(工具集不上盘)——别再捡。
|
|
622
|
+
- **exposure 的成本与口径**:`direct` 的代价实测 = **+1,025 tok/请求**(差值口径;n=2,
|
|
623
|
+
配置 = 3 server / 5 工具全 direct;人口 = agent 侧探针会话;**机制外推**:主 pi 每请求同量级
|
|
624
|
+
≈ +1k,**未测**)。数字随 server 数 / exposure 变化而作废;写**差值**不写绝对值。
|
|
625
|
+
- **三档边界**(可复用的判据):① 上游**文档化结构接口**(`pi mcp list --json` 字段、
|
|
626
|
+
session 条目)→ **可作机器判据**,字段缺失/形状变 ⇒ 记"未知";② 上游**人类 prose**
|
|
627
|
+
(stderr / notify 文案)→ **只落盘留痕、不做分支**;③ 上游**内部布局**(第三方包
|
|
628
|
+
`dist/*.js`)→ **不碰**(本次已删净)。
|
|
629
|
+
**依赖边界**:见上方清单 ①(MC 允许而非要求;内置 MCP 是 pi 自带)——本段不再复述。
|
|
630
|
+
第三方依赖为零:本档只用 pi 内置扩展 + 用户自己选的 MC 包。
|
|
580
631
|
**死代码纪律**:`--extensions` 别名与 `extensions: true` 历史字段(只存在约 1 天)
|
|
581
632
|
**已删净**(无移除条件的兼容层不留);扩展策略只有一个入口:`--extension-policy`
|
|
582
633
|
+ 协议字段 `extensionPolicy`。
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
<!-- 存档:docs/reviews/2026-09-30-builtin-mcp-audit.md
|
|
2
|
+
来源:一次真实多视角分析的 result.md 原文(未删改,仅加本头与下方说明)。
|
|
3
|
+
分析场次目录已随 cleanup 删除;文中消息编号不可再核验,仅作溯源线索
|
|
4
|
+
(与代码注释引用约定一致:行为以自描述为准)。 -->
|
|
5
|
+
|
|
6
|
+
# 存档说明
|
|
7
|
+
|
|
8
|
+
- **主题**:自审 —— agents 的联网检索能力从第三方 `pi-mcp-adapter` 换成 pi 内置 MCP
|
|
9
|
+
(`-e builtin:mcp`)+ `exposure: direct`:这个改动站不站得住?有没有没想到的副作用?
|
|
10
|
+
- **场次**:`mv-mv-main-20260930-135705`(3 视角:效率 / 简单 / 铁律;真实 pi 讨论;
|
|
11
|
+
`forkMode=budget`、`extensionPolicy=mc-tools`(声明=生效,无降级)、配额 **meeting=20**
|
|
12
|
+
(用户的默认值配置)、`maxRR=7`;墙钟 **32m43s**、38 条消息、35 次唤醒(15/10/10)、
|
|
13
|
+
rc≠0 0 次 / retry 0 次、收尾中位 1.1s、存续 **共识**(RR 全体 pass)、并行度 2.34;
|
|
14
|
+
对象 = 本仓 HEAD `af9ee53`)
|
|
15
|
+
- **判定**:**替换成立**(净简化 + 职责归还平台 + 第三方依赖清零);同批落地 9 项
|
|
16
|
+
零运行时成本修正;**配置层不动**(三方撤回各自的配置建议——主 pi 实测在
|
|
17
|
+
用全部三个 server:12 个 session / 13,144 次 toolCall 里 51 次 mcp 调用)
|
|
18
|
+
- **本场最重的发现(新问题)**:**per-server 静默失效** —— 某个 MCP server 连不上 /
|
|
19
|
+
凭据过期时,agents 当次静默失去那件工具,分析照常跑完且产物零痕迹。链条(我逐处
|
|
20
|
+
复核过上游源码):`Promise.allSettled` 吞掉失败(`extensions/mcp/index.ts`)→
|
|
21
|
+
`reportProblems` 只 `ctx.ui.notify`(不 exit)→ 非交互模式用 `noOpUIContext`
|
|
22
|
+
(`core/extensions/runner.ts` 的 `notify: () => {}`)→ 我方只在 rc≠0 时读 stderr
|
|
23
|
+
⇒ **`rc=0` ≠ 工具可用**(我此前探针报告里的那条推论说满了,撤回)
|
|
24
|
+
- **本场同时验证**:`-e builtin:mcp` **真生效**(另一场功能臂真搜到网页标题);每唤醒
|
|
25
|
+
成本 **≈ 0**(38 次唤醒「启动前」中位 −1.3s、三 agent 合计 −2s/−5s/+19s,对照 AFT
|
|
26
|
+
445.9s 与 MC historian 447s 差三个数量级);agents **自然**用 MCP = **0 次**
|
|
27
|
+
(边界后逐条统计工具调用:bash 58/65/56、read 7/0/19、write 10/10/15、edit 4/1/0,
|
|
28
|
+
无一条 `mcp__*`)
|
|
29
|
+
- **过程诚实记录**:三视角共撤回/更正 13 项(含两处**曾被判死代码的分支改判为留**、
|
|
30
|
+
一处"唤醒成功 ⇒ MCP 已加载"的推论撤回、若干自己的配置建议撤回)
|
|
31
|
+
- **落地**:`839ef4f`(九项:删无据断言 / rc≠0 落 stderr / 报告「平台能力:未观测」/
|
|
32
|
+
两条守卫注释改准 / `or reason` 真实理由 + 移除条件 / 三条不变式 / 前提 pi≥0.99 /
|
|
33
|
+
exposure 成本口径 / 值域三档边界;495 python + 54 harness 全绿);方法论 #27
|
|
34
|
+
(检测器不得继承被检测对象的失效面 · 多余的文字一旦断言未知即错误)
|
|
35
|
+
|
|
36
|
+
# 多视角分析结果:内置 MCP 替换(`-e builtin:mcp`)与 `exposure: direct` 自审
|
|
37
|
+
|
|
38
|
+
- **场次**:`mv-mv-main-20260930-135705`(效率 / 简单 / 铁律 三视角,模型 `…deepseek-v4.1-flash`, xhigh)
|
|
39
|
+
- **对象**:本仓 HEAD `af9ee53`(`feat(ext-policy)!: mc-tools 第二份入口改用 pi 内置 MCP(删第三方 adapter 依赖)`)
|
|
40
|
+
- **判定**:**替换成立,可以发布**;但必须同批完成 9 项零运行时成本的修正/落字(§6),
|
|
41
|
+
且有一项新发现(per-server 静默失效,§3)需要在文档中如实声明。
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 1. 结论摘要
|
|
46
|
+
|
|
47
|
+
| # | 结论 | 状态 |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| 1 | 第二份入口从第三方 `pi-mcp-adapter` 换成 pi 内置 `-e builtin:mcp` = **净简化 + 职责归还平台** | 三视角一致支持 |
|
|
50
|
+
| 2 | `--no-extensions` 关的是"扩展发现**与内置扩展**" ⇒ **必须**显式 `-e builtin:mcp`(实现前提,已落字) | 已满足 |
|
|
51
|
+
| 3 | **新发现:per-server(连接级)失效在本形态下完全静默**——`rc=0 ≠ 工具可用` | 三视角一致(互补证据) |
|
|
52
|
+
| 4 | 观测能力位(MCP 探针):**本轮不做**,登记为"条件触发的既定路径" + 报告写"能力未观测" | 三方一致(B+登记) |
|
|
53
|
+
| 5 | 两处曾被判"死代码"的分支改判为 **留**(`else: raise` / `or reason`) | 三方一致 |
|
|
54
|
+
| 6 | 一处**无据断言**必须删(`meeting_loop.py:356` 半句,5 个副本) | 三方一致 |
|
|
55
|
+
| 7 | A 层三项(有墙钟成本)**无干净注入点 ⇒ 只文档化**;配置层改动 = **用户拍板项** | 三方一致 |
|
|
56
|
+
| 8 | 本场推荐改动集**不需要任何真实 LLM 运行**验证(单测 + harness,分钟级、0 LLM) | 三方一致 |
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 2. 替换成立的理由(三视角证据)
|
|
61
|
+
|
|
62
|
+
- **铁律(职责边界/复杂度)**:删掉 `resolve_mcp_adapter_entry`(19 行)+ `MCP_ADAPTER_PACKAGE` + 测试侧 6 处 mock,
|
|
63
|
+
用一个**常量** `meeting_fs.BUILTIN_MCP_ENTRY = "builtin:mcp"`(`meeting_fs.py:903`)替代——
|
|
64
|
+
从"解析第三方包内部布局"降为"引用平台标识",是**职责归还平台**;第三方依赖在本档清零
|
|
65
|
+
(本档 = pi 内置扩展 + 用户自选的 MC 包)。
|
|
66
|
+
- **简单(净简化)**:降级状态空间从"2 入口 × 各 on/off + 部分/完全标签"收敛为"1 个可失败入口";
|
|
67
|
+
测试 mock 点 16 → 9;遗留符号全仓活代码 0 命中。
|
|
68
|
+
- **效率(成本)**:固定成本打平/略优——
|
|
69
|
+
- **token 差值 +1,025/请求**(探针 n=2,两臂各两次独立运行同值:含 = 105,334 / 不含 = 104,309;
|
|
70
|
+
配置 = 3 server / 5 工具全 `direct`);
|
|
71
|
+
- **延迟边际 +0.3–0.8s/唤**(被组内方差淹没,与旧 adapter 的 +0.2s/唤同量级);
|
|
72
|
+
- **3 个 server 并行连接**(上游 `extensions/mcp/index.ts:816` `Promise.all`)⇒ 成本 = max(单 server),非 Σ。
|
|
73
|
+
- **功能验证(真跑一次,含内置 MCP 的臂)**:要求"用 MCP 搜一次"的臂真实取回网页标题
|
|
74
|
+
(`The Agent Harness Where the Coding Agent Extends Itself`,pyshine.com)⇒ `builtin:mcp` **确实生效**。
|
|
75
|
+
- **操作前提(已核 pi --help:43 原文 + 上游源码)**:`--no-extensions` 亦关内置扩展
|
|
76
|
+
(`core/extensions/index.ts` 的 `{ name: "mcp", builtin: true }`;`core/resource-loader.ts` 在
|
|
77
|
+
`noExtensions` 时只保留 CLI 显式 `-e`),且 `-e <path>` 接受 `builtin:<name>`。
|
|
78
|
+
- **未知 builtin 名 = loud + fatal**(核实三处源码):`core/resource-loader.ts:715-719`
|
|
79
|
+
(`Unknown built-in extension`)→ `main.ts:793-797` 诊断 → `main.ts:906-916` `process.exit(1)`。
|
|
80
|
+
⇒ 因此 **pi < 0.99 不是静默降级,而是每次唤醒 rc=1 硬失败**(可见)。
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 3. 本场最重要的发现:**per-server 静默失效**(完整链条)
|
|
85
|
+
|
|
86
|
+
**结论:某个 MCP server 连不上 / OAuth 过期时,agents 当次唤醒静默失去联网工具,而分析照常跑完,
|
|
87
|
+
报告里 rc≠0=0、收尾≈0、"生效=mc-tools" 一切干净。**
|
|
88
|
+
|
|
89
|
+
链条(逐处上游源码 + 我方代码):
|
|
90
|
+
|
|
91
|
+
| 环节 | 位置 | 行为 |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| 单 server 连接结果 | `extensions/mcp/index.ts:816` `Promise.all` + **`:818` `Promise.allSettled(getClient)`** | 失败被 **吞掉**,不抛 |
|
|
94
|
+
| 连接失败/需认证 | `:408-419` `reportProblems` → `ctx.ui.notify(…, "warning")` | **不写 `runtime.diagnostics`** ⇒ 不 exit(对照 `main.ts:906-916`) |
|
|
95
|
+
| 10s 帽触发 | `:833-847`(`startupWaitMs` 默认 `:79`=10000) | 只 `ui.notify(..., "info")` |
|
|
96
|
+
| 整体加载失败 | `:820-826` / `:884` | 也只 `ui.notify(..., "error")` |
|
|
97
|
+
| **通知通道本身** | `core/extensions/runner.ts:323-327` `noOpUIContext.notify = () => {}`;`:565` 未传 ui 即用 noOp | **我们的模式(`--mode json` + `--print`)既非 rpc(`rpc-mode.ts:320`)也非 interactive(`interactive-mode.ts:2532`)⇒ notify 是 no-op** |
|
|
98
|
+
| 我方消费 | `meeting_loop.py:515-521` | stderr **只在 `rc≠0` 时**被读;`rc=0` 时丢弃(`_log_wake_done` 只记 session/elapsed/rc,`:527-537`) |
|
|
99
|
+
| 我方产物 | `wake-logs/*.txt`(`meeting_loop.py:491-498`) | 只有命令行全文 = 证"**请求**了 `-e builtin:mcp`",不证"加载成功" |
|
|
100
|
+
| 登记行 | `meeting_loop.py:367-376` | 由策略代码产出 = 证"**策略跑过**",不证任何 server 连接 |
|
|
101
|
+
| session 文件 | 本场三个 agent 会话逐条枚举 | 条目类型无工具集条目;`session` 头只有 fork 元数据;`toolNames`/`availableTools` 递归 **0 命中** ⇒ **生效工具集不上盘** |
|
|
102
|
+
|
|
103
|
+
**⇒ 合起来的完整链条:失败 → 不 exit → 只 notify → notify 是 no-op → 产物里没有任何痕迹。**
|
|
104
|
+
(三视角独立得到同一结论:铁律/0003 §3、简单/0005 §1、效率/0006 §1,证据互补。)
|
|
105
|
+
|
|
106
|
+
**附带修正**:`rc=0 ⇒ MCP 已加载` 的推论**不成立**——它只证明扩展加载器接受了
|
|
107
|
+
`builtin:mcp` 这个名字,**不证明任何 server 连上、任何工具可用**(简单/0005 §1 自撤回该推论)。
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 4. 观测裁决:本轮**不做**探针,改为"登记 + 声明"
|
|
112
|
+
|
|
113
|
+
**报告侧(零成本)**:显式写 **"平台能力:未观测(本档不采)"**,把不可见说出来。
|
|
114
|
+
|
|
115
|
+
**登记(非行动项,写入 `docs/design.md` 决策 20 的"盲区/既定路径"段)**——出现真正消费者时按此实现:
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
触发条件:用户报"agents 没搜到" / 上游给出工具注册的可读信号(例:pi mcp list 之外的可读出口)
|
|
119
|
+
做法:pi mcp list --json 一次/场:--start 采集 → 落盘分析目录 → --report 读文件
|
|
120
|
+
(探针在组合层/start 层,不进 observability——保持"零插桩事后推导")
|
|
121
|
+
代价:常态 +2.8s(实测 2.72–2.80s,n=3);最坏 +T(我们侧超时,T ≤ 10s)→ 记"未验证"
|
|
122
|
+
取值三态:ok / 异常(state≠connected,点名 server)/ 未验证 —— 禁写"不可用"
|
|
123
|
+
护栏:
|
|
124
|
+
1 不进 observability(报告必须是纯事后推导,同目录两次报告一致)
|
|
125
|
+
2 超时 + fail-open(超时/命令缺失 → "未验证",绝不阻塞或失败 run)
|
|
126
|
+
3 三态写死,禁写"不可用"(我们只知道"此刻不可达",不知道"当时不可用")
|
|
127
|
+
4 文档写明只覆盖持续性失效(凭据过期/端点下线),不覆盖"启动正常、中途断"的瞬断
|
|
128
|
+
5 可注入/测试可短路(否则默认单测会真的连三个远端 server:见 §6 计数)
|
|
129
|
+
6 采样必须以 cwd = fork_cwd(agents 的 spawn cwd)启动、继承 PI_CODING_AGENT_DIR
|
|
130
|
+
边界依据:main.ts:585 process.cwd() → :610 runMcpCommand({cwd}) → cli.ts:190/196-197 读
|
|
131
|
+
<cwd>/.pi/mcp.json + trust;而 meeting_loop.py:393 spawn_cwd = fork_cwd or workdir
|
|
132
|
+
⇒ 在仓库根采样,若被分析项目有 .pi/mcp.json,采样集 ≠ agents 所见(把近似当事实)
|
|
133
|
+
两条已被证伪的错路(别再捡):
|
|
134
|
+
① 成功路径 stderr —— notify 是 no-op,什么都没写
|
|
135
|
+
② session 文件 —— 工具集不上盘
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**不做探针的理由(三方一致)**:无现实消费者(不会据此重试/中止/换档)+ 收益频率无数据
|
|
139
|
+
(`mcp.log` 不存在,无失败率口径)+ 会把整批从"**0 次 LLM 验证**"变成"需真跑 e2e 验证(20–40 min)"。
|
|
140
|
+
|
|
141
|
+
**平台状态查询能力(已核实,供将来实现)**:`pi mcp list [--json]` 是文档化子命令
|
|
142
|
+
(`extensions/mcp/cli.ts:37/70`),JSON 形如 `{servers:[…], errors:[…]}`(`:463-470`),
|
|
143
|
+
每项带 `state` / `tools` / `error`;退出码语义 `:461`(有 config error 或任一 enabled server
|
|
144
|
+
`state !== "connected"` ⇒ 非零)。**属"文档化结构化接口",可作机器判据**(见 §9 三档边界)。
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 5. 两处"死代码"改判为**留**(本场主要的自我纠错成果)
|
|
149
|
+
|
|
150
|
+
### 5.1 `else: raise`(`meeting_loop.py:361-366`)——留代码,只改注释
|
|
151
|
+
|
|
152
|
+
- **原判**(简单/0001 §2):与 `:326` 的守卫重复 ⇒ 不可达 ⇒ 删。
|
|
153
|
+
- **改判**:两处拦的是**两种不同失效模式**——`:326` 拦**元组之外**的值(用户传错),
|
|
154
|
+
`:361` 拦**元组之内、但无分支**的值(开发者加值忘加分支;此时 `:326` **不响**)。
|
|
155
|
+
- **删掉的代价**:新策略值会静默落进 `all` 档 = pi 默认发现 = 载入 AFT/MC 全档
|
|
156
|
+
⇒ **分钟×N 级**、且无信号(成本不对称:保留 0 成本 vs 删除后最坏分钟×N)。
|
|
157
|
+
- **要改的**:`meeting_loop.py:362-363` 现注释"当前守卫下不可达"与同句"值域增长时这里响"
|
|
158
|
+
自相矛盾——改为:
|
|
159
|
+
> 当 `EXTENSION_POLICIES` 增长而分派未同步加分支时在此响(元组**内**无分支 ≠ 元组**外**的值:
|
|
160
|
+
> 后者由 `:326` 的守卫拦);当前元组下不可达,**不是死代码**。
|
|
161
|
+
|
|
162
|
+
### 5.2 `or reason`(`observability._report_extension_line`)——留分支,改测试论证 + 写移除条件
|
|
163
|
+
|
|
164
|
+
- **原判**(简单/0001 §3):现行 writer 下 `reason ⇒ d≠e`,故 `or reason` 永不改变结果 ⇒ 死判定;单测还"冻住"了它。
|
|
165
|
+
- **改判**:**reader/writer 版本结构性解耦**——
|
|
166
|
+
- 快照**只含 4 个模块**(`start_discussion.py:429-430`:meeting_loop/fs/core/engine),
|
|
167
|
+
**不含 observability**;而 `--report` 走**主仓**代码(`mv_cli.py:36,287-289` → `start_discussion.py:582`);
|
|
168
|
+
- ⇒ loop 日志由"分析启动那一刻的快照"写,报告由"今天的代码"读;
|
|
169
|
+
- `af9ee53^:361-362` 真的产出过 `声明=mc-tools 生效=mc-tools 降级原因=部分:…`(**d==e 且 reason 非空**)。
|
|
170
|
+
- **要求**:① 测试 docstring 改**真实理由**(跨版本回落,源行 `git show af9ee53^:meeting_loop.py:361-362`);
|
|
171
|
+
② 补**移除条件**:"当不再存在 af9ee53 之前启动、且仍需 `--report` 的分析目录时可删"。
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 6. 必须修:一处**无据断言**(5 个副本同批改)
|
|
176
|
+
|
|
177
|
+
`meeting_loop.py:356` 的 `downgrade_reason = f"{err};内置 MCP 工具不受影响"` —— 在 §3 已证
|
|
178
|
+
"工具是否可用不可观测"的前提下,这是**断言了未知**("多余的文字一旦断言了未知,就从啰嗦升级为错误")。
|
|
179
|
+
|
|
180
|
+
**落点取"删"而非"改"**(理由:改写会在 5 处**再复述一遍**"入口"这个已在
|
|
181
|
+
`meeting_loop.py:343`、`meeting_fs.py:870/878` 单点声明过的事实;删则净减文字且假保证从仓库消失):
|
|
182
|
+
|
|
183
|
+
| # | 位置 | 动作 |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| 1 | `meeting_loop.py:356` | `downgrade_reason = err`(删半句) |
|
|
186
|
+
| 2 | `tests/test_main_paths.py:686` | fixture 文本同步删 |
|
|
187
|
+
| 3 | `tests/test_meeting_loop.py:862` | 断言同步删 |
|
|
188
|
+
| 4 | `tests/test_meeting_loop.py:798-804`(docstring) | "降级不牵连内置 MCP"改述为入口层或删 |
|
|
189
|
+
| 5 | `docs/design.md:578-579`(半句) | 删,保留"原因字段=入口层失败原因" |
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## 7. A 层:有墙钟成本的三(+1)项 —— **只文档化**,无干净注入点
|
|
194
|
+
|
|
195
|
+
| 项 | 数字/依据 | 口径 |
|
|
196
|
+
|---|---|---|
|
|
197
|
+
| ① 连接帽 `startupWaitMs` 10s × N | `index.ts:79`(默认 10000)+ `:833-847`(`before_agent_start` 等一次)+ `:177/796/835`(`waitedForStartup` 是 **per-session**,而我们**一唤一进程**)⇒ 每唤都等一次;某 server 慢/挂 → 最坏 **+350s/场(22min 场的 +27%)** | 上界、非均值 |
|
|
198
|
+
| ② `exposure=direct` 的 token | **差值 +1,025 tok/请求**(agent 侧实测,n=2,5 工具全 direct) | 配置一变(server 数或 exposure)**作废** |
|
|
199
|
+
| ③ 全局 `mcp.json` 共享税 | 主 pi 每次请求同量级 **+~1k(机制外推、未测)** | **外推,不是测量** |
|
|
200
|
+
| ④ `--approve` ⇒ 被分析项目的 `.pi/mcp.json` 进 agents 工具面 | ⇒ A 层成本**随被分析项目变化、无上界**;唯一杠杆 `--no-approve` 不可行(会丢项目 AGENTS.md) | 记为"无上界" |
|
|
201
|
+
|
|
202
|
+
**为什么只能文档化**:项目级 `mcp.json` 只在 `<cwd>/.pi/mcp.json` 且项目被 trust 时读
|
|
203
|
+
(`extensions/mcp/config.ts:100-101`),而 agents 的 cwd = **被分析项目**(写进去=污染用户仓库);
|
|
204
|
+
无 env/CLI 覆盖;`startupWaitMs` 是**扩展注册选项**(`index.ts:76`),`config.ts` 无字段,无注入点。
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## 8. 明确不做 + 一个用户拍板项
|
|
209
|
+
|
|
210
|
+
**不做**:报告新增 MCP 位/字段;启动期校验;import 期断言(要么**替换** `else`、要么不做,
|
|
211
|
+
手写"已处理集合"= 第二份会漂的抄本);任何"每唤执行"的检查;`pi mcp list` 探针(**本轮**,见 §4 登记)。
|
|
212
|
+
|
|
213
|
+
**用户拍板项(不由分析代拍)**:是否做配置层改动(`enabled:false` 子集化 / per-server `exposure`)。
|
|
214
|
+
- 收益 ≈ **$0.003/场**(≈28.7k tok);
|
|
215
|
+
- 代价落在**主 pi**:`mcp.json` 全局共享,`exposure` 不 gate 连接(`index.ts:805` 是 `servers.filter(isEnabled)`)
|
|
216
|
+
⇒ 混合 exposure 只省 token,`enabled:false` 才同时降 token 与失效面;
|
|
217
|
+
- **实测**(效率,方向性):最近 12 个主 pi session / 13,144 次 toolCall,其中 `mcp` 网关 **51 次**,
|
|
218
|
+
参数里 web-reader **12** / web-search-prime **9** / zread **5** ⇒ **三个 server 主 pi 都在用**
|
|
219
|
+
(方法限制:子串匹配、含 describe 类调用,只作方向性证据)。
|
|
220
|
+
- ⇒ 三方均**撤回**原建议(效率撤回混合 exposure,简单撤回 `enabled:false`),默认**不动配置**;
|
|
221
|
+
若用户明确"这两个我在主 pi 不用",则按简单版 `enabled:false` 做(少一种机制)。
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## 9. 纪律产出(可复用,建议进相应文档)
|
|
226
|
+
|
|
227
|
+
1. **"落盘可以,解析不行"——细化为三档**:
|
|
228
|
+
| 档 | 对象 | 规则 |
|
|
229
|
+
|---|---|---|
|
|
230
|
+
| ① | 上游**文档化结构化接口**(`pi mcp list --json` 字段、session 条目) | **可作机器判据**;字段缺失/形状变 → **"未知"**,不写"不可用" |
|
|
231
|
+
| ② | 上游**人类 prose**(stderr、notify 文案) | **只落盘/留痕**,不做分支 |
|
|
232
|
+
| ③ | 上游**内部布局**(第三方包 `dist/*.js`) | **不碰**(本次已删) |
|
|
233
|
+
(本仓已有合规先例:`observability.py:405-438` 解析 session 的 `thinking_level_change` 得"生效档位"。)
|
|
234
|
+
2. **检测器不得继承被检测对象的失效面**(效率/0010 §2;继承不可避免 ⇒ 关键是**上界 + fail-open**,
|
|
235
|
+
不是"消除继承"):`pi mcp list` 要连那三台 server,若某台挂住,探针自己也会挂住 ⇒ 必须我们侧套 `T ≤ 10s`。
|
|
236
|
+
3. **多余的文字一旦断言未知,就从"啰嗦"升级为"错误"**(简单/0005 §3,直接产出 §6 的修复)。
|
|
237
|
+
4. **无证据的改动建议,不比无证据的断言干净**(简单/0006 §1 自陈)。
|
|
238
|
+
5. **数字必须带口径**:本场统一为——写**差值**(+1,025)不写绝对值(105k 随 fork 源大小漂);
|
|
239
|
+
标明 n、配置版本、人口(哪些 session)、以及**机制外推 vs 实测**。
|
|
240
|
+
6. **三条不变式落字**(否则会被下一个人当死代码删掉):① 两处守卫各拦一种失效模式;
|
|
241
|
+
② observability 不在快照、`--report` 永远用主仓版本;③ **非交互模式下 MCP server 状态不被报告 ⇒ rc=0 ≠ 工具可用**。
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 10. 落地方式与验证(**0 次真实 LLM 运行**)
|
|
246
|
+
|
|
247
|
+
- **合并成一次改动 + 一次全量验证**(不要分成多个周期):
|
|
248
|
+
① `:362-363` 注释;② `or reason` 测试 docstring + 移除条件;③ 三条不变式落字;
|
|
249
|
+
④ 前提声明"pi ≥ 0.99";⑤ §6 的 5 个副本删半句;⑥ token 数的口径(差值/人口/机制);
|
|
250
|
+
⑦ `rc≠0` 时截断落 `stderr`(**只落不解析**、**归"通用诊断"**、**带截断口径**,
|
|
251
|
+
先例 `meeting_fs.py:356` 的 `[:200]`);⑧ `tests/test_meeting_loop.py:737` 的 `0–2` → `1–2`(stale);
|
|
252
|
+
⑨ 报告写"平台能力:未观测"。
|
|
253
|
+
- **验证手段 = 现有测试套件**(492 py + 54 harness,分钟级、0 LLM)。
|
|
254
|
+
唯一有行为的是第 ⑦ 条——用**单元测试造 rc≠0 的假进程输出**验证,**不需要 e2e**
|
|
255
|
+
(本场无任何时序敏感的行为改动;按旧习惯"改完跑一场"是 20–40 分钟换不到本场所需证据)。
|
|
256
|
+
- **`rc≠0` 落 stderr 的归类要求**:必须在文档里归到"通用诊断",**不得**列在"MCP 可见性"名下——
|
|
257
|
+
§3 已证它 0 覆盖 per-server 静默;归类错了等于用一个 0 覆盖的机制去结一笔未结的账。
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## 11. 过程诚实记录(撤回与更正)
|
|
262
|
+
|
|
263
|
+
| 谁 | 撤回/更正 | 原因 |
|
|
264
|
+
|---|---|---|
|
|
265
|
+
| 铁律 | 撤回"给 `builtin:mcp` 常量加能力位校验"(F2(a));撤回 F1 的"版本静默失效"分支;撤回自己提议的 LLM 探针 | 所有权分层(入口路径归我们→解析;名字归 pi→引用不校验;server 状态归 pi 运行时→查询);源码链已证 loud+fatal;源码读比探针便宜且确定 |
|
|
266
|
+
| 简单 | 撤回 `0001` §2(`else` 不可达)、§3(`or reason` 死判定)、`0004` §5/`0005` §5(`enabled:false`)、`0004` §4("唤醒成功 ⇒ MCP 已加载")、`0004` §1(import 期断言) | 前提错误 + reader/writer 解耦 + 主 pi 实测 + no-op notify |
|
|
267
|
+
| 效率 | 撤回 `0003` §1 的覆盖面(`rc≠0` 落 stderr 抓不到 per-server)、撤回 `0001` §4 子集化建议、让出 (A) 探针 | 覆盖面经复核不成立;主 pi 在用三 server;无消费者 + 会把验证成本从分钟级推到 20–40 分钟 |
|
|
268
|
+
| 计数更正 | `setup_environment` 的直接执行点 = **7 处**(`tests/test_spec.py:297/566/589/599/635`、`tests/test_flow_composition.py:98`、`tests/test_startup_defaults.py:52`),另 `tests/test_main_paths.py:873` 一处是 **mock** ⇒ 记 **7+1** | 效率/0013 更正,铁律在轮转中在案确认 |
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## 12. 三方立场清单(谁在什么视角上贡献了什么)
|
|
273
|
+
|
|
274
|
+
- **效率**:A 层定价(10s×N ≈ +350s/场最坏、+1,025 tok/差值、主 pi 共享税)、
|
|
275
|
+
`pi mcp list --json` 实测价签(2.72–2.80s,n=3)、"每场一次 vs 每唤 = 35 倍差"、
|
|
276
|
+
"检测器继承失效面"、`--approve ⇒ 无上界`、"B 层可 0 LLM 验证 ⇒ 不要 e2e"。
|
|
277
|
+
- **简单**:净简化计量(mock 16→9、状态空间收敛)、三处自我撤回与一次接受驳回、
|
|
278
|
+
"三条不变式落字"(含本场核心产出:可读性缺失会被误读成复杂度过剩,进而诱导删除正确代码)、
|
|
279
|
+
"`:356` 半句取删不取改"、护栏 5"可注入是前提"、探针归类 A 层(不入 B 层零成本批)。
|
|
280
|
+
- **铁律**:职责分层(入口路径/平台标识/运行时状态三分)、`else` 与 `or reason` 的实物反驳、
|
|
281
|
+
per-server 静默链条的完整取证(`runner.ts:327` no-op)、`pi mcp list` 的 **cwd=fork_cwd 前提**、
|
|
282
|
+
三档边界细化、`:356` 的编辑边界与 5 副本清单、登记文本的护栏合并。
|
|
283
|
+
|
|
284
|
+
**无未决分歧**。唯一留在用户手里的是 §8 的配置层改动(`enabled:false` / exposure 子集化)。
|
package/docs/reviews/README.md
CHANGED
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
| `2026-09-25-extension-mechanisms-review.md` | 复验 0.8.2 三处机制(报告落盘 / `--set-viewer` / 观看命令通道)+ 测试覆盖与断言强度 | **① 显示层失败会跳过清理**(BrokenPipe 逃逸 → rmtree 被跳过、目录残留;修法 `_print_best_effort` + rmtree 进 finally ⇒ 清理必达);**② `--set-viewer` 半成功**(校验在写之后 → rc≠0 但文件已写入;改 B′ 校验前移);③ 通道模型由「三出口」收敛为四通道角色表,并证实 custom_message 随 fork 进每场上下文;文档三处「不持久化」复述、断言偏弱、prompt 复述、fail-open 宽窄不对称 | `51a4535` |
|
|
41
41
|
| `2026-09-25-patch-audit-review.md` | 审阅 0.8.0 → 0.9.0 一周改动是否有补丁堆叠 / 复杂度失配 / 职责边界问题 | **判定:没有补丁堆叠**;真问题是**文档漂移 F1**(两份入口降级语义改了、6 处复述没跟)、**名实不符 F2/S3**(`_viewer_set_errors` 自称唯一组合点+数量≥2,皆不成立)、**契约只有注释 F4**(cleanup 裸 print 禁令 → 本仓首条 AST 结构断言);顺带:自然使用复测 `ctx_search` 0 次(不可证伪那句指引)、MCP adapter 无收尾尾巴 | `6c024be` |
|
|
42
42
|
| `2026-09-27-aft-mc-evidence-audit.md` | 复盘「屏蔽 AFT」决策(对照数据 / 结论链 / 现默认扩展策略是否成立) | **判定:成立**(承重换成真场:AFT 收尾 66–78% vs ≈0%;MC historian e2e21 62 次/60 失败 vs e2e23 0 次 + 上游 `#916`)。**最重一条 = 审计自身证据链**:决策 20 把 09-13 那批**装置有缺陷**的探针数字当「受控对照」(#26 早已记录)→ 两笔退出证据位;另订正 entry 结构句(2 个生命周期钩子,非「无 hook」)、adapter 每轮钩子 = 已知盲区、e2e23 的 12m31s 标为源不可复核;新增**数字口径规则**与**回退判据**(带「需求场景在窗口外」前置限定)+ 加回 AFT 的定价程序 | `ffe28aa` |
|
|
43
|
+
| `2026-09-30-builtin-mcp-audit.md` | 内置 MCP 替换(`-e builtin:mcp`)与 `exposure: direct` 自审 | **per-server 静默失效**(`rc=0` ≠ 工具可用:失败被 allSettled 吞掉 → 只走非交互模式下为 no-op 的 notify → 产物零痕迹);每唤醒成本 ≈ 0(38 唤);agents 自然用 MCP = 0 次;三视角撤回/更正 13 项 | `839ef4f` |
|
|
43
44
|
|
|
44
45
|
## 环境口径(读报告时的背景)
|
|
45
46
|
|
package/docs/test-methodology.md
CHANGED
|
@@ -450,3 +450,70 @@ historian vs e2e23 48.1s/唤醒 + historian 0)与 **MC 自己的 DB 账本**
|
|
|
450
450
|
验证 + fork 源(带交接叙事)做规模/成本验证;每臂硬超时;统计只算**边界之后**的
|
|
451
451
|
条目(继承历史的 toolCall 不算——第一版踩过);同条件重复 ≥3 次压 provider 方差
|
|
452
452
|
(单样本差异被噪音淹没的实例:同条件 3.9s vs 44s,11 倍)。
|
|
453
|
+
|
|
454
|
+
### 27. 证据链的两条纪律:检测器不得继承失效面 · 多余的文字一旦断言未知即错误(2026-09-30 内置 MCP 自审)
|
|
455
|
+
|
|
456
|
+
**来源**:把 agents 的联网检索从第三方 `pi-mcp-adapter` 换成 pi 内置 MCP(`-e builtin:mcp`)后的
|
|
457
|
+
一次多视角自审(`docs/reviews/2026-09-30-*-audit.md` 同批)。它挖出一条我们**从未意识到**的
|
|
458
|
+
失效路径,并顺带产出两条通用纪律。
|
|
459
|
+
|
|
460
|
+
**发现(结论,可复用)**:某个 MCP server 连不上 / 凭据过期时,**agents 当次静默失去那件工具**,
|
|
461
|
+
分析照常跑完且产物里**一点痕迹都没有**——`rc=0` ≠ 工具可用。
|
|
462
|
+
|
|
463
|
+
链条(逐处上游源码,2026-09-30 核):
|
|
464
|
+
|
|
465
|
+
```
|
|
466
|
+
单 server 失败 → Promise.allSettled 吞掉(extensions/mcp/index.ts)
|
|
467
|
+
→ reportProblems → ctx.ui.notify(..., "warning")(不 exit)
|
|
468
|
+
→ 非交互模式用的是 noOpUIContext(core/extensions/runner.ts: notify 是空函数)
|
|
469
|
+
→ 我们只在 rc≠0 时读 stderr ⇒ 什么都没留下
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
**纪律一:检测器不得继承被检测对象的失效面。**
|
|
473
|
+
想拿"服务器状态"当判据,最容易想到的就是去问那台服务器(例:`pi mcp list`)——但那样一来,
|
|
474
|
+
**它挂住时检测器自己也挂住**。继承往往不可避免(要判就必须连),所以重点不是"消除继承",而是
|
|
475
|
+
给检测器**加我们侧的上界 + fail-open**(超时即记"未验证",绝不阻塞生产路径),并**明文写出它覆盖
|
|
476
|
+
什么、不覆盖什么**(只覆盖持续性失效,不覆盖"启动正常、中途断"的瞬断)。
|
|
477
|
+
|
|
478
|
+
**纪律二:多余的文字一旦断言未知,就从"啰嗦"升级为"错误"。**
|
|
479
|
+
本次的实物:降级时我写了一行 `downgrade_reason = f"{err};内置 MCP 工具不受影响"`——在"工具是否
|
|
480
|
+
可用不可观测"已经成立的前提下,后半句就是**断言了未知**。处理取**删**不取改:改写会在多处
|
|
481
|
+
**再复述**一遍入口事实(那句话在代码里已单点声明),删则净减文字、假保证从仓库消失。
|
|
482
|
+
|
|
483
|
+
**推论(值域边界,落地成三档)**:
|
|
484
|
+
| 档 | 对象 | 规则 |
|
|
485
|
+
|---|---|---|
|
|
486
|
+
| ① | 上游**文档化结构接口**(`pi mcp list --json` 的字段、session 条目) | **可作机器判据**;字段缺失/形状变 ⇒ 记"未知",改写"不可用" |
|
|
487
|
+
| ② | 上游**人类 prose**(stderr、notify 文案) | **只落盘留痕**,不做分支 |
|
|
488
|
+
| ③ | 上游**内部布局**(第三方包 `dist/*.js`) | **不碰**(本次正是删掉了这类耦合) |
|
|
489
|
+
|
|
490
|
+
**附带收获**:① 分析目录的运行快照只含 4 个模块,`observability` 不在其中 ⇒ `--report` 永远用
|
|
491
|
+
**主仓今天**的代码读**当时** writer 写的日志——判定条件必须容得下旧 writer 的形态(这是"报告里
|
|
492
|
+
一个看似多余的 `or reason`"的真实理由);② 报告新增一行「平台能力:未观测」:**把不可见说出来**,
|
|
493
|
+
与"缺席≠0"是同一条纪律的两面。
|
|
494
|
+
|
|
495
|
+
### 28. 补丁脚本纪律:文本先落文件 · 断言后一次写(2026-09-30 一天内三次同坑)
|
|
496
|
+
|
|
497
|
+
**现象**:一天里三次用 shell heredoc 写"含中文引号的 Python 补丁",三次都在
|
|
498
|
+
`SyntaxError: invalid syntax. Perhaps you forgot a comma?` 上炸——原因是把 `"…"`(中文引号)或
|
|
499
|
+
`"`(英文双引号)嵌进了双引号 Python 字符串里。报错信息("忘了逗号")还**指向错误方向**,
|
|
500
|
+
每次都要额外一轮排查。**三次都没有写坏文件**——靠的是下面第②条。
|
|
501
|
+
|
|
502
|
+
**三条纪律**:
|
|
503
|
+
|
|
504
|
+
1. **文本先落文件,拼接用纯 ASCII 短脚本。** 含引号 / 多行 / 中文的补丁正文,一律先用
|
|
505
|
+
`write` 工具写成独立文件,再用一个**只含 ASCII** 的小脚本把它拼进目标(读文件 → 定位 →
|
|
506
|
+
插入 → 写回)。不要在命令行里"现场拼字符串"——shell + Python + 中文引号三层转义叠加 =
|
|
507
|
+
必然踩坑。
|
|
508
|
+
2. **先定位再断言,全部通过后才 write 一次。** 补丁脚本的结构固定为:
|
|
509
|
+
`读全文 → 对每个改动 assert 唯一命中 → 改内存 → 最后 write`。失败时**全或无**:
|
|
510
|
+
assert 失败 ⇒ 一个字节都不写。今天两次 `AssertionError` 都是"未写"状态,靠的就是这个顺序;
|
|
511
|
+
反面写法(边改边写)在 assert 失败时会留下**半改**的文件。
|
|
512
|
+
3. **行号定位必须配关键字断言。** 用行号改(比大段文本匹配更稳)时,每一行都要
|
|
513
|
+
`assert "关键字" in lines[i]`——行号漂移时立刻响,而不是默默改错行。今天的实例:
|
|
514
|
+
`assert "形状" in lines[363]` 失败 ⇒ 说明我对注释占几行的假设错了 ⇒ 先打印真实行再改。
|
|
515
|
+
纯行号(无断言)的补丁 = 定时炸弹。
|
|
516
|
+
|
|
517
|
+
**为什么值得单列**:这三条管的不是"代码对不对",而是"**改代码的手段**本身会不会引入故障"。
|
|
518
|
+
工具的选择要按内容特征走(有引号/多行/中文 ⇒ 块文件;纯 ASCII 单行 ⇒ 命令行即可),
|
|
519
|
+
这不是口味问题——一天三次的重复率说明它是**结构性**的,不是手滑。
|
package/meeting_fs.py
CHANGED
|
@@ -294,9 +294,9 @@ def resolve_mc_tools_entry(agent_dir=None):
|
|
|
294
294
|
`resolveSiblingEntryPath("subagent-entry.js")`)定位它;我们等价地读它声明的
|
|
295
295
|
扩展入口(`pi.extensions[0]`),再取同目录下的 subagent-entry.js。
|
|
296
296
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
297
|
+
失败语义:任一步缺失返回 `(None, 原因)` ——**由调用方按策略决定**:loop
|
|
298
|
+
生产态做**可见降级**(本档唯一"可失败"的入口就是它;内置 MCP 入口是常量),
|
|
299
|
+
严格态(`MV_MC_TOOLS_STRICT=1`)直接报错。
|
|
300
300
|
"""
|
|
301
301
|
pkg_dir, exts, err = _declared_extensions(MC_PACKAGE, agent_dir)
|
|
302
302
|
if err:
|
|
@@ -309,25 +309,6 @@ def resolve_mc_tools_entry(agent_dir=None):
|
|
|
309
309
|
f"({os.path.relpath(cand, pkg_dir)})——上游版本可能改了布局")
|
|
310
310
|
|
|
311
311
|
|
|
312
|
-
def resolve_mcp_adapter_entry(agent_dir=None):
|
|
313
|
-
"""解析 MCP adapter 的扩展入口(它声明的 `pi.extensions[0]`)——mc-tools 第二份。
|
|
314
|
-
|
|
315
|
-
为什么需要:MCP 工具(web_search / web_reader / zread…)由 pi-mcp-adapter
|
|
316
|
-
提供,而 `--no-extensions` 关掉的是**扩展发现**——显式 `-e` 路径照常生效
|
|
317
|
-
(pi --help 原文)。不显式加载 = agents 完全没有联网检索能力。
|
|
318
|
-
|
|
319
|
-
与 MC 的差别:这里要的**就是主入口本身**(它注册 MCP 工具),不取兄弟文件。
|
|
320
|
-
"""
|
|
321
|
-
pkg_dir, exts, err = _declared_extensions(MCP_ADAPTER_PACKAGE, agent_dir)
|
|
322
|
-
if err:
|
|
323
|
-
return None, err
|
|
324
|
-
cand = os.path.normpath(os.path.join(pkg_dir, exts[0]))
|
|
325
|
-
if os.path.isfile(cand):
|
|
326
|
-
return cand, ""
|
|
327
|
-
return None, (f"{MCP_ADAPTER_PACKAGE} 声明的入口不存在"
|
|
328
|
-
f"({os.path.relpath(cand, pkg_dir)})")
|
|
329
|
-
|
|
330
|
-
|
|
331
312
|
def pi_agent_dir():
|
|
332
313
|
"""pi 的 agent 目录(`$PI_CODING_AGENT_DIR` 或 `~/.pi/agent`)——**单一实现**。
|
|
333
314
|
|
|
@@ -885,17 +866,17 @@ def parse_log_nameonly(output):
|
|
|
885
866
|
# agents 需要主项目背景(背景蒸馏机制已移除),这是它的补充通道;
|
|
886
867
|
# 该入口 = **工具注册 + 两个生命周期钩子**(开/关 DB),**无** historian/压缩执行钩子
|
|
887
868
|
# (真场计数 historian 0:e2e24 0/3、e2e25 0;ctx_search 可用;成本未测得显著差异)。
|
|
888
|
-
#
|
|
889
|
-
# MC_TOOLS_STRICT_ENV
|
|
869
|
+
# 代价:MC 那份入口**允许而非要求**(缺它 = 可见降级;严格模式见
|
|
870
|
+
# MC_TOOLS_STRICT_ENV);内置 MCP 是常量入口、恒在
|
|
890
871
|
# none : 零扩展——最快、**零依赖**(不依赖任何扩展;无 MC 的机器/CI 用这档)
|
|
891
872
|
# all : 走 pi 默认扩展发现(A/B 实验与显式 opt-in 用)
|
|
892
873
|
# (顺序只影响 CLI 帮助的罗列——**不承载语义**,勿按下标取值:
|
|
893
874
|
# 默认档看 DEFAULT_EXTENSION_POLICY)
|
|
894
875
|
EXTENSION_POLICIES = ("mc-tools", "none", "all")
|
|
895
876
|
DEFAULT_EXTENSION_POLICY = "mc-tools"
|
|
896
|
-
# mc-tools
|
|
897
|
-
#
|
|
898
|
-
#
|
|
877
|
+
# mc-tools 档**允许而非要求** MC 那份入口(ctx_search)——它现在是唯一"可失败"的
|
|
878
|
+
# 入口(内置 MCP 是常量、恒在),缺失时**可见**(登记行点名原因);
|
|
879
|
+
# 语义清单见 docs/design.md 决策 20——无静默铁律。
|
|
899
880
|
# 测试/探针要保真(确认"本场确实带着 ctx_search 在跑")时,用环境变量把它变严格:
|
|
900
881
|
# MV_MC_TOOLS_STRICT=1 → 解析失败即报错退出(测试环境准确性优先,用户 2026-09-14 定)
|
|
901
882
|
MC_TOOLS_STRICT_ENV = "MV_MC_TOOLS_STRICT"
|
|
@@ -912,9 +893,14 @@ def mc_tools_strict():
|
|
|
912
893
|
# 入口是它的内部文件,路径解析见 resolve_mc_tools_entry 的 docstring)
|
|
913
894
|
MC_PACKAGE = "@cortexkit/pi-magic-context"
|
|
914
895
|
|
|
915
|
-
#
|
|
916
|
-
#
|
|
917
|
-
|
|
896
|
+
# mc-tools 档的第二份入口 = pi 的**内置** MCP 扩展(pi 0.99+ 自带,无第三方包)。
|
|
897
|
+
# 为什么写成常量:它不需要"解析"——名字由 pi 自己注册(`builtin:` 前缀 + 名字);
|
|
898
|
+
# 这是有意去掉一个外部依赖(`pi-mcp-adapter` 的入口解析/版本漂移全没了)。
|
|
899
|
+
# 为什么必须显式 `-e`:`--no-extensions` 关的是"扩展发现**与内置扩展**"(pi --help
|
|
900
|
+
# 原文)——内置 MCP 也在关停范围;证据:上游 `core/extensions/index.ts` 里
|
|
901
|
+
# `{ name: "mcp", builtin: true }`,`core/resource-loader.ts` 在 noExtensions 时
|
|
902
|
+
# 只保留 CLI 显式 `-e` 的扩展(2026-09-30 读源码核实)。
|
|
903
|
+
BUILTIN_MCP_ENTRY = "builtin:mcp"
|
|
918
904
|
|
|
919
905
|
FORK_MODES = ("budget", "compaction", "full")
|
|
920
906
|
DEFAULT_FORK_MODE = "budget"
|
package/meeting_loop.py
CHANGED
|
@@ -330,53 +330,55 @@ 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
|
-
# mc-tools = 零扩展 +
|
|
334
|
-
# 并入默认档,不再新增档位——保持简单):
|
|
333
|
+
# mc-tools = 零扩展 + 显式加载**两份能力**(零第三方依赖):
|
|
335
334
|
# ① MC 的 subagent-entry(工具注册 + 生命周期钩子,无 historian)→ ctx_search
|
|
336
|
-
# ② MCP
|
|
337
|
-
# 为什么必须显式 -e:`--no-extensions`
|
|
338
|
-
# (pi --help
|
|
339
|
-
#
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
entry, err = resolver()
|
|
346
|
-
if entry:
|
|
347
|
-
resolved.append((label, entry))
|
|
348
|
-
else:
|
|
349
|
-
missing.append((label, err))
|
|
335
|
+
# ② pi **内置** MCP 扩展 → web_search / web_reader / zread 等
|
|
336
|
+
# 为什么必须显式 -e:`--no-extensions` 关的是"扩展发现**与内置扩展**"
|
|
337
|
+
# (pi --help 原文)——内置 MCP 也在关停范围,不显式加载 agents 就没有
|
|
338
|
+
# MCP 工具;`-e <path>` 同时接受 `builtin:<name>`(pi --help 原文)。
|
|
339
|
+
# 为什么用内置而不再用第三方 adapter:pi 0.99+ 自带,少一个外部依赖
|
|
340
|
+
# (用户 2026-09-30 定);此前的 adapter 入口解析机制随之删除。
|
|
341
|
+
# 降级语义:唯一"可失败"的入口是 MC(解析第三方包的内部文件)——
|
|
342
|
+
# 失败则**本档核心能力(ctx_search)不到位** → 生效=none,原因里点名;
|
|
343
|
+
# 内置 MCP 是常量入口、恒在,故降级不影响它(原因字段会说明这一点)。
|
|
350
344
|
cmd += no_ext
|
|
351
|
-
|
|
345
|
+
cmd += ["-e", meeting_fs.BUILTIN_MCP_ENTRY]
|
|
346
|
+
entry, err = meeting_fs.resolve_mc_tools_entry()
|
|
347
|
+
if entry:
|
|
352
348
|
cmd += ["-e", entry]
|
|
353
|
-
|
|
354
|
-
reason = ";".join(f"{label} 不可用({err})" for label, err in missing)
|
|
349
|
+
else:
|
|
355
350
|
if meeting_fs.mc_tools_strict():
|
|
356
351
|
# 严格模式(测试/探针保真):缺入口即响,不降级——否则测试可能在
|
|
357
|
-
# "
|
|
358
|
-
log(agent, f"[fatal] mc-tools 档入口解析失败(严格模式):{
|
|
359
|
-
raise RuntimeError(f"mc-tools 档不可用: {
|
|
360
|
-
#
|
|
361
|
-
|
|
362
|
-
|
|
352
|
+
# "没装 MC"的环境里通过,而 ctx_search 从未生效
|
|
353
|
+
log(agent, f"[fatal] mc-tools 档入口解析失败(严格模式):{err}")
|
|
354
|
+
raise RuntimeError(f"mc-tools 档不可用: {err}")
|
|
355
|
+
# 允许而非要求:缺 MC → 少一份 -e,但**可见**
|
|
356
|
+
# 原因只写**入口层**事实(err 来自解析)。不写“MCP 工具不受影响”:
|
|
357
|
+
# 工具是否可用我们观测不到(非交互模式下 server 状态不进任何产物,
|
|
358
|
+
# 见 docs/design.md 决策 20「平台能力的可见性」)。
|
|
359
|
+
downgrade_reason = err
|
|
360
|
+
effective_policy = "none"
|
|
363
361
|
elif extension_policy == "all":
|
|
364
362
|
# pi 默认发现:**不加**任何 --no-*、也不加 -e(A/B 与显式 opt-in)
|
|
365
363
|
pass
|
|
366
364
|
else:
|
|
367
|
-
#
|
|
368
|
-
#
|
|
365
|
+
# 失败模式不同,与上面 :326 的守卫不可互相替代:
|
|
366
|
+
# · :326 拦**元组之外**的值(用户传错)
|
|
367
|
+
# · 这里拦**元组之内、但无分支**的值(加值忘加分支;此时 :326 不响)
|
|
368
|
+
# 删掉的代价不对称:新策略值会静默落进 `all` 档(= pi 默认发现 =
|
|
369
|
+
# 载入 AFT/MC 全档,分钟×N 级且无信号);保留 0 成本。当前三档都有
|
|
370
|
+
# 分支 ⇒ 本分支不可达,但**不是死代码**。
|
|
369
371
|
raise RuntimeError(
|
|
370
372
|
f"扩展策略分派未穷尽: {extension_policy!r}"
|
|
371
373
|
f"(合法值: {'/'.join(meeting_fs.EXTENSION_POLICIES)})")
|
|
372
374
|
if first_wake:
|
|
373
375
|
# 登记行(观测面的稳定字段;报告据此给"声明 vs 生效")。只在首唤打:
|
|
374
376
|
# 策略在一次运行内不变,变了也是配置错误(重跑即可)。
|
|
375
|
-
#
|
|
376
|
-
#
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
377
|
+
# 降级时把原因写进**登记行的同一行**(S2:第二行是复述,已删——报告只解析
|
|
378
|
+
# 本行,`observability._report_extension_line` 的 regex 匹配到行尾)。
|
|
379
|
+
# 不再有"部分/完全"标签:本档只有 MC 一个入口需要解析(内置 MCP 是常量),
|
|
380
|
+
# 失败即"核心能力缺失",原因本身会点名(2026-09-30)。
|
|
381
|
+
reason = f" 降级原因={downgrade_reason}" if downgrade_reason else ""
|
|
380
382
|
log(agent, f"扩展策略: 声明={extension_policy} 生效={effective_policy}"
|
|
381
383
|
f" strict={int(meeting_fs.mc_tools_strict())}{reason}")
|
|
382
384
|
model = cfg.get("model") or ""
|
|
@@ -518,6 +520,14 @@ def wake_llm(workdir, agent, prompt,
|
|
|
518
520
|
if new_sid:
|
|
519
521
|
save_session_id(workdir, agent, new_sid)
|
|
520
522
|
if r.returncode != 0:
|
|
523
|
+
# 通用诊断(**只落盘、不解析、不作分支**):rc≠0 时 stderr 此前只被读来
|
|
524
|
+
# 判断“是不是 session 失效”,其余丢弃——诊断信息本可留在 loop 日志。
|
|
525
|
+
# 截断 200 字符(先例 meeting_fs 的 [:200])并**显式标注截断口径**。
|
|
526
|
+
# 注意:这**不**解决“MCP server 静默失效”——那种情形 rc=0、stderr 为空
|
|
527
|
+
# (上游 notify 在非交互模式是 no-op),见 design.md 决策 20。
|
|
528
|
+
err_txt = (r.stderr or "").strip().replace("\n", " ⏎ ")
|
|
529
|
+
if err_txt:
|
|
530
|
+
log(agent, f"pi stderr(截断 200 字符): {err_txt[:200]}")
|
|
521
531
|
# 常见可重试失败:session 文件损坏/不存在。pi 对 --session-id
|
|
522
532
|
# 通常自动创建;保留 stderr 日志便于诊断。明确 "No session
|
|
523
533
|
# found" 则清空 status 后下轮新建。
|
package/observability.py
CHANGED
|
@@ -679,9 +679,21 @@ def _report_extension_line(base, out):
|
|
|
679
679
|
line = f"扩展策略:声明 {d} | 生效 {e}(strict={strict}"
|
|
680
680
|
line += f",降级:{reason.strip()}" if reason else ""
|
|
681
681
|
line += ")"
|
|
682
|
-
|
|
683
|
-
|
|
682
|
+
# 触发条件:声明≠生效 **或** 有降级原因——2026-09-30 起「生效」以本档
|
|
683
|
+
# 核心能力(MC 的 ctx_search)为准,而内置 MCP 入口恒在:可能出现
|
|
684
|
+
# 「声明=生效但仍降级」的组合,只看 d != e 会把降级漏掉(观测面纪律:
|
|
685
|
+
# 降级必须可见)。
|
|
686
|
+
if d != e or reason:
|
|
687
|
+
line += " ⚠ 降级(工具能力不全——见本行「降级」字段)"
|
|
684
688
|
out.append(line)
|
|
689
|
+
# 平台能力位(2026-09-30 自审结论):**本档不采集** MCP server 的连接状态——
|
|
690
|
+
# 非交互模式下失败只走 `ctx.ui.notify`,而它是 no-op;session 也不落工具集。
|
|
691
|
+
# 所以 rc=0 **不等于**工具可用;报告必须把「未观测」说出来(缺席≠0 的同一
|
|
692
|
+
# 纪律)。**判定用声明或生效任一命中**:内置 MCP 与 MC 入门是两份入口,
|
|
693
|
+
# MC 降级时内置那份仍在(生效=none ≠ 没有 MCP 工具)。none 档不写此行。
|
|
694
|
+
if d in ("mc-tools", "all") or e in ("mc-tools", "all"):
|
|
695
|
+
out.append("平台能力:未观测(MCP server 连接状态不采集——rc=0 ≠ 工具"
|
|
696
|
+
"可用;见 docs/design.md 决策 20)")
|
|
685
697
|
else:
|
|
686
698
|
out.append(f"扩展策略:声明 {declared} | 生效 n/a(日志中无登记行)")
|
|
687
699
|
|
package/package.json
CHANGED
package/start_discussion.py
CHANGED
|
@@ -626,8 +626,8 @@ def main():
|
|
|
626
626
|
choices=list(meeting_fs.EXTENSION_POLICIES),
|
|
627
627
|
default=meeting_fs.DEFAULT_EXTENSION_POLICY,
|
|
628
628
|
help="agents 的扩展策略:mc-tools=默认,只要 MC 的只读检索工具 "
|
|
629
|
-
"ctx_search + MCP
|
|
630
|
-
"all=走 pi 默认发现")
|
|
629
|
+
"ctx_search + pi 内置 MCP 工具(MC 那份缺了可见降级);"
|
|
630
|
+
"none=零扩展(零依赖);all=走 pi 默认发现")
|
|
631
631
|
parser.add_argument("--start", action="store_true", help="创建后启动讨论")
|
|
632
632
|
parser.add_argument("--skip-setup", action="store_true",
|
|
633
633
|
help="跳过环境生成,只启动已有环境(需 --dir)")
|