Skip to content

Commit 0061ee2

Browse files
committed
refactor(acp): rename session plan updates to agenda
1 parent c2a2897 commit 0061ee2

39 files changed

Lines changed: 873 additions & 212 deletions

File tree

guidelines/Domain.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ ACP 协议规定 tool call 字段除 `toolCallId`/`title` 外全部可选、且
6060

6161
- MUST: `acp-mapper` 保持为无状态纯函数,不跨事件追踪 `toolCallId` 状态。需要跨事件的补偿(如 gemini 跳过 `tool_call` start 的孤儿 `tool_call_update`)由下游两个 assembler(`message-assembler.ts``useUIMessageAssembler.ts`)以 lazy-upsert 方式建卡,不在 mapper 引入状态。
6262
- MUST: 对 `tool_call``tool_call_update` 使用同一套字段位置无关的提取逻辑(`extractToolInput`/`extractTextContent`/`extractDiffs`/`extractLocations`)——`input`/`content`/`diff`/`locations` 无论出现在 start 还是 update 都按相同规则提取,使未观测过的 agent 也能正确兼容。
63+
- MUST: ACP 协议边界的 `sessionUpdate === "plan"` 在 FylloCode 内部映射为 `agenda_update`,共享类型使用 `AgendaEntry`,渲染层会话内存字段使用 `Session.agentAgenda`。不得把该运行时行动清单与 FylloCode 自有 `create-plan` / `plan.create` / lineage plan 链路混用。
6364
- MUST: 把针对特定 agent 怪癖的纠偏隔离为独立、带注释标注的小函数,不混入通用提取逻辑。当前两处:`resolveStatus`(qodercli `completed + rawOutput.error` 降级为 `failed`)、`normalizeMcpTool`(仅依据 codex 形态 `rawInput.{server,tool}` 归一 MCP 标识,其他形态 fallback 原 title)。新增此类补丁必须在函数上方注释标注「agent 怪癖补丁」及其适用范围与 fallback 行为。
6465
- SHOULD: `references/acp/tool-call-trace/` 是各 agent 真实 trace 与差异矩阵的权威参考;新增 agent 兼容前先核对 trace 事实,修正差异矩阵中过时条目后再实现。
6566

guidelines/IPC.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ keywords: [ipc, electron, preload, channels, contracts]
105105
- `chat:setConfigOption`:入参 `{ projectId, sessionId, configId, type: "select" | "boolean", value: string | boolean }`,使用 zod discriminated union 校验 `value` 类型与 `type` 字面量匹配(`select` 仅接收非空 `string``boolean` 仅接收 `boolean`)。返回 `IpcResponse<{ configOptions: AcpSessionConfigOption[] }>`,主进程在调用 `connection.setSessionConfigOption` 成功后将全集 `configOptions` 写回 `SessionMeta.config_options`,再原样返回给 renderer。错误码集合:`VALIDATION_ERROR`(入参或 session 缺 `acpSessionId`)、`CONFIG_OPTION_INVALID_VALUE``value` 不在缓存的 select schema 中)、`CONFIG_OPTION_NOT_SUPPORTED`(agent 未实现 `session/set_config_option`,识别 ACP `-32601` 与语义等价错误)、`ACP_NOT_READY``ACP_ERROR`
106106
- `MessageChunkData` 新增 `{ kind: "config_options_update"; options: AcpSessionConfigOption[] }` 分支,语义为「全集替换」:覆盖 `Session.configOptions` 而非 patch 单项;与 `available_commands_update` / `session_info_update` 同位治理(`acp-session-recovery#shouldSuppressDuringReplay` 白名单),`loadSession` replay 期间也不被抑制。
107107
- `AcpSessionConfigOption` 类型由 `src/shared/types/acp-config.ts` 导出,**不依赖** `@agentclientprotocol/sdk` 的 SDK 类型;主进程 `acp-mapper.normalizeAcpSessionConfigOptions` 负责剥除 `_meta`、把 `null` category/description 归一为 `undefined`,并保留 `select.options` 的平铺与分组两种形态。
108-
- `proposal:stageStream` / `proposal:archive` 不向 `driveAcpStream``onControlEvent`,因此所有控制事件(`config_options_update` / `available_commands_update` / `usage_update` / `plan_update` / `session_info_update`)一律被忽略:不发 sink chunk、不写磁盘(内容增量仍由 driver 统一经 `MessageAssembler` 落盘)。仅 `chat:stream:message` 提供 `onControlEvent`,转发并持久化这些控制事件到 `SessionMeta`。注意 archive 与 apply 对齐后 `session_info_update` 也不再转发 title chunk。
108+
- `proposal:stageStream` / `proposal:archive` 不向 `driveAcpStream``onControlEvent`,因此所有控制事件(`config_options_update` / `available_commands_update` / `usage_update` / `agenda_update` / `session_info_update`)一律被忽略:不发 sink chunk、不写磁盘(内容增量仍由 driver 统一经 `MessageAssembler` 落盘)。仅 `chat:stream:message` 提供 `onControlEvent`,转发并持久化这些控制事件到 `SessionMeta`。注意 archive 与 apply 对齐后 `session_info_update` 也不再转发 title chunk。
109109

