ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ClawX ACP 会话计划指示器:基于 update_plan 工具调用的纯渲染层实现与数据校验

ClawX ACP 会话计划指示器:基于 update_plan 工具调用的纯渲染层实现与数据校验 人工智能AI 应用桌面应用交互助手【免费下载链接】ClawXClawX is a desktop app that provides a graphical interface for OpenClaw AI agents. It turns CLI-based AI orchestration into a desktop experience without using the terminal. China website is https://clawx.com.cn.项目地址https://gitcode.com/gh_mirrors/cl/ClawX点击查看免费下载导读ClawX 桌面端为 OpenClaw ACPAgent Client Protocol会话提供了一个特殊的 Composer 计划指示器当 Agent 通过结构化update_plan工具调用更新当前任务计划时渲染器会从活动 ACP 时间线中提取最新有效的计划数据以只读、默认折叠的进度胶囊形式展示在聊天输入区上方用户无需进入终端即可实时掌握 Agent 正在执行的任务清单。本文围绕harness/specs/tasks/acp-session-plan-indicator.md这一 Harness 任务规格展开完整讲解该特性的数据来源、结构校验规则、状态选择算法、UI 交互与无障碍细节并结合仓库源码current-plan.ts、AcpSessionPlan.tsx、ChatInput.tsx与测试用例深入剖析其实现原理与边界行为。功能概述与设计边界核心目标该任务的目标非常明确把当前 ACP 会话中最新一次可回放的update_plan投射到活动聊天输入区且不引入任何额外持久化。它属于acp-chat-experience场景下的 UI 特性taskType:ui-feature从任务规格中的intent可以看出这是一种纯投射projection设计数据源是 ACP 会话自身的回放时间线timeline而不是单独存储的计划文件指示器只做展示不做任何增删改查会话切换、页面重载、应用重启后只有当 ACP 回放再次提供该会话的结构化update_plan输入时计划才会被恢复。Scope纯渲染层实现任务规格在Scope一节明确划定了边界This task defines a Renderer-only ACP timeline projection. It does not change Main-owned ACP transport, history replay, or session routing.也就是说该特性不触碰 Electron 主进程Main拥有的 ACP 传输、历史回放与会话路由。持久的 ACP 回放与权威边界在 harness/reference/acp-chat.md 中单独阐述指示器只是渲染器侧对已有时间线数据的一次只读解读。Out of Scope什么不该做规格明确列出了三个绝不越界的行为实现时严禁触碰禁止从工具标题、工具输出或助手文本中推断计划步骤—— 计划内容只能来自经过校验的结构化ToolCallItem.input禁止在 ACP 回放不再提供有效结构化输入时保留计划—— 计划指示器必须隐藏而非回忆禁止编辑、完成、删除或以任何方式变更 OpenClaw 的计划步骤—— 指示器是纯只读的。这三点约束直接体现在 acp-chat-state-and-history.md 规则的第 35 段作为全局权威规则之一被requiredRules引用。数据来源ACP 时间线与 update_plan 工具调用时间线快照TimelineSnapshot计划投影的输入是AcpTimelineSnapshot它来自渲染器对 ACP 事件的语义归约semantic reduction。从源码可以确认该快照至少包含两个关键字段itemOrder按时间顺序排列的项目 ID 列表itemsById以项目 ID 为键的对象映射。getCurrentAcpPlan正是依靠这两个字段从时间线尾部向前遍历工具调用项目来寻找最新的有效计划。识别 update_plan 调用在 current-plan.ts 中识别逻辑通过工具标题完成function isUpdatePlanTitle(title: string): boolean { const separatorIndex title.indexOf(:); return separatorIndex 0 title.slice(0, separatorIndex) update_plan; }即工具标题形如update_plan: ...冒号前部分严格等于update_plan即视为计划更新调用。但请注意标题只用于认出这是一个计划调用绝不用于提取计划内容——计划条目只允许来自校验过的结构化input数据。这一点在测试中也有体现planCall(other-tool, initialPlan, completed, read_file: package.json)不会被识别为计划调用返回null。计划数据的结构校验Validation输入结构要求projectPlan函数current-plan.ts是整条链路的核心校验器。它要求input满足以下结构{ plan: [ { step: Inspect the session, status: completed }, { step: Project the current plan, status: in_progress }, { step: Render the indicator, status: pending } ] }具体校验规则如下校验项规则违反时的处理input顶层类型必须是对象且非数组返回nullplan字段必须是数组且非空返回null每个条目必须是对象且非数组返回nullstep字段必须是非空字符串trim后长度 0返回nullstatus字段必须属于pending/in_progress/completed之一返回nullin_progress条目数至多 1 个返回nullisPlanStatus类型守卫current-plan.ts精确限制了三种合法状态function isPlanStatus(value: unknown): value is AcpCurrentPlanStatus { return value pending || value in_progress || value completed; }校验通过后投影结果AcpCurrentPlan的结构为type AcpCurrentPlan { steps: AcpCurrentPlanStep[]; // 有序步骤含 step 文本与 status completedCount: number; // 已完成数 totalCount: number; // 总步骤数 };测试用例对校验的完整覆盖acp-current-plan.test.ts 用it.each覆盖了全部无效输入分支missing input缺 inputan empty plan空数组a non-object entry条目非对象a blank step空白步骤文本an unknown status未知状态如blockedmultiple in-progress steps多个进行中步骤所有这些情况下较新的候选计划会被跳过回退到更早的有效计划。最新有效计划的选取与失败回退算法getCurrentAcpPlancurrent-plan.ts实现了规格中要求的最新优先 失败回退策略export function getCurrentAcpPlan(snapshot: AcpTimelineSnapshot): AcpCurrentPlan | null { for (let index snapshot.itemOrder.length - 1; index 0; index - 1) { const item snapshot.itemsById[snapshot.itemOrder[index]]; if (!item || item.kind ! tool-call || item.status failed || !isUpdatePlanTitle(item.title)) { continue; } const plan projectPlan(item.input); if (plan) return plan; } return null; }算法行为从尾部向前遍历最新的工具调用优先被检查三重过滤跳过非tool-call项目、failed状态的项目、非update_plan标题的项目即时生效状态为running/in_progress的运行中计划调用只要结构化输入有效会立即显示测试shows a valid running plan update immediately验证了这一点失败回退当更新的update_plan调用状态为failed或输入校验失败时继续向前寻找上一个有效计划测试falls back to the prior valid plan after a newer update fails与skips a newer candidate with %s验证了这一点无计划返回 null整个时间线中不存在任何有效计划时返回null此时输入区不显示指示器。为什么是回退而不是报错从产品语义看这一设计保证了用户体验的连续性Agent 某次计划更新因失败或格式异常未能落地时用户仍能看到它上一次明确承诺的计划而不是看到空白。同时规格限定回退只回退到更早的有效结构化输入绝不从文本、输出或全局缓存重建数据。会话隔离计划恢复严格绑定活动会话渲染器侧的组合逻辑计划投影在 src/pages/Chat/index.tsx 中完成使用useMemo保证只读派生const currentPlan useMemo( () visibleAcpTimeline.sessionId currentSessionKey ? getCurrentAcpPlan(visibleAcpTimeline) : null, [currentSessionKey, visibleAcpTimeline], );关键点在于只有当可见时间线的sessionId与当前选中会话键一致时才会计算计划。这保证了切换会话时计划指示器立即与新的活动会话绑定时间线变化如新的回放数据到达时计划随之重新计算不同会话的计划互不串扰。E2E 对会话隔离的验证tests/e2e/chat-acp-inline-timeline.spec.ts 中的测试session plan replay isolates session A through an A-to-B-to-A switch构造了 A、B 两个会话各自独立的回放数据会话 A 回放session-a-planTodo items: 1 / 2会话 B 回放session-b-planTodo items: 0 / 1从 A 切到 B指示器文本变为Todo items: 0 / 1展开后显示Keep session B separate从 B 切回 A指示器恢复为Todo items: 1 / 2且重新挂载后默认折叠aria-expandedfalse面板数量为 0展开后显示的是 A 会话的最新回放内容Load fresh session A replay等。测试还通过记录 ACP 加载会话键的顺序[MAIN_SESSION_KEY, sessionBKey, MAIN_SESSION_KEY]确认每次切换都触发了正确的会话加载而非使用任何内存残留。重载后的恢复路径测试session plan replay restores a collapsed plan after renderer reload验证了规格中的另一条行为渲染器page.reload()后指示器从 ACP 的session/load回放rawInput.plan中重新恢复计划且恢复后默认折叠。也就是说计划的持久化完全依赖 ACP 回放本身渲染器内存不跨重载存活。UI 实现AcpSessionPlan 组件的只读指示器组件职责AcpSessionPlan.tsx 是实际的 UI 组件接收plan、sessionKey、isExpanded与onExpandedChange四个属性。它有以下几个关键设计无计划时不渲染if (!plan) return null;保证空状态零 DOM 残留默认折叠组件内部useState初始化expanded: false每次挂载都从折叠开始展开状态按计划身份 会话键记忆getPlanIdentity将[completedCount, totalCount, steps]序列化为 JSON 字符串作为身份指纹。只有当身份未变且会话键未变时用户展开的面板才会保持打开一旦计划内容更新或切换会话面板自动收起对应单元测试closes an expanded panel when its plan or session identity changes。进度胶囊Collapsed 状态折叠时组件渲染一个胶囊形按钮左侧是ListChecks图标lucide-react中间是本地化进度文本Todo items: {{completed}} / {{total}}使用tabular-nums等宽数字全部完成时呈现绿色主题样式border-green-500/20 bg-green-500/10 text-green-700按钮带完整的aria-expanded、aria-controls、aria-label语义支持键盘焦点focus-visible:ring-2。展开面板Expanded 状态展开后组件渲染一个上浮面板absolute bottom-full right-0悬浮于输入区上方面板内是ol有序列表每个步骤为li三种状态对应三种图标completed→CheckCircle2绿色文本、in_progress→CircleEllipsis、pending→Circle步骤文本使用break-words允许长文本换行而非截断单元测试专门断言了break-words类面板内没有任何 button、input、checkbox 等交互控件单元测试断言panel.querySelectorAll(button, input[typecheckbox])长度为 0从结构上保证只读max-h-48 overflow-y-auto限制面板高度步骤多时可滚动步骤文本不附加 Running/Pending/Completed 文字标签完全靠图标与颜色表达状态。在 ChatInput 中的挂载与联动状态行集成ChatInput.tsx 将指示器放进 Composer 上方的工作状态行const showStatusRow showWorkingIndicator || showSubagentControl || currentPlan ! null;状态行同时容纳思考中/子代理工作指示、子代理会话控件AcpSubagentSessions与计划指示器AcpSessionPlan。currentPlan ! null是显示状态行的充分条件之一与发送中/子代理忙碌状态互不干扰。面板互斥与受控展开ChatInput内部维护expandedComposerPanel状态类型为{ sessionKey: string; panel: subagents | plan | null }用于控制子代理面板与计划面板的互斥展开const composerSessionKey draftKey ?? ; const activeComposerPanel expandedComposerPanel.sessionKey composerSessionKey ? expandedComposerPanel.panel : null;AcpSessionPlan的isExpanded由activeComposerPanel plan驱动onExpandedChange回调负责写回状态行。这里同样以composerSessionKey即草稿键/会话键作为展开状态的会话边界——切换会话后旧会话的展开状态不会残留在新会话上。E2E 对布局位置的断言E2E 测试对指示器的实际布局提出了硬性要求指示器 x 坐标 宽度应大于输入框右边界 - 100贴近输入框右缘指示器的 y 坐标小于输入框的 y 坐标位于输入框上方展开面板底部不超过指示器按钮底部panelBox.y panelBox.height toggleBox.y且不遮挡输入框 expandedComposerBox.y。这些断言确保指示器作为输入区上方的一行附属状态存在不会遮挡或干扰正文输入。国际化i18n与多语言支持语言包结构规格要求新增 UI 文案必须同步覆盖英文、中文、日文、俄文四种语言。实际语言包位于 shared/i18n/locales 下的en/chat.json、zh/chat.json、ja/chat.json、ru/chat.json四个文件在acp.sessionPlan键下保持一致的四条文案keyenzhjaruprogressTodo items: {{completed}} / {{total}}待办项 {{completed}} / {{total}}タスク {{completed}} / {{total}}Задачи: {{completed}} / {{total}}expandExpand plan展开计划計画を展開Развернуть планcollapseCollapse plan折叠计划計画を折りたたむСвернуть планtasksPlan tasks计划任务計画のタスクЗадачи плана组件通过useTranslation(chat)读取这些文案进度文本用{{completed}}/{{total}}插值。仓库另有i18n-locale-parity.test.ts单元测试保障各语言包键的一致性。相关 Harness 规格与验证方式关联文档场景规格acp-chat-experience.md 将该任务列为 ACP 聊天体验场景的组成部分权威规则acp-chat-state-and-history.md 第 35 段专门规范了计划指示器的数据边界只能从结构化输入派生、只能选最新非失败有效计划、会话作用域内回退、无持久化/全局缓存参考文档harness/reference/acp-chat.md 说明持久的 ACP 回放与权威边界。规格自带的验证命令任务规格requiredTests提供了完整的验证命令集可用于复现与回归# 单元测试投影算法 pnpm exec vitest run tests/unit/acp-current-plan.test.ts # 单元测试组件渲染、键盘交互、只读性 pnpm exec vitest run tests/unit/acp-session-plan.test.tsx tests/unit/chat-input.test.tsx tests/unit/chat-acp-inline-timeline.test.tsx # Electron E2E实时计划显示、会话切换、重载回放恢复 pnpm exec playwright test tests/e2e/chat-acp-inline-timeline.spec.ts -g session plan|plan indicator # Harness 规格校验与干跑 pnpm harness validate --spec harness/specs/tasks/acp-session-plan-indicator.md pnpm harness run --spec harness/specs/tasks/acp-session-plan-indicator.md --dry-run这些命令覆盖了从纯函数到 UI 组件再到真实 Electron 应用的全链路验证。验收标准与实现要点总结任务规格acceptance一节给出了可逐条核对的验收标准结合源码可以归纳为实现要点验收条目实现位置校验结构化ToolCallItem.input为非空有序计划条目含非空 step、合法状态、至多一个 in_progresscurrent-plan.ts 的projectPlan选取最新非失败有效 update_plan新计划失败时回退到前一个有效计划current-plan.ts 的getCurrentAcpPlan计划恢复限定于活动 ACP 会话的回放时间线时间线变化时重算src/pages/Chat/index.tsx 的useMemo组合不新增持久化、缓存、传输、后端端点、IPC 通道或计划变更控制全链路仅使用useMemo派生与内存useState无任何写路径指示器只读、默认折叠、键盘可达、展开仅显示规范化计划详情AcpSessionPlan.tsx 的按钮语义与面板结构新 UI 文案覆盖英/中/日/俄四种语言shared/i18n/locales 四份 chat.json 的acp.sessionPlanE2E 覆盖实时显示、会话切换、重载后从rawInput.plan恢复tests/e2e/chat-acp-inline-timeline.spec.ts 三个专项测试总结ClawX 的 ACP 会话计划指示器是一个典型的纯渲染层投影范例它不拥有数据、不写入数据只对 ACP 回放时间线中经过严格结构校验的update_plan工具输入做一次只读投影并以无障碍友好的进度胶囊形式呈现在输入区上方。其设计精髓在于三点校验严格任何一项不合规即放弃该候选计划、回退保守新计划失败只回退到上一个有效结构化输入、边界清晰会话隔离、无持久化、无计划变更能力。理解这套实现对于在 ClawX 中扩展其他基于 ACP 时间线的只读派生 UI如文件活动、子代理会话状态同样具有直接的参考价值。赞分享人工智能AI 应用桌面应用交互助手【免费下载链接】ClawXClawX is a desktop app that provides a graphical interface for OpenClaw AI agents. It turns CLI-based AI orchestration into a desktop experience without using the terminal. China website is https://clawx.com.cn.项目地址https://gitcode.com/gh_mirrors/cl/ClawX点击查看免费下载相关推荐three.js XRManager 完全指南基于 WebXR Device API 的会话管理与渲染层架构three.js XRManager 完全指南基于 WebXR Device API 的会话管理与渲染层架构 XRManager 是 three.js 通用渲前端3D渲染图形学Android NDK 之 Hello Vulkan基于 GameActivity 的三角形渲染、校验层与预旋转实战Android NDK 之 Hello Vulkan基于 GameActivity 的三角形渲染、校验层与预旋转实战 本指南以 ndk samples 仓库中示例工程移动开发微信聊天记录导出WeChatMsg 快速导出 HTML/Word/CSV还能生成聊天年度报告微信聊天记录导出WeChatMsg 快速导出 HTML/Word/CSV还能生成聊天年度报告 WeChatMsg 是一款开源的微信聊天记录导出工具它读取微上一篇什么是SOCD一文看懂格斗游戏神器Hitboxersocd键位重映射与方向冲突终极解决方案下一篇Web Starter Kit与Riot.js集成轻量级组件化多设备开发创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表