Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
d3e2cc1
fix: /model 切换后 banner 当前模型同步 | 决策: EngineSession 持有 checker/fastMode…
liuqihonggit Oct 20, 2026
0907107
docs: TASK057 补验收缺陷修复章节(双源消除+banner同步+QuerySync踩坑)
liuqihonggit Oct 20, 2026
895a676
feat: banner 供应商显示链(SessionInfo.Vendor 字段+BannerItem vendor 前缀渲染) | 决…
liuqihonggit Oct 20, 2026
6e42ace
feat: 子代理交互层 Step1 消息光标导航(MessageCursor 协调器 + 第四交互态 + 选中高亮 + 分组展开) | …
liuqihonggit Oct 20, 2026
e30d242
feat: 子代理交互层 Step2 每代理活动日志(最近3条环形缓冲+hidden丢弃计数,展开态逐行渲染) | 决策: 活动日志落 S…
liuqihonggit Oct 20, 2026
e16d290
docs: TASK055 子代理交互层 Step1+2 落地记录(消息光标导航+活动日志,四列验收表+决策记录)
liuqihonggit Oct 20, 2026
c9b8407
feat: /vendor 供应商模态选择器(CommandPickerEngine 泛化复用整条模态链) | 决策: ModelPick…
liuqihonggit Oct 20, 2026
d5d0b36
docs: TASK058 供应商选择器落地记录(基线 782+52 + selftest 双通过 + 踩坑)
liuqihonggit Oct 20, 2026
275a0e4
fix: TUI 横向线残影根因修复(引擎日志静默) | 决策: 根因是 CreateGuiSessionAsync 无 loggerPr…
liuqihonggit Oct 20, 2026
c2e4d13
feat: 引擎侧八插件加载后台化 McpPluginBootstrap(启动开销优化 A 项)+ AGENTS.md 临时日志归置规矩 …
liuqihonggit Oct 20, 2026
cf385b2
fix: AsyncHostWrapper 释放顺序保证——先收尾后台插件链再释放容器(退出竞态根治) | 决策: 竞态根因=容器本体先标…
liuqihonggit Oct 20, 2026
f78e267
feat: 配置错误友好呈现(修复闪退刷堆栈) | 决策: Main 顶层 catch ConfigurationException 统一…
liuqihonggit Oct 20, 2026
2c97cc2
docs: DSG041 引擎侧子代理事件源改造计划(三缺口③ spec) | 决策: 方案A主干=沿既有 SubAgentEventCh…
liuqihonggit Oct 20, 2026
a8be935
fix: 模型列表持久化改落 providers_llm/{vendor}/models.json(消除 settings.json 膨胀…
liuqihonggit Oct 20, 2026
b583e28
feat: DSG041 批1 子代理事件协议扩展(IEventConsumer 三回调 + SessionController 前置分流…
liuqihonggit Oct 20, 2026
18232ab
docs: DSG041 批1 收口更新(验收表 L3 ✅ + 实施记录含 AgentSuccess 语义取反坑)
liuqihonggit Oct 20, 2026
f2eb1f3
docs: DSG041 各批验收方式定义(单测红绿→E2E 复制先例→selftest 真实运行→用户真机收口,层级递进)
liuqihonggit Oct 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,12 @@ nuget包: 拒绝全部微软的AI包,因为大部分不支持NativeAOT。
要求分类,通常一个文件夹内直接暴露的文件少于40个,可以多层文件夹。
强迫症就是每个文件夹内文件和文件夹不应该同时存在,而是纯文件夹或者内纯文件,不得混淆。

### 临时日志归置(强制)

**AI 产生的编译日志/调试日志/探针输出**(`dotnet build` 重定向、测试输出捕获、计时探针、selftest 输出等)必须统一写入项目根 `.xxx/` 目录(如 `.xxx/logs/`),**禁止散落在仓库根目录**(污染 git status、混入误提交)。一次性分析产物用完即删;需跨步骤比对的留在 `.xxx/` 下命名(如 `.xxx/logs/build_red.txt`)。

**本地测试构建走 Debug 配置**,不做 AOT 发布(`PublishAot`/Release 全量编译耗时长,测试迭代优先速度);AOT 验证留给 CI/Release。

## 编译

### 16 功能域架构(强制编译顺序)
Expand Down
106 changes: 106 additions & 0 deletions docs/proposed/design/DSG041-subagent-event-source-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# DSG041 — 引擎侧子代理事件源改造计划(三缺口③)

> 状态:proposed(2026-10-21)
> 关联:TASK055 M7-7(子代理分组渲染已落地)、ADR 0143(协议层唯一数据源=IEventConsumer 事件模型)、ADR 0141(bridge 事件投影)
> 前置调研:2026-10-21 引擎侧执行链全量探索(探索代理报告,全部结论已核到文件:行号)

## 1. 问题

native_tui(TUI 宿主)子代理分组渲染全链已落地(SubagentGroupCoordinator + BridgeEvent 三事件 + ReplBridgeDriver 路由),但**真实引擎链路无子代理事件源**——目前仅 Demo 的 SimulatedEngine 模拟发射。真实链路表现:TUI 跑真实引擎时子代理调用不产生任何分组显示。

## 2. 调研结论(现状事实)

**核心发现:子代理事件源在引擎侧已存在且已发射**——`AgentStreamExecutionMiddleware`(前台)/ `AgentForkMiddleware`(fork/teammate)/ `ForkSubAgentManagerActor`(后台完成)持续向 `SubAgentEventChannel` Emit `AgentStarted` / 带 AgentId 的活动事件 / `AgentFinished`(ChatStreamEvent 层)。ChatService 管道路径(GUI/E2E 已验证:`SubAgentEventStreamE2ETests`)事件正常流动。

**native_tui 主代理路径丢事件的三个断点**:

| # | 断点 | 位置 | 性质 |
|---|------|------|------|
| ① | **作用域断点(根因)** | 主代理路径 `AgentBase.ExecuteStreamAsync → QueryEngine.QueryAsync` 不经 ChatService 管道;`ToolExecutionHandler.cs:80` 的 `EnterScope()` 不在链上 | `SubAgentEventChannel.Current`(AsyncLocal)为 null → 发射侧 `channel?.Emit` 全部静默跳过 |
| ② | **IEventConsumer 协议断点** | `IEventConsumer.cs`(8 方法)无子代理回调;`SessionController.ProcessEvent` 的 Switch 不传 onAgentStarted/onAgentFinished;`AgentStreamChunkAdapter` 不传播 chunk.AgentId | 事件模型(协议本体)缺子代理粒度 |
| ③ | **桥协议断点** | 引擎侧 `Cli.Bridge.BridgeEvent`(modules/apps/engine/bridge/BridgeProtocol.cs)无 subagent 三事件;`BridgeEventConsumer`/`InProcessEventConsumer` 无映射 | bridge 形态(JSON 投影)发不出来 |

**子代理执行链事实**:子代理是独立 `AgentBase` 实例(非工具内嵌套回合),内部走完整 QueryEngine LLM+工具循环;数据可得性全齐(SubagentId=chunk.AgentId、AgentType/Description=spawnOptions、ToolName/Summary=chunk 字段、TokenGain=Usage/ProgressTracker 差值、IsError=succeeded)。

## 3. 方案 A(主干推荐)— 沿既有 SubAgentEventChannel 机制打通

**原则:发射侧零新代码**(三个发射点已在 Emit),每层挂点均有先例可循。

### 四层改造(自底向上)

| 层 | 改造 | 落点 | 先例 |
|----|------|------|------|
| L1 通道接通 | `AgentBase.ExecuteStreamAsync` 持有 SubAgentEventChannel(挂 Options/字段),QueryAsync 循环内逐 chunk 排空通道转 AgentStreamChunk(新增 AgentStreamChunkType.AgentStarted/AgentFinished)随主流 yield;工具执行段进作用域 | `AgentBase.cs:340-469`(循环)+ `QueryEngine.cs`(`QueryMiddlewareContext` 挂通道 + `QueryCoreMiddleware` 工具段排空,或 AgentBase 层排空二选一——实施时按侵入度定) | `QueryLoopMiddleware.WaitForTaskWithDrainAsync`(50ms 轮询排空先例)+ `ToolExecutionHandler.cs:80` EnterScope |
| L2 chunk 传播 | `AgentStreamChunkAdapter.ToChatStreamEvent` 传播 `chunk.AgentId` + 新 chunk 类型映射 → `ChatStreamEvent.AgentStarted/AgentFinished`(工厂已存在 ChatStreamEvent.cs:166/177) | `AgentStreamChunkAdapter.cs:12-51` | 工厂现成 |
| L3 协议扩展 | `IEventConsumer` 新增 `OnSubagentStarted(subagentId, agentType, description)` / `OnSubagentToolUse(subagentId, toolName, summary, tokenGain)` / `OnSubagentFinished(subagentId, isError)`;`SessionController.ProcessEvent` Switch 传入三回调 | `IEventConsumer.cs` + `SessionController.cs:157-185` | **ADR 0143 不变式:先改事件模型(本体)再同步投影** |
| L4 双消费者映射 | 引擎侧 `Cli.Bridge.BridgeEvent` 增 subagent 三 record(字段对齐 native_tui BridgeEvent.cs:83/89/92,JSON 形状进 BridgeJsonContractTests 锁定);`BridgeEventConsumer` + `InProcessEventConsumer` 实现 L3 三方法 | `BridgeProtocol.cs:34-127` + 两消费者 | 两消费者语义对齐先例 |

### 消费端(native_tui,零改动验证面)

`ReplBridgeDriver.cs:110-131` 已路由三事件到 `SubagentGroupCoordinator`——L1-L4 打通后真实链路自动出分组显示,TUI 侧**零代码改动**(验收面)。

## 4. 方案 B(补充位)— IAgentService 轮询聚合

`AgentServiceImpl` 已有 `AgentCompleted` 事件 + `GetAgentProgressAsync`/`GetAgentActivityHistoryAsync` 轮询面。仅在宿主层起轮询器投影终态。

**定位:不作为主方案**——轮询延迟、拿不到精确 Started 时刻、tokenGain 非增量语义,且与方案 A 通道机制构成两套实现(违反唯一数据源规范)。fork 后台路径完成事件已在 `ForkSubAgentManagerActor.cs:467` 发射、通道接通即覆盖,无需 B。

## 5. 实施批次(渐进式,每批编译+测试+commit)

| 批次 | 内容 | 验证 |
|------|------|------|
| 批1 | L3 协议扩展:IEventConsumer 三方法(默认空实现——不破坏既有实现方)+ SessionController.ProcessEvent 三回调 | host.tests 新测试(fake consumer 断言回调) |
| 批2 | L1 通道接通:AgentBase/QueryEngine 段排空 + EnterScope + AgentStreamChunk 新类型 | agents 域单测 + E2E 复制 SubAgentEventStreamE2ETests 先例 |
| 批3 | L2 chunk 传播:AgentStreamChunkAdapter AgentId + 类型映射 | adapters 单测 |
| 批4 | L4 引擎侧 DTO + BridgeEventConsumer 映射 | BridgeJsonContractTests 锁形状 |
| 批5 | InProcessEventConsumer 映射 + native_tui 真实链路验收 | Host --selftest 扩子代理断言(真实 spawn,不调 LLM 可用 MockServer) |

**顺序理由**:L3 先行(事件模型=协议本体,ADR 0143 演进次序铁律"先本体后投影");L1 最重(执行链侵入)单独成批;批5 端到端收口。

## 6. 风险与决策点

1. **L1 挂点二选一**(批2 实施时定):`QueryMiddlewareContext`+QueryCoreMiddleware 排空(对齐 QueryLoopMiddleware 先例,但 QueryEngine 是 cognition 域共享组件侵入面大)vs AgentBase 循环内排空(侵入小但 fork 后台路径需另接)。倾向后者先行 + fork 路径复用通道。
2. **AsyncLocal 作用域传播**:EnterScope 须覆盖子代理内全部工具执行段(含嵌套 spawn);E2E 先例已验证前台/fork 两条路径。
3. **tokenGain 增量语义**:ProgressTracker.RecordTokenUsage 原子累加 vs 差值发射——对齐 native_tui 消费语义(TokenDelta 逐次累加)取差值。
4. **无消费方场景零影响**:通道无作用域静默丢弃的既有语义保留(CLI 纯文本等场景)。
5. **IEventConsumer 默认实现**:三方法走接口默认实现(DIM),既有 8+ 实现方零改动;注意 DIM 须经接口类型调用才可见(项目已有先例坑)。

## 7. 四列验收表

| 基建实现 | 消费点 | 已实现 | 已验收 |
|---------|--------|--------|--------|
| L3 IEventConsumer 三方法(DIM 默认空)+ SessionController RouteSubagentEvent 前置分流 | BridgeEventConsumer / InProcessEventConsumer(批4/批5 接映射) | ✅ 批1(b64c5d16d) | ✅ SessionControllerSubagentEventTests 5/5(Started/Finished/工具分流/普通零回归/混合流)+ host.tests 1371 + abs.tests 694 + engine/cli/Host 三链编译绿 |
| L1 SubAgentEventChannel 接通 QueryEngine 主代理路径 | AgentStreamExecutionMiddleware/ForkSubAgentManagerActor 发射侧(零改动) | ⏳ 批2 | ⏳ E2E 复制先例 + agents 单测 |
| L2 AgentStreamChunkAdapter AgentId 传播 + 类型映射 | SessionController chunk→ChatStreamEvent 翻译 | ⏳ 批3 | ⏳ adapters 单测 |
| L4 引擎侧 BridgeEvent 三 record | bridge_serve JSON 协议消费方 | ⏳ 批4 | ⏳ BridgeJsonContractTests |
| InProcessEventConsumer 映射 | EngineTurnClient → ReplBridgeDriver(既有路由零改动) | ⏳ 批5 | ⏳ Host --selftest 扩断言(ADR 0080 真实运行) |
| TUI 真实链路分组显示 | SubagentGroupCoordinator → 分组渲染(既有零改动) | ⏳ 批5 | ⏳ 用户真实终端(agents 场景真实引擎 spawn) |

## 8. 后续

- ~~批1(L3 协议扩展)可立即开工(无前置依赖)~~ **✅ 已完成(b64c5d16d,2026-10-21)**
- 批2(L1 通道接通):挂点二选一决策见风险1(倾向 AgentBase 循环内排空先行)
- 本 DSG 落地后 TASK055 三缺口③关闭

### 批1 实施记录(b64c5d16d)

- IEventConsumer 三方法走 **DIM 默认空实现**(既有 8+ 实现方零改动;默认实现须经接口类型调用的既有坑已规避)
- SessionController.ProcessEvent 前置分流 **RouteSubagentEvent**:AgentId 非 null 即子代理事件——AgentStarted→OnSubagentStarted(身份三元组)、AgentFinished→OnSubagentFinished(**AgentSuccess 成功语义取反为 isError**)、带 AgentId 工具事件→OnSubagentToolUse(tokenGain 恒 0 兜底,批2 填真实值);返回 true 跳过主 Switch
- 实施坑:AgentSuccess→isError 语义取反(首轮直传 false 导致 finished.IsError 断言反);mixed 流中子代理 Content 事件暂不投影返回 false 走主路径(消费面仅三回调)

## 9. 各批验收方式定义(2026-10-21 用户确认"先定义验收方式再逐个进行")

> 原则:TDD 红绿循环(每批先红测试后实现)+ 层级递进验收(单测 → E2E → 真实运行),全链最终以 ADR 0080 真实运行收口。

| 批次 | 单元级验收(红绿循环) | 集成级验收 | 通过判定 |
|------|----------------------|-----------|---------|
| 批2 L1 通道接通 | AgentBase/AgentServiceImpl 新单测:通道挂载后子代理事件随主流 yield(EnterScope 前后 Current 值断言 + 排空转 chunk 断言) | **E2E 复制 SubAgentEventStreamE2ETests 先例**(test/mock/mock_server.e2e.tests)——改造为 QueryEngine 主代理路径(AgentBase.ExecuteStreamAsync 直连,不经 ChatService 管道),断言 AgentStarted/带 AgentId 活动/AgentFinished 全序列可观察 | E2E 绿 + 既有全量回归零破坏(agents/llm/brain 域测试) |
| 批3 L2 chunk 传播 | AgentStreamChunkAdapter 单测:chunk.AgentId → ChatStreamEvent.AgentId、新 chunk 类型(AgentStarted/AgentFinished)→ 对应事件工厂映射 | SessionControllerSubagentEventTests 扩 chunk 路径用例(主代理路径 chunk→事件→回调全链) | adapter 输出事件 AgentId 非空 + 类型映射正确 |
| 批4 L4 引擎侧 DTO | BridgeEventConsumer 映射单测(三方法→三 record 字段对齐)+ **BridgeJsonContractTests 锁 JSON 形状**(subagent_started/subagent_tool_use/subagent_finished 序列化快照) | bridge_serve 形态冒烟(进程级 stdout JSON 投影) | JSON 形状锁定 + 双端字段一致(与 native_tui BridgeEvent 逐字段核对) |
| 批5 TUI 映射+真链 | InProcessEventConsumer 映射单测(NativeTui.Host.Tests:三方法→BridgeEvent 三事件投影) | **Host --selftest 扩子代理断言**(MockServer 假 LLM + 真实 spawn 路径,不依赖真实 API Key)→ 用户真实终端收口(agents 场景真实分组显示) | selftest 绿 + 用户真机确认分组渲染正确 |

<!-- 🤖 Auto Decision: 2026-10-21 -->
<!-- 决策: 方案A主干=沿既有 SubAgentEventChannel 机制打通(发射侧零新代码),四层改造 L3协议→L1通道→L2传播→L4投影(ADR 0143 先本体后投影次序);方案B轮询仅补充位不采纳 -->
<!-- 原因: 事件源已存在(AgentStreamExecutionMiddleware 等三点已 Emit),根因是主代理路径无通道作用域+协议三层缺子代理粒度;轮询方案延迟/非增量/双实现三违 -->
<!-- 替代方案: B=IAgentService 轮询聚合(延迟+两套实现否决);从零新造事件源(既有通道机制浪费否决) -->
<!-- 验证: 调研报告全部结论核到文件:行号;spec 待批1 开工后逐批验收 -->
Loading