110110
## Verification
111111

guidelines/RendererProcess.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ keywords: [renderer, vue, pinia, routing, ui]
105105

106106
## Chat Session Event Rail
107107

108-
- `ChatSessionEventRail` 可以展示执行计划、proposal 入口和当前 Chat 主会话内未处理的 Fyllo action 提醒。pending Fyllo action 必须从 `activeSession.messages``activeSession.actionStates` 响应式派生;缺失 action state 表示待处理,`succeeded` / `failed` / `cancelled` 任一状态存在后不再作为 pending rail item 展示。
108+
- `ChatSessionEventRail` 可以展示行动清单、proposal 入口和当前 Chat 主会话内未处理的 Fyllo action 提醒。pending Fyllo action 必须从 `activeSession.messages``activeSession.actionStates` 响应式派生;缺失 action state 表示待处理,`succeeded` / `failed` / `cancelled` 任一状态存在后不再作为 pending rail item 展示。
109109
- Fyllo action rail item 只负责提醒与定位:组件只能 emit `locate-action`,由 `ChatContainer` 在消息滚动容器内查找 `data-fyllo-action-id` anchor 并滚动到原 action card。事件栏不得 import action dispatcher、task store、lineage API、`src/renderer/src/api/*``window.api`,也不得确认、取消、重试或持久化 action state。
110110

111111
## Plan Review Flow
@@ -119,7 +119,7 @@ keywords: [renderer, vue, pinia, routing, ui]
119119

120120
- `useChatStore` 必须按已建立的 `sessionId` 维护 chat stream run、status、cancel 和瞬时 error;组件消费的 `chatStatus``streamError``cancelFn` 只能从当前 `useSessionStore.activeSessionId` 对应 session 派生,草稿态或无运行态时回落为 `ready` / `null`
121121
- 选择 session 或进入草稿态只能清当前视图瞬时错误,不得取消、失效或清空其他 session 的后台 stream。只有显式 stop 可以取消当前选中 session 的 run,或取消仍处于首条消息 setup 阶段的 pending draft run。
122-
- 后台 session 的有效 stream 回调必须继续更新所属 session 的内存消息、标题、token usage、可用命令、配置选项、计划状态和 session 状态;切回已加载 session 时应显示这份内存最新状态,而不是依赖重新读磁盘修正丢失的 chunk。
122+
- 后台 session 的有效 stream 回调必须继续更新所属 session 的内存消息、标题、token usage、可用命令、配置选项、行动清单状态和 session 状态;切回已加载 session 时应显示这份内存最新状态,而不是依赖重新读磁盘修正丢失的 chunk。
123123

