前端事实边界浏览器保存的是服务端事实的版本化投影和用户本地交互状态。任何乐观变化都必须可撤销、可对账,任何高权限动作都必须回到服务端授权。

前端拥有交互状态,不拥有后端事实

Run、Step、Tool、Approval 与 Artifact 的事实来自事件和快照;展开、选中、草稿、滚动位置和本地筛选属于前端。两类状态分仓存放并使用不同命名,避免把“用户点击取消”直接写成后端 cancelled。乐观状态带 command ID 与 base version,服务端拒绝或 Snapshot 到达时可对账。

路由使用稳定 Run ID,例如 /runs/:runId。刷新后先拉 Snapshot,再从游标恢复事件;多标签页通过 BroadcastChannel 或服务端订阅共享事实通知,但每个标签仍独立维护局部 UI。标签页不能互相转发高权限 Token 或把某个页面的乐观状态当服务端事实。

按领域能力拆 Vue 模块

run负责事件 Schema、Reducer、连接和命令;conversation负责消息、输入与引用;tools负责调用、审批和结果;artifacts负责工作区、版本与 Diff;shared只放跨域协议基础、无障碍组件和 Telemetry。组件、测试与样式跟随领域就近放置。

不要按“pages、components、utils”把同一能力拆散,也不要建立知道所有领域的 useAgent() 巨型 Composable。跨域协作通过明确命令和选择器,例如 ApprovalPanel 发出 approveRequested,由 tools domain 组装协议命令。

领域模块的责任与禁止项
模块负责不负责
run协议、投影、连接、恢复具体审批视觉
toolsTool 状态、预览、决策命令服务端授权结论
artifacts版本、Diff、验证展示直接写仓库/对象存储
shared稳定基础组件和 Telemetry业务状态与万能 helper

Composable、Store 与组件的边界

协议 Reducer 是无 Vue 依赖的纯 TypeScript 函数。连接生命周期放 useRunStream Composable,并在卸载时释放 AbortController、计时器和监听;跨路由 Run 投影进入 Store。SFC 使用 <template>、类型化 props/emits 和局部状态表达 UI,不直接触碰全局 Runtime。

<script setup lang="ts">
const props = defineProps<{ action: ApprovalView }>()
const emit = defineEmits<{
  approve: [id: string, version: number]
  reject: [id: string, reason: string]
}>()
</script>

<template>
  <section :aria-labelledby="'approval-' + action.id">
    <h3 :id="'approval-' + action.id">{{ action.summary }}</h3>
    <button @click="emit('approve', action.id, action.version)">批准本次动作</button>
  </section>
</template>

Vue 官方 TypeScript 与 Composition API 文档推荐在 Vue 3 SFC 中结合 <script setup>;这提供类型化 props/emits 和逻辑复用,但不是把所有逻辑抽成 Composable 的理由。只被一个视图使用的状态和交互保留在该 SFC。

流式连接的完整生命周期

连接前取得当前用户的短期会话和恢复游标;事件先做 Schema/版本校验,进入队列后按帧或时间窗批量归并,再单次更新 Store。重连采用带抖动退避并尊重服务端 Retry-After,页面卸载和切换 Run 时 Abort 旧连接,防止旧事件写入新视图。

Snapshot 包含 server version 和续传游标。客户端暂停事件提交,替换事实投影,再重放 Snapshot 之后已确认的事件;无法证明连续时清空本地事件缓存并重新订阅。鉴权失败、游标过期和普通网络故障是不同恢复路径,不能都无限重连。

Vue 前端运行链:副作用与纯状态转换分离
Composable鉴权、连接、重连、取消和清理。
Schema 队列校验、批量、游标和诊断。
Reducer/Store纯投影、选择器和对账。
SFC状态呈现、输入和语义事件。

关键组件按状态而非页面堆叠

RunStatus、MessageItem、ToolCall、ApprovalPanel、ArtifactWorkspace、EvidenceList 和 ErrorRecovery 分别消费领域 View Model。每个组件覆盖空、加载、部分、失败、过期和无权限,而不是只对成功截图优化。错误组件提供下一可行动步骤,并区分“本地显示失败”和“服务端事实未知”。

组件契约同时描述键盘顺序、焦点变化、live region、响应式和内容上限。新流式内容不抢焦点;审批打开时焦点进入语义标题或首个控件,关闭后回到触发点;宽表格局部滚动。视觉状态用文字和图形共同表达,不只靠颜色或动画。

浏览器端不持有高权限能力

所有 Tool 执行、租户隔离和资源授权由服务端完成。浏览器只发送带 CSRF 防护、幂等键、base state version 和行动 ID 的命令。按钮隐藏是体验,不是授权;攻击者可直接构造请求,服务端必须重新验证身份、对象与参数。

Markdown、HTML、Artifact、URL 与文件名均视为不可信数据,使用允许列表净化并禁用任意脚本/事件属性。前端错误与遥测不记录 Token、完整 Prompt、敏感 Tool Result 或用户媒体;Trace 用安全 ID 关联,详细内容留在有访问控制的后端。

用录制协议驱动稳定测试

单元层直接测试 Reducer、格式化、权限显示和命令构造;组件层为每种 Tool、Approval 与 Artifact 状态挂载 SFC,断言语义 DOM 和交互事件;集成层用 Fake Stream 注入重复、乱序、断线、Snapshot 与恢复;E2E 覆盖发起、审批、取消、刷新恢复和交付。

Vue 官方测试指南把 unit、component 和 end-to-end 区分为不同故障保护层。Agent 应用还需要协议夹具版本化:服务端 Schema 升级时旧夹具仍验证降级,未知事件不崩溃,回放后投影等价。时间、随机退避和 animation frame 使用可控时钟,避免概率 UI 产生脆弱测试。

前端也要可观测

核心指标是首个可见响应、事件到渲染延迟、交互到命令确认、重连成功与长任务内存。长对话做消息/DOM 虚拟化,Markdown 解析在批量边界执行,大 Artifact 延迟加载;优化前先用浏览器 Performance 与内存记录定位,不能只凭 Token 速率推断 UI 性能。

前端 Span 携带 W3C Trace Context 或受控 correlation ID,与服务端 Run/Tool Trace 关联;记录 Schema 拒绝、序列缺口、快照对账、重连、审批过期和用户放弃。不得把埋点成功率当任务成功,最终还需业务后置条件与可用性证据。

主动提取:为什么组件不能直接监听 SSE 并修改自己的状态?
协议判断会散落,重复/乱序/恢复产生多个解释器,无法做确定性回放。应由连接层接收、Reducer 投影,组件只消费 View Model。
故障判断:浏览器按钮已隐藏,是否足以阻止无权限 Tool?
不够。隐藏只改善体验,攻击者可构造请求;服务端仍须按主体、资源、参数和版本授权。

资料与事实边界

以下以官方文档、标准、论文和原始工程资料为主。厂商 API、价格、限额与模型能力会变化,落地时仍需按目标版本做能力探测和回归。

  1. Vue:TypeScript with Composition API — SFC、script setup、类型化 props 与 emits
  2. Vue:Composables — 可复用有状态逻辑与生命周期清理边界
  3. Vue:Testing — unit、component、E2E 与 Composable 测试建议
  4. W3C Trace Context — 浏览器与服务端分布式 Trace 传播标准
  5. OWASP Cross Site Scripting Prevention Cheat Sheet — 不可信 HTML、属性、URL 和输出编码边界