mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 11:13:43 +08:00
* feat(webchat): add approval resolve + replay for API-Key channel (ISSUE #413 P1) Before this, a WebChat (API-Key) channel that hit a ToolGuard-protected tool parked the turn in a pending approval the visitor could never clear — it hung for 30 min until the GC timeout and the turn was wasted. This PR closes the loop, mirroring the web ChatController. A1 — no code change. tool_approval_requested already reaches the SDK via ToolExecutionGuardHelper's streamTracker.broadcastObject (direct SSE push, bypassing the StreamDelta path). Adding it to forwardVisitorEvent would double-deliver; the default-drop is correct. A2 — new /sessions/approve and /sessions/deny REST endpoints. Auth is the existing visitorToken + conversationId ownership guard; the actor is webchatUsername(visitorId), which resolves the 'no MateClaw username' blocker noted in the old stopSession javadoc. Both broadcast tool_approval_resolved so the SDK clears its banner in real time. A3 — approve returns an SSE stream: resolveAndConsume (atomic DB + metadata + memory), restoreChatOrigin (recovers the webchat origin captured at createPending), then chatWithReplayStream replays the tool call and continues the turn. Replay may re-trigger approvals, which the existing tool_approval_requested direct push handles. A4 — stopSession now sweeps pending approvals (denyAllByConversation) and broadcasts each resolution, so stopping a stream no longer leaves approvals lingering for the GC. Tests: WebChatApprovalInteractionTest (7) — deny resolves + broadcasts, deny auth/ownership guards, idempotent unknown-pending, stop sweep clears pending, stop no-op when nothing pending. Regression: WebChatStopStreamTest (5), WebChatArchivePinTest (6), WebChatSchemaFieldsTest (5), WebChatWikiPageListTest (8), ApprovalWorkflowServiceResolveTest (13), GcTest (7), RecoveryTest (7). * fix(webchat): IDOR guard + SSE hang fix (PR #415 review) Addresses all review feedback from mateaix: P0 IDOR (security): /sessions/approve and /sessions/deny accepted a client-supplied pendingId without cross-checking it belonged to the caller's conversation. A visitor could resolve / replay another visitor's guarded tool call. Fix: getPending(pendingId) then assert conversationId matches before resolving. Added getPending delegate on ApprovalWorkflowService so the webchat controller (which holds the workflow facade) can do the precise lookup. SSE hang: approveSession's already-resolved / error branches broadcast 'done' before streamTracker.register/attach, so the event had no subscriber and the SSE hung to the 10-min timeout. Fix: register+attach first, then resolveAndConsume. Removed the now-duplicate register/attach in the replay branch. Tests: +2 IDOR cases (cross-visitor pendingId rejected 404; mismatched pendingId rejected 404). denyResolvesPending now asserts via getPending (findPendingByConversation returns the earliest pending, polluted by cross-test map state). denyUnknownPendingIsSafe updated to expect 404 (no longer leaks pendingId existence). Isolated IDOR victim/attacker visitor IDs to avoid cross-test conversationId collisions. Regression: WebChatStopStreamTest (5), WebChatArchivePinTest (6), ApprovalWorkflowServiceResolveTest (13), GcTest (7), RecoveryTest (7). * style(webchat): use simple ChatOrigin name in approveSession (PR #415 review) Reviewer flagged fully-qualified inline types (ResolveOutcome was fixed in the prior commit; ChatOrigin was missed). Add the import and switch the 3 FQN references in approveSession to the simple name, matching the ResolveOutcome cleanup. chatStream's pre-existing FQN usages are out of this PR's scope and left untouched. |
||
|---|---|---|
| .. | ||
| java/vip/mate | ||
| resources | ||