mateclaw/mateclaw-server/src/main
倪程伟 23c1d49241 feat(webchat): stop an in-flight session stream (POST /sessions/stop)
Until now webchat had no way to actually interrupt a running stream —
ChatController's /api/v1/chat/{id}/stop was technically permitAll'd but
silently no-op'd on webchat streams because WebChatController.chatStream
dropped the subscribe() return value, so ChatStreamTracker.requestStop
had no Disposable to dispose. Visitors could only "stop" client-side by
closing the SSE connection; the server-side LLM call kept running,
burning tokens and firing any side-effecting tools to completion.

Two changes (issue #353):

1. WebChatController.chatStream: keep the Disposable and register it
   with streamTracker.setDisposable, mirroring ChatController#chatStream
   line 495. Now requestStop actually disposes the Flux.

2. New endpoint POST /api/v1/channels/webchat/sessions/stop:
   - Auth mirrors the other session-management endpoints: X-MC-Key +
     X-MC-Visitor-Token + ownsConversation (404 on unknown sessionId,
     so callers can't probe the namespace).
   - Returns {stopped: true|false}; false means no active stream
     (idempotent, not an error).
   - No approval sweep — webchat has no MateClaw username and exposes
     no approval UI today; defer until that surfaces.

WebChatStopStreamTest (@SpringBootTest, H2, V147) — 5 cases:
- stopActiveStream registers a real Flux.never() Disposable on the
  tracker and asserts both stopped=true AND disposable.isDisposed(),
  proving the chatStream wiring change is what makes the endpoint work.
- noActiveStreamReturnsFalse — idempotent path.
- bad token / bad API key → 401.
- unknown sessionId → 404.
2026-06-18 06:33:17 +08:00
..
java/vip/mate feat(webchat): stop an in-flight session stream (POST /sessions/stop) 2026-06-18 06:33:17 +08:00
resources fix(webchat): list sessions whose conversationId hashed (long ids) 2026-06-17 23:21:08 +08:00