124124
## Verification
125125

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
schema: spec-driven
2+
created: 2026-07-01T00:00:00.000Z
3+
status: applying
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
## Context
2+
3+
当前调用链从主进程接收 ACP `session/update``sessionUpdate === "plan"` 开始:
4+
5+
```mermaid
6+
flowchart LR
7+
ACP["ACP session/update<br/>sessionUpdate = plan"] --> Mapper["acp-mapper<br/>plan_update"]
8+
Mapper --> Stream["StreamContentEvent / MessageChunkData<br/>PlanEntry[]"]
9+
Stream --> ChatStore["renderer chat store<br/>setSessionPlan"]
10+
ChatStore --> Session["Session.plan"]
11+
Session --> Rail["ChatPlanPanel<br/>执行计划"]
12+
```
13+
14+
问题不在上游 ACP 协议字段,而在 FylloCode 内部把这条运行时状态继续称为 plan。FylloCode 已经有正式 `create-plan` tool 和 `plan.create` action,二者是可审阅、可编辑、可批准、会进入 lineage 的轻量规划方案。ACP `plan` 则是 Agent 当前 turn 的全量运行时安排快照,不持久化、不进入 lineage、不由用户编辑。
15+
16+
## Goals / Non-Goals
17+
18+
**Goals:**
19+
20+
-`acp-mapper` 接收到 ACP `sessionUpdate === "plan"` 后,FylloCode 内部统一使用 Agenda 命名。
21+
- 让共享类型、事件判别值、store action、session 字段、composable 返回值、组件名、测试名和 UI 文案形成同一套词汇。
22+
- 明确残留边界:上游协议字面值 `sessionUpdate: "plan"` 可以保留,FylloCode 自有 plan 工具链必须保留。
23+
- 用搜索验收约束防止遗漏旧内部命名。
24+
25+
**Non-Goals:**
26+
27+
- 不修改 ACP SDK 或协议定义;上游仍叫 `plan`
28+
- 不修改 `create-plan` MCP tool、`plan.create` action、Plan Slideover、lineage plan、`PlanDocument`、session `plans/` 文件。
29+
- 不改变 Agenda 的运行时语义:仍全量替换、不持久化、不进入 message assembler、不进入 `session.messages`
30+
- 不迁移历史 archived OpenSpec change 文档;历史变更记录可以保留当时的 plan 术语。
31+
32+
## Decisions
33+
34+
### 决策 1:协议边界保留 `plan`,内部事件改为 `agenda_update`
35+
36+
`acp-mapper.mapSessionUpdate` 的 switch 仍保留 `case "plan"`,因为这是 ACP 协议输入。该分支之后产出的 `SessionEvent` 改为 `{ kind: "agenda_update"; entries: AgendaEntry[] }`。对应归一化函数改为 `normalizeAgendaEntries`,常量改为 `AGENDA_PRIORITIES` / `AGENDA_STATUSES`
37+
38+
备选:把 UI 叫 Agenda,但内部继续 `plan_update`。否决,因为这会继续让共享类型、测试和调用链保留冲突命名,不能满足“不要遗漏”的目标。
39+
40+
### 决策 2:共享类型使用 `AgendaEntry`,session 字段使用 `agentAgenda`
41+
42+
`AgendaEntry` 只描述 ACP Agent 推送的运行时条目:`content``priority``status``Session.plan` 改为 `Session.agentAgenda`,比 `agenda` 更明确所有权在 Agent,不是用户日程或 FylloCode 规划方案。
43+
44+
备选:使用 `TodoEntry` / `Session.todo`。否决,因为 Todo 的用户心智更接近可编辑、可勾选、长期追踪的待办事项,容易与 FylloCode task 混淆。
45+
46+
### 决策 3:组件和 UI 采用 `ChatAgentAgendaPanel` / “行动清单”
47+
48+
组件文件 `src/renderer/src/components/chat/event/ChatPlanPanel.vue` 改为 `ChatAgentAgendaPanel.vue`。事件栏内部 prop 从 `planEntries` 改为 `agentAgendaEntries`,面板标题从“执行计划”改为“行动清单”。
49+
50+
备选:UI 标题显示 “Agent Agenda”。否决,当前事件栏相关文案均使用简体中文,`chat-event-rail-panel-style` 已要求 panel 标题使用中文。
51+
52+
### 决策 4:搜索验收采用“禁止旧内部命名 + 明确允许残留”
53+
54+
实施完成后必须执行面向遗漏的搜索。以下旧内部命名在 `src/``test/``openspec/specs/``guidelines/` 中不应再出现:
55+
56+
- `PlanEntry`
57+
- `plan_update`
58+
- `ChatPlanPanel`
59+
- `setSessionPlan`
60+
- `Session.plan`
61+
- `activeSession.plan`
62+
- `session.plan`
63+
- `planEntries`
64+
- “执行计划”(仅指 ACP runtime agenda UI,不含历史 archive 文档)
65+
66+
允许残留:
67+
68+
- `src/main/services/chat/acp-mapper.ts` 中的 `case "plan"` / `sessionUpdate === "plan"` 协议边界。
69+
- ACP 引用资料或 archived OpenSpec change 中的历史术语。
70+
- FylloCode 自有 `create-plan``plan.create`、lineage plan、Plan Slideover、`PlanDocument`、session `plans/` 相关命名。
71+
72+
## Risks / Trade-offs
73+
74+
- [遗漏旧命名] → 在 tasks 中加入 `rg` 内容搜索与 `rg --files` 文件名搜索,且要求测试名同步改名。
75+
- [误改 FylloCode plan 工具链] → proposal、design、tasks 明确 Non-Goals 和允许残留;实施时不得重命名 lineage/plan-tool 相关 API。
76+
- [跨进程事件名变更导致编译但运行遗漏]`StreamContentEvent``MessageChunkData` 仍通过 TypeScript 联合类型做穷尽检查;renderer/main/tests 同步更新。
77+
- [spec capability 名仍含 `chat-plan-display`] → 本次通过 delta 修改该既有 capability 的要求内容,不在同一变更中重命名 OpenSpec capability 目录,避免把运行时调用链重命名与 OpenSpec 历史能力迁移混为一谈。
78+
79+
## Migration Plan
80+
81+
无数据迁移。Agenda 仍不落盘,旧 session meta 中也不存在 `plan` 字段需要迁移。实现回滚只需把内部命名和 UI 文案恢复为旧 plan 命名,不会留下持久化状态。
82+
83+
## Open Questions
84+
85+
无。
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
## Why
2+
3+
ACP 的 `session/update``plan` 表示 Agent 当前 turn 的运行时执行安排,而 FylloCode 已经引入了自己的 `create-plan` / `plan.create` / Plan Slideover 规划方案链路。两者同名但语义不同:ACP `plan` 不持久化、不可审批、只做过程可视化;FylloCode plan 是 session-scoped Markdown 文档,可审阅、编辑和批准。
4+
5+
本变更把 ACP `plan` 在 FylloCode 内部统一命名为 Agenda(UI 文案为“行动清单”),从主进程接收到 ACP `sessionUpdate === "plan"` 后的全部内部调用链中消除旧的 `plan` 命名残留,避免继续与正式规划方案概念混淆。
6+
7+
## What Changes
8+
9+
- **BREAKING**:跨进程流式事件分支从 `{ kind: "plan_update"; entries: PlanEntry[] }` 改为 `{ kind: "agenda_update"; entries: AgendaEntry[] }`
10+
- **BREAKING**:共享类型 `PlanEntry` 改名为 `AgendaEntry``Session.plan` 运行时字段改为 `Session.agentAgenda`
11+
- 主进程 `acp-mapper` 仍按上游协议接收 `sessionUpdate === "plan"`,但内部归一化函数、常量、`SessionEvent`、IPC 控制事件和测试全部使用 Agenda 命名。
12+
- 渲染进程 store、composable、props、变量、组件文件名和测试全部使用 Agenda 命名;`ChatPlanPanel.vue` 改为 `ChatAgentAgendaPanel.vue`,UI 标题改为“行动清单”。
13+
- `useUIMessageAssembler`、proposal apply/archive 控制事件忽略逻辑、chat session event rail 展示条件同步从 plan 命名改为 agenda 命名。
14+
- 当前规范与 guidelines 中描述 ACP 运行时 plan 展示/流转的条目同步改为 Agenda/行动清单。
15+
- 不改变 FylloCode 自有 `create-plan` tool、`plan.create` Fyllo action、lineage plan、Plan Slideover、`PlanDocument` 等正式规划方案链路。
16+
17+
## Capabilities
18+
19+
### New Capabilities
20+
21+
- 无。
22+
23+
### Modified Capabilities
24+
25+
- `acp-chat-backend`:ACP `sessionUpdate === "plan"` 仍作为协议输入,但 FylloCode 内部 `SessionEvent`、chat store 分发和会话运行时状态改为 Agenda 命名。
26+
- `ipc-streaming`:跨进程流式协议从 `plan_update` / `PlanEntry` 改为 `agenda_update` / `AgendaEntry`
27+
- `chat-plan-display`:当前 ACP 执行计划面板要求改为 Agent Agenda / 行动清单面板要求,组件、prop、session 字段和 UI 标题全部改名。
28+
- `chat-session-event-rail`:事件栏显示条件从 `activeSession.plan` 改为 `activeSession.agentAgenda`,并以行动清单事件卡片表述。
29+
- `chat-event-rail-panel-style`:事件栏 panel 样式规范中的 `ChatPlanPanel` / “执行计划”改为 `ChatAgentAgendaPanel` / “行动清单”。
30+
31+
## Impact
32+
33+
- 共享类型:`src/shared/types/chat.ts``src/shared/types/stream-event.ts``src/shared/types/ipc.ts` 间接受影响。
34+
- 主进程:`src/main/services/chat/acp-mapper.ts``src/main/ipc/chat.ts``src/main/domain/chat/acp-session-recovery.ts``src/main/services/chat/session-event-mapper.ts` 相关测试。
35+
- 渲染进程:`src/renderer/src/stores/chat.ts``src/renderer/src/stores/session.ts``src/renderer/src/composables/useChatEventRail.ts``src/renderer/src/composables/useUIMessageAssembler.ts``src/renderer/src/components/chat/event/EventRailContent.vue``ChatPlanPanel.vue` 组件重命名及相关测试。
36+
- 文档与规范:`openspec/specs/acp-chat-backend``ipc-streaming``chat-plan-display``chat-session-event-rail``chat-event-rail-panel-style` 的 delta,以及 `guidelines/IPC.md``guidelines/RendererProcess.md` 中 ACP 运行时 plan 相关描述。
37+
- 不影响 session meta 持久化格式;Agenda 仍是纯运行时内存态,不写入 session meta,不进入 `session.messages`

0 commit comments

Comments
 (0)