# 实时 API 与 Webhook ## 1. 传输选择 | 场景 | 传输 | 原因 | | --- | --- | --- | | Run 文本/工具/状态输出 | SSE | 单向、可重连、HTTP 友好 | | 实时语音 | WebSocket | 双向音频和控制 | | 协同编辑/presence | WebSocket | 双向低延迟 | | 设备会话 | WebSocket | 双向命令、状态和取消 | | 普通通知 | SSE 或分页 GET | 无需双向连接 | | 外部系统通知 | HTTPS Webhook | 异步推送 | WebSocket 认证也只通过 Header:Web 使用 Cookie handshake;Tauri 使用 `Authorization: Bearer`。禁止 query token。 ## 2. Run SSE ```http GET /v1/runs/{run_id}/events Accept: text/event-stream Last-Event-ID: event_01... ``` 事件格式沿用 `http-and-events.md`:`id`、`type`、`run_id`、`turn_id`、`sequence`、`created_at`、`data`。 规则: - 连接先发送当前 `run.snapshot`,再发送新事件; - `Last-Event-ID` 在保留窗口内重放;超出窗口返回 `409 event_cursor_expired`,客户端 GET Run/Message 后重连; - `content.block.delta` 是预览,`content.block.completed` 提供 Block 权威快照,`message.completed` 指向权威 Message; - `heartbeat` 不写业务表; - `run.completed|failed|cancelled` 是终止事件; - 客户端对 event ID 去重,不仅依赖 TCP 恰好一次。 ## 3. WebSocket 通用握手 ```http GET /v1/realtime/voice/{session_id} Upgrade: websocket Sec-WebSocket-Protocol: hunjing.realtime.v1 Authorization: Bearer ... ``` Web 使用 Cookie 替代 Authorization。连接成功后第一帧: ```json { "type": "session.ready", "session_id": "...", "resume_token": "short-lived-opaque", "capabilities": [], "heartbeat_interval_ms": 15000 } ``` `resume_token` 只在后续同认证主体的 Header `Hunjing-Realtime-Resume` 使用,不放 URL;短期、单会话、可撤销。 JSON 控制帧统一包含 `type`、`event_id`、`sequence`、`sent_at`。二进制帧只承载协商后的音频/媒体块,元数据由前置 JSON frame 描述。 ## 4. 实时语音 ```text POST /v1/realtime-voice-sessions WS /v1/realtime/voice/{session_id} POST /v1/realtime-voice-sessions/{session_id}/terminations ``` 主要帧: ```text input_audio.start input_audio.chunk input_audio.commit transcript.delta transcript.completed agent.text.delta output_audio.start output_audio.chunk output_audio.completed response.interrupt session.error session.completed ``` - 音频 codec、采样率、语言和端点检测在 Session 创建时协商。 - `response.interrupt` 取消当前模型和 TTS,不删除已固化用户段。 - 服务端施加最大帧、带宽、静音、会话时长和费用预算。 - 原始音频保留策略在 Session 创建响应明确返回。 ## 5. 协同编辑 ```text POST /v1/collaboration-sessions WS /v1/realtime/collaboration/{session_id} ``` 帧:`presence.joined/left`、`document.operation`、`document.ack`、`cursor.updated`、`review.updated`、`session.error`。每个 operation 带 client operation ID、base version 和资源 ID;服务端按 ACL 校验。不能通过协同通道编辑支付账、审批记录或已发布不可变版本。 ## 6. 设备会话 ```text POST /v1/device-sessions WS /v1/realtime/devices/{session_id} ``` 帧:`capability.state`、`capture.started/chunk/completed`、`permission.changed`、`session.pause/resume/terminate`、`device.error`。每帧校验 DeviceGrant;撤权后连接立即停止相关 capability。Tauri 不暴露通用 Shell frame。 ## 7. 社区通知 ```http GET /v1/notifications/events Accept: text/event-stream ``` 只发送薄事件:notification ID、type、resource ID、created_at;正文通过 Native API 按当前权限读取。屏蔽或权限变化后旧通知不能成为资源读取旁路。 ## 8. Hunjing Outbound Webhook ```text GET /v1/webhook-endpoints POST /v1/webhook-endpoints PATCH /v1/webhook-endpoints/{endpoint_id} DELETE /v1/webhook-endpoints/{endpoint_id} POST /v1/webhook-endpoints/{endpoint_id}/secret-rotations ``` 投递 Header: ```http Hunjing-Webhook-ID: webhook_event_01... Hunjing-Webhook-Timestamp: 178... Hunjing-Webhook-Signature: v1= ``` 签名输入是:`id + "." + timestamp + "." + raw_body`。接收方验证原始字节、时间窗和 event ID 去重。Secret 明文只显示一次,支持新旧 Secret 短期重叠轮换。 Envelope: ```json { "id": "webhook_event_01...", "type": "run.completed", "created_at": "2026-09-03T08:00:00Z", "data": { "resource_id": "run_01...", "personal_domain_id": "domain_01..." } } ``` Webhook 是薄通知,不包含消息正文、工具参数、支付敏感数据或诊断内容。接收方用自己的凭据读取资源。 投递至少一次、不保证顺序;2xx 确认,3xx 不跟随,失败指数退避并有死信/管理重放。删除事件可能无法再次读取资源,Envelope 保留最小墓碑信息。 ## 9. Provider Inbound Webhook ```text POST /webhooks/v1/payments/{provider} POST /webhooks/v1/media/{provider} POST /webhooks/v1/identity/{provider} ``` - 先读取 raw body,再按 Provider 官方 SDK/算法验签; - 验签前不解析为业务命令; - 按 Provider event ID 幂等,保存接收、验证、处理状态和不可逆 body hash; - 不依赖事件顺序,必要时向 Provider 主动查询当前状态; - 快速返回 Provider 要求的 2xx,重处理进入内部 Job; - 业务结果由 Ledger/权威资源决定,不由单个 Webhook body 直接覆盖。