Repository navigation
SupportOS v1.4.0 — the real-time release
SupportOS v1.4.0 — the real-time release
The three follow-up roadmap items are shipped — plus two serious latent bugs found and fixed in the job pipeline underneath the webhook path. 224/224 tests green (+52).
🪝 Incoming webhook push for conversations
- Register webhooks from the app (Sync Health → Webhook push):
POST /api/webhooks/registercreates the webhook in Help Scout with your locally configured secret; delete just as easily - Conversation changes push in seconds: a
convo.*event enqueues a single-conversation sync, and when it lands every connected client receives a real-timeconversation-updatedSSE event (id, number, subject + an honestreason: webhook) - Restart-safe: persisted-but-unprocessed webhook events are drained automatically on boot
- Demo it end-to-end:
POST /api/demo/simulate-webhookdrives an event through the exact production path — HMAC-signed self-POST → persist → dedup → job → worker tick → mirror update → SSE
🔎 Semantic docs search (local embeddings + optional Qdrant)
- Docs mirror articles are chunked and embedded by a background job; vectors are stored locally in SQLite, so semantic search works without Qdrant — and gets ANN speed when Qdrant is running
- Hybrid retrieval:
GET /api/docs/searchfuses FTS5 keyword and semantic vector result lists with Reciprocal Rank Fusion; every hit records whether keyword search, semantic search, or both found it, and a mode note explains exactly what ran - Honest degradation at every layer (no model → FTS only with setup instructions; Qdrant down → local cosine scan; provider unreachable → FTS only, retried next query)
⏱️ SLA / business-hours reporting per mailbox
- Per-mailbox schedules: IANA timezone, active weekdays, open window — plus first-response and resolution SLA targets — edited in Settings → Business hours
- Reports → SLA & business hours: first-response and resolution in wall AND business minutes (median included), met/missed against targets, and live "currently waiting" aging with at-risk counts
- The business-minutes engine is pure and DST-safe (built on the platform timezone database, unit-tested across a spring-forward transition and a half-hour zone); invalid input returns null instead of a fabricated number
🐛 Two latent job-pipeline bugs fixed (found while wiring the webhook e2e)
- Every background job was permanently unclaimable at runtime:
jobs.run_atwas written in ISO-8601 (...T...Z) while the claim loop compares against SQLitedatetime('now')(... ...) —'T' > ' 'lexicographically, so webhook-triggered syncs, attachment downloads, AI jobs and embedding passes all sat queued forever since v1.0.0. Tests stayed green because they called components directly, skipping the claim loop. - Job payloads reached the worker as unparsed JSON strings, so
payload.remoteIdread asundefinedand sync jobs "completed" without syncing anything. - Both fixes ship with regression tests that enqueue → claim → execute exactly as the worker does.
Full changelog: CHANGELOG.md