OmniStream Docs
  • Panduan Pengguna
  • Developer
  • API Reference
Developer Hub
Pendahuluan
Autentikasi
Model Data
Webhook
WebSocket
    Koneksi WebSocketEvent routing WebSocketPesan klien ke server
Self-Hosting
Error & Rate Limit
WebSocket

Pesan klien ke server

Pesan klien ke server

Mayoritas lalu lintas WebSocket OmniStream mengalir dari server ke klien (event broadcast). Namun klien juga dapat mengirim pesan ke server untuk dua keperluan: mengindikasikan bahwa user sedang mengetik dan menjaga koneksi tetap hidup lewat ping.

Halaman ini mendokumentasikan dua tipe pesan klien yang dikenali oleh ws-server. Pesan di luar tipe ini diabaikan (dengan warning log), sehingga Anda tidak perlu khawatir menambah noise jika sengaja mengirim payload eksperimental.

Kode parser ada di crates/ws-server/src/handler.rs::handle_socket dengan tipe ClientWsMessage.

Format umum

Semua pesan klien adalah JSON dengan field diskriminator type:

Code
{ "type": "<kind>", "...": "..." }

Server memakai tagged enum Serde untuk parsing:

Code
#[derive(Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] pub enum ClientWsMessage { Typing { conversation_id: Uuid }, Ping, }

typing

Memberi tahu sistem bahwa user sedang mengetik pada percakapan tertentu. Server meneruskan event ke channel Redis typing_indicators yang kemudian di-broadcast ke semua klien (lihat Event routing).

Payload

Code
{ "type": "typing", "conversation_id": "b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e" }

Field conversation_id wajib ber-format UUID v4. Kegagalan parse menghasilkan warning di log server dan pesan diabaikan — koneksi tidak ditutup.

Semantik

  • Kirim typing saat user mulai mengetik setelah kosong, atau pada setiap keystroke dengan throttle (rekomendasi: 1 pesan per 2 detik).
  • Tidak ada pesan stop_typing. Implementasi saat ini hanya mengirim flag is_typing: true; frontend penerima menghapus indikator setelah 4-5 detik tanpa update baru (timer-based).
  • Pastikan conversation_id adalah percakapan yang memang sedang Anda buka. Kirim ke ID lain akan memicu typing palsu di sisi supervisor.

Contoh throttle frontend

Code
let lastTypingAt = 0; function onInput() { const now = Date.now(); if (now - lastTypingAt >= 2000) { ws.send(JSON.stringify({ type: "typing", conversation_id: currentConversationId, })); lastTypingAt = now; } }

Apa yang terjadi setelah server menerima?

  1. Handler WebSocket mem-parse pesan menjadi ClientWsMessage::Typing.
  2. Server mengambil agent_id dan agent_name dari AgentClaims yang sudah ter-validate saat handshake.
  3. Server mempublikasikan pesan ke Redis typing_indicators dengan payload lengkap (lihat Event routing).
  4. Subscriber menerima pesan dan mem-broadcast-nya ke semua koneksi WebSocket.
  5. Klien lain (termasuk tab milik agent pengirim) menerima event typing_indicators dan memperbarui UI.

Tidak ada response dari server untuk pesan ini. Jangan menunggu acknowledgment.

ping

Pesan keepalive untuk menjaga koneksi melewati reverse proxy yang menutup koneksi idle. Server menerimanya sebagai no-op.

Payload

Code
{ "type": "ping" }

Tidak ada field lain.

Semantik

  • Kirim tiap 30 detik (atau lebih sering jika proxy Anda memiliki idle timeout <60 detik).
  • Tidak ada response pong. Jika Anda membutuhkan bukti bahwa server masih hidup, andalkan event server yang mengalir secara normal atau WebSocket close event untuk deteksi kematian.
  • Frame WebSocket native ping/pong (opcode 0x9/0xA) juga berfungsi — Axum / Tungstenite menangani otomatis. Pesan JSON {"type":"ping"} adalah alternatif untuk klien yang tidak mudah mengirim control frame.

Contoh interval

Code
const HEARTBEAT_MS = 30_000; const heartbeat = setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: "ping" })); } }, HEARTBEAT_MS); ws.addEventListener("close", () => clearInterval(heartbeat));

Apa yang tidak bisa dilakukan lewat WebSocket

WebSocket hanya untuk push event dan sinyal typing/ping. Tindakan berikut harus lewat REST API:

AksiCara
Kirim pesan ke customerPOST /api/messages
Assign percakapanPOST /api/conversations/:id/transfer
Resolve percakapanPATCH /api/conversations/:id dengan status: "resolved"
Update presence manual (away/busy)PATCH /api/auth/me

Alasannya: API REST punya audit trail, rate limiting, dan middleware RBAC yang konsisten. WebSocket hanya dipakai untuk jalur real-time di mana latensi penting.

Error handling

  • JSON invalid — server menulis warning ke log dan mengabaikan pesan. Koneksi tidak ditutup.
  • type tidak dikenal — sama seperti di atas: log warning, abaikan.
  • conversation_id bukan UUID — sama: log warning, abaikan. Frontend tidak mendapat feedback eksplisit, jadi validasi di klien sebelum mengirim.
  • Koneksi mati (send error) — handler secara otomatis menghapus koneksi dari registry dan, jika ini koneksi terakhir, mempublikasikan presence offline.

Troubleshooting

  • Typing indicator muncul di supervisor tetapi tidak di agent pengirim sendiri — ini normal: typing di-broadcast ke semua tapi frontend biasanya menyembunyikan indikator untuk diri sendiri. Cek kode TypingStore di frontend/src/lib/stores/ jika perlu.
  • Ping tidak mencegah reconnect loop — periksa proxy_read_timeout di nginx. Nilai default 60s biasanya cukup, tetapi beberapa provider cloud mengatur lebih rendah. Anda juga bisa mengurangi interval ping ke 15s.
  • Server mengirim event tetapi klien tidak menerima — pastikan ws.readyState === 1 sebelum mengirim. Koneksi yang belum open akan diam-diam mengabaikan pesan yang dikirim.

File terkait

  • Parser dan handler: crates/ws-server/src/handler.rs
  • Subscriber: crates/ws-server/src/subscriber.rs
  • Publish typing dari handler: crates/ws-server/src/handler.rs (fungsi publish ke Redis)
  • Enum ClientWsMessage: crates/ws-server/src/handler.rs
Last modified on June 8, 2026
Event routing WebSocketPrasyarat Self-Hosting
On this page
  • Format umum
  • typing
    • Payload
    • Semantik
    • Contoh throttle frontend
    • Apa yang terjadi setelah server menerima?
  • ping
    • Payload
    • Semantik
    • Contoh interval
  • Apa yang tidak bisa dilakukan lewat WebSocket
  • Error handling
  • Troubleshooting
  • File terkait
JSON
Rust
JSON
TypeScript
JSON
TypeScript