2.9 KiB
2.9 KiB
事件映射
三层映射
Pi 发出的事件首先经过 PiEventAdapter 变成经过裁剪和脱敏的 HarnessEvent。Web 的 reducer 再把有序事件投影为 assistant-ui 消息、工具和运行状态。Pi 原始对象不会直接进入浏览器。
| Pi / Harness 来源 | HarnessEvent | Web / assistant-ui 状态 |
|---|---|---|
| Session 创建 | session.started |
建立初始 timeline |
| HTTP message input | user.message |
user message,附件仅为 IDs |
| Harness 开始 run | run.started |
isRunning=true,显示停止按钮 |
message_update.text_delta |
assistant.delta |
按稳定 messageId 追加文本 |
assistant message_end |
assistant.completed |
assistant message complete |
tool_execution_start |
tool.requested |
创建工具调用 part |
tool_execution_end 成功 |
tool.completed |
工具卡显示 result |
tool_execution_end 失败 |
tool.failed |
工具卡显示安全错误 |
| Broker 请求 | approval.required |
打开服务器审批 Dialog |
| Broker 决策 / 超时 / 取消 | approval.resolved |
关闭 pending 状态 |
| AttachmentStore 成功 | attachment.uploaded |
保存安全附件元数据 |
| AttachmentStore 拒绝 | attachment.rejected |
timeline / 错误状态 |
| ModelStore 配置 | model.configured |
更新安全模型摘要 |
| Harness 正常结束 | run.completed |
run 和消息标为 complete |
| Harness 取消 | run.cancelled |
消息 incomplete/cancelled |
| Harness 失败 | run.failed |
消息 incomplete/error |
| Session 删除 | session.ended |
连接回到 idle |
Skill 推导事件
skill.requested 和 skill.loaded 不是 Pi SDK 的原生事件。Harness 按下面条件推导:
flowchart LR
A[Pi read start] --> B[realpath]
B --> C{是否匹配 SkillIndex}
C -->|否| D[只保留普通 tool 事件]
C -->|是| E[skill.requested]
E --> F{同一 toolCallId read 成功}
F -->|是| G[skill.loaded]
F -->|否| H[不发 loaded]
真实路径匹配避免仅凭用户提供的字符串伪造 Skill 活动。事件由 Session 队列串行处理,因此异步 realpath 不会导致 read end 越过 read start。
顺序、重放与去重
- 每个 Session 的 sequence 从 1 单调递增。
- SSE 初次连接可用
afterSequence,重连可用Last-Event-ID。 - Server 先重放 cursor 之后的事件,再订阅新事件,并发送 heartbeat。
- Web 忽略 sequence 小于等于当前值的重复事件。
- sequence 出现缺口时,Web 不应用该事件,而是重置投影并从 0 重连。
- timeline 最多保留 500 条客户端状态,界面显示最近 100 条筛选结果。
数据最小化
事件负载经过 redactAndLimit。API Key、authorization、token 等敏感字段被脱敏;工具参数和结果有字节上限。图片事件只含 ID、MIME、字节数和尺寸,不含 base64。错误映射只保留明确 code 和有限长度的安全消息。