Feat/pet tool approval#264
Conversation
- PushTypeToolApproval: new push type for tool approval requests - ActionToolApprovalResponse: client response action for approval - ToolApprovalPush: push data with request_id, tool, arguments - ToolApprovalResponse: response data with request_id, approved
- Add pendingApprovals map to track approval requests in-flight - Add approvalMu mutex for concurrent access - Implement ApproveTool(): send push to client, wait for response - Implement ResolveApproval(): resolve pending approval with user decision - Timeout 60 seconds, auto-deny on timeout - Push only to the session that initiated the tool call via sessionPush
- Add section 3.9 tool_approval with push format, field reference - Add client response format and frontend handling example - Update push_type quick reference table with tool_approval - Update version to v2.11
|
|
||
| petHook := pet.NewPetHook(svc.CharManager(), svc.ActionManager(), svc, svc.MemoryStore(), svc.ConversationStore(), svc.UserProfileManager()) | ||
| svc.SetApprovalResolver(petHook.ResolveApproval) | ||
| agentLoop.MountHook(agent.NamedHook("pet", petHook)) |
There was a problem hiding this comment.
High: this hook is mounted globally (对所有 channel 生效), but PetHook.ApproveTool now waits for a pet-channel WebSocket approval keyed by req.ChatID. On non-pet channels, req.ChatID is a Telegram/Discord/etc chat id, so sessionPush will never reach a pet client and every tool call will block for 60s and then auto-deny. The approval path needs to be scoped to pet-originated turns, or it will break tool execution outside the pet channel.
| if h.petService.sessionPush != nil { | ||
| h.petService.sessionPush(req.ChatID, pushMsg) | ||
| } else if h.petService.pushHandler != nil { | ||
| h.petService.pushHandler(pushMsg) | ||
| } | ||
|
|
||
| ch := make(chan bool, 1) | ||
| h.approvalMu.Lock() | ||
| h.pendingApprovals[requestID] = ch |
There was a problem hiding this comment.
High: the approval request is sent before pendingApprovals[requestID] is registered. If the client responds quickly, handleToolApprovalResponse can call ResolveApproval before the map entry exists, which drops the decision and leaves this call waiting until the 60s timeout. Register the channel first, then publish the push.
| if err := json.Unmarshal(req.Data, &data); err != nil { | ||
| return s.sendError(sessionID, req.Action, "invalid tool approval response data") | ||
| } | ||
| if s.resolveApproval != nil { |
There was a problem hiding this comment.
Medium: this resolves any request_id without checking that the response came from the same session/connection that received the approval prompt. A different connected client that learns the request_id can approve or deny someone else's tool call, and it also contradicts the docs that say the reply must come back on the same WebSocket connection.
PR: Pet 通道工具审批机制
背景
当前 pet 通道的 LLM 可以调用工具箱中的高风险工具(如
exec、write_file、subagent等),虽然有 deny 黑名单和 workspace 限制,但没有用户显式确认机制。用户无法在工具执行前进行审批,存在安全隐患。目标
实现
ToolApproverhook,让 pet 通道的客户端可以审批/拒绝高风险工具的执行。实现方案
1. 新增类型和常量 (
pkg/pet/types.go)PushTypeToolApproval = "tool_approval"— 推送类型ActionToolApprovalResponse = "tool_approval_response"— 客户端响应 ActionToolApprovalPush— 推送给客户端的审批请求ToolApprovalResponse— 客户端返回的审批结果2. PetHook 实现 ToolApprover (
pkg/pet/hooks.go)pendingApprovals映射,存储等待审批的请求ApproveTool():推送审批请求给客户端 → 等待用户响应(60s 超时)→ 返回审批结果ResolveApproval():由 PetService 在收到客户端响应时调用,结束等待3. PetService 新增审批支持 (
pkg/pet/service.go)sessionPush回调:定向推送给指定 session 的客户端resolveApproval回调:将客户端审批结果转发给 PetHookhandleToolApprovalResponse():处理tool_approval_response请求4. PetChannel 新增定向推送 (
pkg/channels/pet/channel.go)sendPushToSession():只推送给匹配sessionID的 WebSocket 连接5. 审批推送流程
6. 客户端交互格式
服务端推送:
{ "type": "push", "push_type": "tool_approval", "data": { "request_id": "tool_approval_1714896000123456789", "tool": "exec", "arguments": { "command": "ipconfig" } }, "timestamp": 1714896000 }客户端回复(同一条 WebSocket 连接):
{ "action": "tool_approval_response", "data": { "request_id": "tool_approval_1714896000123456789", "approved": true } }7. 后续待改进
tool_approval.require_approval配置项)文件变更
修改文件
pkg/pet/types.gopkg/pet/hooks.gopkg/pet/service.gopkg/channels/pet/channel.gopkg/gateway/gateway.godocs/PET_CHANNEL_API.md提交记录